From 2d438a6a53d5c04548f322257f3e98a157990233 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Mon, 1 Feb 2016 17:51:01 +0900 Subject: [PATCH 01/91] Bug 1224790 followup - Make the test actually run. r=me --- dom/tests/mochitest/chrome/chrome.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dom/tests/mochitest/chrome/chrome.ini b/dom/tests/mochitest/chrome/chrome.ini index df87f8e8040..9e7ab88d02f 100644 --- a/dom/tests/mochitest/chrome/chrome.ini +++ b/dom/tests/mochitest/chrome/chrome.ini @@ -37,9 +37,11 @@ support-files = [test_bug830396.xul] [test_bug830858.xul] [test_bug1224790-1.xul] -skip-if = os != 'cocoa' +# synthesizeNativeOSXClick does not work on 10.6 +skip-if = os != 'mac' || os_version == '10.6' [test_bug1224790-2.xul] -skip-if = os != 'cocoa' +# synthesizeNativeOSXClick does not work on 10.6 +skip-if = os != 'mac' || os_version == '10.6' [test_callback_wrapping.xul] [test_clonewrapper.xul] [test_cyclecollector.xul] From f4d970568ac3711f5a900e090946a3cd31deefb4 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Fri, 29 Jan 2016 17:28:04 +0000 Subject: [PATCH 02/91] Bug 1237681: remove REGISTRY and VERSION for in-tree-generated images; r=garndt --- testing/docker/desktop-build/REGISTRY | 1 - testing/docker/desktop-build/VERSION | 1 - testing/docker/desktop-test/REGISTRY | 1 - testing/docker/desktop-test/VERSION | 1 - 4 files changed, 4 deletions(-) delete mode 100644 testing/docker/desktop-build/REGISTRY delete mode 100644 testing/docker/desktop-build/VERSION delete mode 100644 testing/docker/desktop-test/REGISTRY delete mode 100644 testing/docker/desktop-test/VERSION diff --git a/testing/docker/desktop-build/REGISTRY b/testing/docker/desktop-build/REGISTRY deleted file mode 100644 index cb1e1bb482a..00000000000 --- a/testing/docker/desktop-build/REGISTRY +++ /dev/null @@ -1 +0,0 @@ -taskcluster diff --git a/testing/docker/desktop-build/VERSION b/testing/docker/desktop-build/VERSION deleted file mode 100644 index 0e24a92ffa1..00000000000 --- a/testing/docker/desktop-build/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1.12 diff --git a/testing/docker/desktop-test/REGISTRY b/testing/docker/desktop-test/REGISTRY deleted file mode 100644 index cb1e1bb482a..00000000000 --- a/testing/docker/desktop-test/REGISTRY +++ /dev/null @@ -1 +0,0 @@ -taskcluster diff --git a/testing/docker/desktop-test/VERSION b/testing/docker/desktop-test/VERSION deleted file mode 100644 index d3532a107ee..00000000000 --- a/testing/docker/desktop-test/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.5.7 From fc848ae250d509a9cd3838b4f569255b5c985304 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 28 Jan 2016 19:59:31 +0000 Subject: [PATCH 03/91] Bug 1231318: pass MOZ_BUILD_DATE to builds based on pushdate; r=jlund --- testing/mozharness/mozharness/mozilla/building/buildbase.py | 5 +++++ testing/taskcluster/tasks/build.yml | 1 + 2 files changed, 6 insertions(+) diff --git a/testing/mozharness/mozharness/mozilla/building/buildbase.py b/testing/mozharness/mozharness/mozilla/building/buildbase.py index d371059f494..afbfddfadf3 100755 --- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -772,6 +772,11 @@ or run without that action (ie: --no-{action})" buildid = self.buildbot_config['properties']['buildid'].encode( 'ascii', 'replace' ) + else: + # for taskcluster, there are no buildbot properties, and we pass + # MOZ_BUILD_DATE into mozharness as an environment variable, only + # to have it pass the same value out with the same name. + buildid = os.environ.get('MOZ_BUILD_DATE') if not buildid: self.info("Creating buildid through current time") diff --git a/testing/taskcluster/tasks/build.yml b/testing/taskcluster/tasks/build.yml index 93067db24d7..afea4dce501 100644 --- a/testing/taskcluster/tasks/build.yml +++ b/testing/taskcluster/tasks/build.yml @@ -48,6 +48,7 @@ task: GECKO_HEAD_REF: '{{head_ref}}' TOOLTOOL_REPO: 'https://git.mozilla.org/build/tooltool.git' TOOLTOOL_REV: 'master' + MOZ_BUILD_DATE: '{{pushdate}}' extra: build_product: '{{build_product}}' From f975eb21d436353868c2802bc9bb42dd07f90ae1 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 3 Nov 2015 13:29:19 -0500 Subject: [PATCH 04/91] Bug 1222475 - use UniquePtr instead of nsAutoArrayPtr in dom/; r=baku,bz,terrence --- dom/archivereader/ArchiveZipEvent.cpp | 10 +++-- dom/camera/GonkCameraControl.cpp | 7 ++- dom/encoding/TextDecoder.cpp | 7 +-- dom/encoding/TextEncoder.cpp | 7 +-- dom/html/HTMLFrameSetElement.cpp | 25 ++++++----- dom/html/HTMLFrameSetElement.h | 9 ++-- dom/ipc/TabParent.cpp | 11 ++--- dom/mobilemessage/MobileMessageManager.cpp | 7 +-- dom/mobilemessage/ipc/SmsParent.cpp | 7 +-- dom/svg/SVGFEConvolveMatrixElement.cpp | 4 +- dom/telephony/ipc/TelephonyChild.cpp | 6 ++- dom/xslt/xpath/txXPCOMExtensionFunction.cpp | 44 ++++++++++++++----- dom/xslt/xslt/txMozillaStylesheetCompiler.cpp | 7 +-- dom/xslt/xslt/txStylesheetCompiler.cpp | 7 +-- 14 files changed, 98 insertions(+), 60 deletions(-) diff --git a/dom/archivereader/ArchiveZipEvent.cpp b/dom/archivereader/ArchiveZipEvent.cpp index 31bd85cd146..56251eef66e 100644 --- a/dom/archivereader/ArchiveZipEvent.cpp +++ b/dom/archivereader/ArchiveZipEvent.cpp @@ -10,6 +10,9 @@ #include "nsContentUtils.h" #include "nsCExternalHandlerService.h" +#include "mozilla/UniquePtr.h" + +using namespace mozilla; using namespace mozilla::dom; USING_ARCHIVEREADER_NAMESPACE @@ -191,8 +194,8 @@ ArchiveReaderZipEvent::Exec() } // Read the name: - nsAutoArrayPtr filename(new char[filenameLen + 1]); - rv = inputStream->Read(filename, filenameLen, &ret); + auto filename = MakeUnique(filenameLen + 1); + rv = inputStream->Read(filename.get(), filenameLen, &ret); if (NS_FAILED(rv) || ret != filenameLen) { return RunShare(NS_ERROR_UNEXPECTED); } @@ -201,7 +204,8 @@ ArchiveReaderZipEvent::Exec() // We ignore the directories: if (filename[filenameLen - 1] != '/') { - mFileList.AppendElement(new ArchiveZipItem(filename, centralStruct, mEncoding)); + mFileList.AppendElement(new ArchiveZipItem(filename.get(), centralStruct, + mEncoding)); } // Ignore the rest diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 425d46104f4..9d06e9e00b9 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -2273,8 +2273,7 @@ nsGonkCameraControl::CreatePoster(Image* aImage, uint32_t aWidth, uint32_t aHeig // ARGB is 32 bits / pixel size_t tmpLength = mWidth * mHeight * sizeof(uint32_t); - nsAutoArrayPtr tmp; - tmp = new uint8_t[tmpLength]; + UniquePtr tmp = MakeUnique(tmpLength); GrallocImage* nativeImage = static_cast(mImage.get()); android::sp graphicBuffer = nativeImage->GetGraphicBuffer(); @@ -2284,7 +2283,7 @@ nsGonkCameraControl::CreatePoster(Image* aImage, uint32_t aWidth, uint32_t aHeig uint32_t stride = mWidth * 4; int err = libyuv::ConvertToARGB(static_cast(graphicSrc), - srcLength, tmp, stride, 0, 0, + srcLength, tmp.get(), stride, 0, 0, mWidth, mHeight, mWidth, mHeight, libyuv::kRotate0, libyuv::FOURCC_NV21); @@ -2307,7 +2306,7 @@ nsGonkCameraControl::CreatePoster(Image* aImage, uint32_t aWidth, uint32_t aHeig } nsString opt; - nsresult rv = encoder->InitFromData(tmp, tmpLength, mWidth, + nsresult rv = encoder->InitFromData(tmp.get(), tmpLength, mWidth, mHeight, stride, imgIEncoder::INPUT_FORMAT_HOSTARGB, opt); diff --git a/dom/encoding/TextDecoder.cpp b/dom/encoding/TextDecoder.cpp index b1d6f0c6051..24f7ec166e3 100644 --- a/dom/encoding/TextDecoder.cpp +++ b/dom/encoding/TextDecoder.cpp @@ -7,6 +7,7 @@ #include "mozilla/dom/TextDecoder.h" #include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/UnionTypes.h" +#include "mozilla/UniquePtrExtensions.h" #include "nsContentUtils.h" #include @@ -65,18 +66,18 @@ TextDecoder::Decode(const char* aInput, const int32_t aLength, } // Need a fallible allocator because the caller may be a content // and the content can specify the length of the string. - nsAutoArrayPtr buf(new (fallible) char16_t[outLen + 1]); + auto buf = MakeUniqueFallible(outLen + 1); if (!buf) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } int32_t length = aLength; - rv = mDecoder->Convert(aInput, &length, buf, &outLen); + rv = mDecoder->Convert(aInput, &length, buf.get(), &outLen); MOZ_ASSERT(mFatal || rv != NS_ERROR_ILLEGAL_INPUT); buf[outLen] = 0; - if (!aOutDecodedString.Append(buf, outLen, fallible)) { + if (!aOutDecodedString.Append(buf.get(), outLen, fallible)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } diff --git a/dom/encoding/TextEncoder.cpp b/dom/encoding/TextEncoder.cpp index f9dcacc134c..28c18e81610 100644 --- a/dom/encoding/TextEncoder.cpp +++ b/dom/encoding/TextEncoder.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/TextEncoder.h" #include "mozilla/dom/EncodingUtils.h" +#include "mozilla/UniquePtrExtensions.h" #include "nsContentUtils.h" namespace mozilla { @@ -54,18 +55,18 @@ TextEncoder::Encode(JSContext* aCx, } // Need a fallible allocator because the caller may be a content // and the content can specify the length of the string. - nsAutoArrayPtr buf(new (fallible) char[maxLen + 1]); + auto buf = MakeUniqueFallible(maxLen + 1); if (!buf) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } int32_t dstLen = maxLen; - rv = mEncoder->Convert(data, &srcLen, buf, &dstLen); + rv = mEncoder->Convert(data, &srcLen, buf.get(), &dstLen); // Now reset the encoding algorithm state to the default values for encoding. int32_t finishLen = maxLen - dstLen; - rv = mEncoder->Finish(buf + dstLen, &finishLen); + rv = mEncoder->Finish(&buf[dstLen], &finishLen); if (NS_SUCCEEDED(rv)) { dstLen += finishLen; } diff --git a/dom/html/HTMLFrameSetElement.cpp b/dom/html/HTMLFrameSetElement.cpp index 454ba0308bd..49c7fbb8d55 100644 --- a/dom/html/HTMLFrameSetElement.cpp +++ b/dom/html/HTMLFrameSetElement.cpp @@ -8,6 +8,7 @@ #include "mozilla/dom/HTMLFrameSetElementBinding.h" #include "mozilla/dom/EventHandlerBinding.h" #include "nsGlobalWindow.h" +#include "mozilla/UniquePtrExtensions.h" NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet) @@ -81,7 +82,7 @@ HTMLFrameSetElement::SetAttr(int32_t aNameSpaceID, */ if (aAttribute == nsGkAtoms::rows && aNameSpaceID == kNameSpaceID_None) { int32_t oldRows = mNumRows; - ParseRowCol(aValue, mNumRows, getter_Transfers(mRowSpecs)); + ParseRowCol(aValue, mNumRows, &mRowSpecs); if (mNumRows != oldRows) { mCurrentRowColHint = NS_STYLE_HINT_FRAMECHANGE; @@ -89,7 +90,7 @@ HTMLFrameSetElement::SetAttr(int32_t aNameSpaceID, } else if (aAttribute == nsGkAtoms::cols && aNameSpaceID == kNameSpaceID_None) { int32_t oldCols = mNumCols; - ParseRowCol(aValue, mNumCols, getter_Transfers(mColSpecs)); + ParseRowCol(aValue, mNumCols, &mColSpecs); if (mNumCols != oldCols) { mCurrentRowColHint = NS_STYLE_HINT_FRAMECHANGE; @@ -116,19 +117,19 @@ HTMLFrameSetElement::GetRowSpec(int32_t *aNumValues, const nsAttrValue* value = GetParsedAttr(nsGkAtoms::rows); if (value && value->Type() == nsAttrValue::eString) { nsresult rv = ParseRowCol(value->GetStringValue(), mNumRows, - getter_Transfers(mRowSpecs)); + &mRowSpecs); NS_ENSURE_SUCCESS(rv, rv); } if (!mRowSpecs) { // we may not have had an attr or had an empty attr - mRowSpecs = new nsFramesetSpec[1]; + mRowSpecs = MakeUnique(1); mNumRows = 1; mRowSpecs[0].mUnit = eFramesetUnit_Relative; mRowSpecs[0].mValue = 1; } } - *aSpecs = mRowSpecs; + *aSpecs = mRowSpecs.get(); *aNumValues = mNumRows; return NS_OK; } @@ -146,19 +147,19 @@ HTMLFrameSetElement::GetColSpec(int32_t *aNumValues, const nsAttrValue* value = GetParsedAttr(nsGkAtoms::cols); if (value && value->Type() == nsAttrValue::eString) { nsresult rv = ParseRowCol(value->GetStringValue(), mNumCols, - getter_Transfers(mColSpecs)); + &mColSpecs); NS_ENSURE_SUCCESS(rv, rv); } if (!mColSpecs) { // we may not have had an attr or had an empty attr - mColSpecs = new nsFramesetSpec[1]; + mColSpecs = MakeUnique(1); mNumCols = 1; mColSpecs[0].mUnit = eFramesetUnit_Relative; mColSpecs[0].mValue = 1; } } - *aSpecs = mColSpecs; + *aSpecs = mColSpecs.get(); *aNumValues = mNumCols; return NS_OK; } @@ -205,7 +206,7 @@ HTMLFrameSetElement::GetAttributeChangeHint(const nsIAtom* aAttribute, nsresult HTMLFrameSetElement::ParseRowCol(const nsAString & aValue, int32_t& aNumSpecs, - nsFramesetSpec** aSpecs) + UniquePtr* aSpecs) { if (aValue.IsEmpty()) { aNumSpecs = 0; @@ -233,7 +234,7 @@ HTMLFrameSetElement::ParseRowCol(const nsAString & aValue, commaX = spec.FindChar(sComma, commaX + 1); } - nsFramesetSpec* specs = new (fallible) nsFramesetSpec[count]; + auto specs = MakeUniqueFallible(count); if (!specs) { *aSpecs = nullptr; aNumSpecs = 0; @@ -327,8 +328,8 @@ HTMLFrameSetElement::ParseRowCol(const nsAString & aValue, aNumSpecs = count; // Transfer ownership to caller here - *aSpecs = specs; - + *aSpecs = Move(specs); + return NS_OK; } diff --git a/dom/html/HTMLFrameSetElement.h b/dom/html/HTMLFrameSetElement.h index 5470e668f76..b6bbe5d957d 100644 --- a/dom/html/HTMLFrameSetElement.h +++ b/dom/html/HTMLFrameSetElement.h @@ -8,6 +8,7 @@ #define HTMLFrameSetElement_h #include "mozilla/Attributes.h" +#include "mozilla/UniquePtr.h" #include "nsIDOMHTMLFrameSetElement.h" #include "nsGenericHTMLElement.h" @@ -144,8 +145,8 @@ protected: private: nsresult ParseRowCol(const nsAString& aValue, - int32_t& aNumSpecs, - nsFramesetSpec** aSpecs); + int32_t& aNumSpecs, + UniquePtr* aSpecs); /** * The number of size specs in our "rows" attr @@ -163,11 +164,11 @@ private: /** * The parsed representation of the "rows" attribute */ - nsAutoArrayPtr mRowSpecs; // parsed, non-computed dimensions + UniquePtr mRowSpecs; // parsed, non-computed dimensions /** * The parsed representation of the "cols" attribute */ - nsAutoArrayPtr mColSpecs; // parsed, non-computed dimensions + UniquePtr mColSpecs; // parsed, non-computed dimensions }; } // namespace dom diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index f9781c3cd2c..95de646b84c 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -38,6 +38,7 @@ #include "mozilla/Preferences.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" +#include "mozilla/UniquePtr.h" #include "mozilla/unused.h" #include "BlobParent.h" #include "nsCOMPtr.h" @@ -2125,25 +2126,25 @@ TabParent::RecvEnableDisableCommands(const nsString& aAction, { nsCOMPtr remoteBrowser = do_QueryInterface(mFrameElement); if (remoteBrowser) { - nsAutoArrayPtr enabledCommands, disabledCommands; + UniquePtr enabledCommands, disabledCommands; if (aEnabledCommands.Length()) { - enabledCommands = new const char* [aEnabledCommands.Length()]; + enabledCommands = MakeUnique(aEnabledCommands.Length()); for (uint32_t c = 0; c < aEnabledCommands.Length(); c++) { enabledCommands[c] = aEnabledCommands[c].get(); } } if (aDisabledCommands.Length()) { - disabledCommands = new const char* [aDisabledCommands.Length()]; + disabledCommands = MakeUnique(aDisabledCommands.Length()); for (uint32_t c = 0; c < aDisabledCommands.Length(); c++) { disabledCommands[c] = aDisabledCommands[c].get(); } } remoteBrowser->EnableDisableCommands(aAction, - aEnabledCommands.Length(), enabledCommands, - aDisabledCommands.Length(), disabledCommands); + aEnabledCommands.Length(), enabledCommands.get(), + aDisabledCommands.Length(), disabledCommands.get()); } return true; diff --git a/dom/mobilemessage/MobileMessageManager.cpp b/dom/mobilemessage/MobileMessageManager.cpp index 0c6c97e0ca0..36b9644a230 100644 --- a/dom/mobilemessage/MobileMessageManager.cpp +++ b/dom/mobilemessage/MobileMessageManager.cpp @@ -24,6 +24,7 @@ #include "mozilla/dom/ToJSValue.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/UniquePtr.h" #include "nsIMmsService.h" #include "nsIMobileMessageCallback.h" #include "nsIMobileMessageDatabaseService.h" @@ -424,7 +425,7 @@ MobileMessageManager::GetMessages(const MobileMessageFilter& aFilter, endDate = aFilter.mEndDate.Value(); } - nsAutoArrayPtr ptrNumbers; + UniquePtr ptrNumbers; uint32_t numbersCount = 0; if (!aFilter.mNumbers.IsNull() && aFilter.mNumbers.Value().Length()) { @@ -432,7 +433,7 @@ MobileMessageManager::GetMessages(const MobileMessageFilter& aFilter, uint32_t index; numbersCount = numbers.Length(); - ptrNumbers = new const char16_t* [numbersCount]; + ptrNumbers = MakeUnique(numbersCount); for (index = 0; index < numbersCount; index++) { ptrNumbers[index] = numbers[index].get(); } @@ -464,7 +465,7 @@ MobileMessageManager::GetMessages(const MobileMessageFilter& aFilter, nsCOMPtr continueCallback; nsresult rv = dbService->CreateMessageCursor(hasStartDate, startDate, hasEndDate, endDate, - ptrNumbers, numbersCount, + ptrNumbers.get(), numbersCount, delivery, hasRead, read, hasThreadId, threadId, diff --git a/dom/mobilemessage/ipc/SmsParent.cpp b/dom/mobilemessage/ipc/SmsParent.cpp index f3512c77dd6..da5bc4a524a 100644 --- a/dom/mobilemessage/ipc/SmsParent.cpp +++ b/dom/mobilemessage/ipc/SmsParent.cpp @@ -22,6 +22,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/mobilemessage/Constants.h" // For MessageType +#include "mozilla/UniquePtr.h" #include "nsContentUtils.h" #include "nsTArrayHelpers.h" #include "xpcpublic.h" @@ -830,12 +831,12 @@ MobileMessageCursorParent::DoRequest(const CreateMessageCursorRequest& aRequest) const SmsFilterData& filter = aRequest.filter(); const nsTArray& numbers = filter.numbers(); - nsAutoArrayPtr ptrNumbers; + UniquePtr ptrNumbers; uint32_t numbersCount = numbers.Length(); if (numbersCount) { uint32_t index; - ptrNumbers = new const char16_t* [numbersCount]; + ptrNumbers = MakeUnique(numbersCount); for (index = 0; index < numbersCount; index++) { ptrNumbers[index] = numbers[index].get(); } @@ -845,7 +846,7 @@ MobileMessageCursorParent::DoRequest(const CreateMessageCursorRequest& aRequest) filter.startDate(), filter.hasEndDate(), filter.endDate(), - ptrNumbers, numbersCount, + ptrNumbers.get(), numbersCount, filter.delivery(), filter.hasRead(), filter.read(), diff --git a/dom/svg/SVGFEConvolveMatrixElement.cpp b/dom/svg/SVGFEConvolveMatrixElement.cpp index 45faf68c645..2b0b0b62d03 100644 --- a/dom/svg/SVGFEConvolveMatrixElement.cpp +++ b/dom/svg/SVGFEConvolveMatrixElement.cpp @@ -6,6 +6,8 @@ #include "mozilla/dom/SVGFEConvolveMatrixElement.h" #include "mozilla/dom/SVGFEConvolveMatrixElementBinding.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/UniquePtrExtensions.h" #include "DOMSVGAnimatedNumberList.h" #include "nsSVGUtils.h" #include "nsSVGFilterInstance.h" @@ -203,7 +205,7 @@ SVGFEConvolveMatrixElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstan if (orderX > NS_SVG_OFFSCREEN_MAX_DIMENSION || orderY > NS_SVG_OFFSCREEN_MAX_DIMENSION) return failureDescription; - nsAutoArrayPtr kernel(new (fallible) float[orderX * orderY]); + UniquePtr kernel = MakeUniqueFallible(orderX * orderY); if (!kernel) return failureDescription; for (uint32_t i = 0; i < kmLength; i++) { diff --git a/dom/telephony/ipc/TelephonyChild.cpp b/dom/telephony/ipc/TelephonyChild.cpp index 593387f6e57..2355e036c12 100644 --- a/dom/telephony/ipc/TelephonyChild.cpp +++ b/dom/telephony/ipc/TelephonyChild.cpp @@ -7,6 +7,7 @@ #include "TelephonyChild.h" #include "mozilla/dom/telephony/TelephonyDialCallback.h" +#include "mozilla/UniquePtr.h" #include "TelephonyIPCService.h" USING_TELEPHONY_NAMESPACE @@ -212,12 +213,13 @@ TelephonyRequestChild::DoResponse(const DialResponseMMISuccess& aResponse) uint32_t count = info.get_ArrayOfnsString().Length(); const nsTArray& additionalInformation = info.get_ArrayOfnsString(); - nsAutoArrayPtr additionalInfoPtrs(new const char16_t*[count]); + auto additionalInfoPtrs = MakeUnique(count); for (size_t i = 0; i < count; ++i) { additionalInfoPtrs[i] = additionalInformation[i].get(); } - callback->NotifyDialMMISuccessWithStrings(statusMessage, count, additionalInfoPtrs); + callback->NotifyDialMMISuccessWithStrings(statusMessage, count, + additionalInfoPtrs.get()); break; } case AdditionalInformation::TArrayOfnsMobileCallForwardingOptions: { diff --git a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp index ba369752272..f70c096b44e 100644 --- a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp +++ b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp @@ -17,8 +17,11 @@ #include "xptcall.h" #include "txXPathObjectAdaptor.h" #include "mozilla/Attributes.h" +#include "mozilla/UniquePtr.h" +#include "nsContentUtils.h" #include "nsIClassInfo.h" #include "nsIInterfaceInfo.h" +#include "js/RootingAPI.h" NS_IMPL_ISUPPORTS(txXPathObjectAdaptor, txIXPathObject) @@ -295,23 +298,38 @@ txXPCOMExtensionFunctionCall::GetParamType(const nsXPTParamInfo &aParam, } } -class txParamArrayHolder +class txParamArrayHolder : public JS::Traceable { public: txParamArrayHolder() : mCount(0) { } + txParamArrayHolder(txParamArrayHolder&& rhs) + : mArray(mozilla::Move(rhs.mArray)) + , mCount(rhs.mCount) + { + rhs.mCount = 0; + } ~txParamArrayHolder(); bool Init(uint8_t aCount); operator nsXPTCVariant*() const { - return mArray; + return mArray.get(); + } + + static void trace(txParamArrayHolder* holder, JSTracer* trc) { holder->trace(trc); } + void trace(JSTracer* trc) { + for (uint8_t i = 0; i < mCount; ++i) { + if (mArray[i].type == nsXPTType::T_JSVAL) { + JS::UnsafeTraceRoot(trc, &mArray[i].val.j, "txParam value"); + } + } } private: - nsAutoArrayPtr mArray; + mozilla::UniquePtr mArray; uint8_t mCount; }; @@ -338,8 +356,12 @@ bool txParamArrayHolder::Init(uint8_t aCount) { mCount = aCount; - mArray = new nsXPTCVariant[mCount]; - memset(mArray, 0, mCount * sizeof(nsXPTCVariant)); + mArray = mozilla::MakeUnique(mCount); + if (!mArray) { + return false; + } + + memset(mArray.get(), 0, mCount * sizeof(nsXPTCVariant)); return true; } @@ -363,8 +385,8 @@ txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext, uint8_t paramCount = methodInfo->GetParamCount(); uint8_t inArgs = paramCount - 1; - txParamArrayHolder invokeParams; - if (!invokeParams.Init(paramCount)) { + JS::Rooted invokeParams(nsContentUtils::RootingCxForThread()); + if (!invokeParams.get().Init(paramCount)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -385,7 +407,7 @@ txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext, // Create context wrapper. context = new txFunctionEvaluationContext(aContext, mState); - nsXPTCVariant &invokeParam = invokeParams[0]; + nsXPTCVariant &invokeParam = invokeParams.get()[0]; invokeParam.type = paramInfo.GetType(); invokeParam.SetValNeedsCleanup(); NS_ADDREF((txIFunctionEvaluationContext*&)invokeParam.val.p = context); @@ -412,7 +434,7 @@ txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext, return NS_ERROR_FAILURE; } - nsXPTCVariant &invokeParam = invokeParams[i]; + nsXPTCVariant &invokeParam = invokeParams.get()[i]; if (paramInfo.IsOut()) { // We don't support out values. return NS_ERROR_FAILURE; @@ -500,7 +522,7 @@ txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext, return NS_ERROR_FAILURE; } - nsXPTCVariant &returnParam = invokeParams[inArgs]; + nsXPTCVariant &returnParam = invokeParams.get()[inArgs]; returnParam.type = returnInfo.GetType(); if (returnType == eSTRING) { nsString *value = new nsString(); @@ -514,7 +536,7 @@ txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext, } } - rv = NS_InvokeByIndex(mHelper, mMethodIndex, paramCount, invokeParams); + rv = NS_InvokeByIndex(mHelper, mMethodIndex, paramCount, invokeParams.get()); // In case someone is holding on to the txFunctionEvaluationContext which // could thus stay alive longer than this function. diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp index 379ba4f33da..2e4768fb3d3 100644 --- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp @@ -39,6 +39,7 @@ #include "mozilla/Attributes.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/EncodingUtils.h" +#include "mozilla/UniquePtr.h" using namespace mozilla; using mozilla::dom::EncodingUtils; @@ -523,9 +524,9 @@ handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler) dom::Element* element = aNode->AsElement(); uint32_t attsCount = element->GetAttrCount(); - nsAutoArrayPtr atts; + UniquePtr atts; if (attsCount > 0) { - atts = new txStylesheetAttr[attsCount]; + atts = MakeUnique(attsCount); uint32_t counter; for (counter = 0; counter < attsCount; ++counter) { txStylesheetAttr& att = atts[counter]; @@ -541,7 +542,7 @@ handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler) rv = aCompiler->startElement(ni->NamespaceID(), ni->NameAtom(), - ni->GetPrefixAtom(), atts, + ni->GetPrefixAtom(), atts.get(), attsCount); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/xslt/xslt/txStylesheetCompiler.cpp b/dom/xslt/xslt/txStylesheetCompiler.cpp index b086f44bfbf..bf37a9fb240 100644 --- a/dom/xslt/xslt/txStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txStylesheetCompiler.cpp @@ -5,6 +5,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Move.h" +#include "mozilla/UniquePtr.h" #include "txStylesheetCompiler.h" #include "txStylesheetCompileHandlers.h" @@ -124,9 +125,9 @@ txStylesheetCompiler::startElement(const char16_t *aName, nsresult rv = flushCharacters(); NS_ENSURE_SUCCESS(rv, rv); - nsAutoArrayPtr atts; + UniquePtr atts; if (aAttrCount > 0) { - atts = new txStylesheetAttr[aAttrCount]; + atts = MakeUnique(aAttrCount); } bool hasOwnNamespaceMap = false; @@ -169,7 +170,7 @@ txStylesheetCompiler::startElement(const char16_t *aName, getter_AddRefs(localname), &namespaceID); NS_ENSURE_SUCCESS(rv, rv); - return startElementInternal(namespaceID, localname, prefix, atts, + return startElementInternal(namespaceID, localname, prefix, atts.get(), aAttrCount); } From e8856d4c4e97c85bd466d4d66f38be90cf71f7de Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Fri, 29 Jan 2016 10:00:45 -0500 Subject: [PATCH 05/91] Bug 1243039 - Allow, on try, to schedule TaskCluster test jobs multiple times. DONTBUILD. r=garndt --- testing/taskcluster/mach_commands.py | 9 +++++++-- .../taskcluster/taskcluster_graph/commit_parser.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/testing/taskcluster/mach_commands.py b/testing/taskcluster/mach_commands.py index 52fc7718f2a..074015557fc 100644 --- a/testing/taskcluster/mach_commands.py +++ b/testing/taskcluster/mach_commands.py @@ -320,7 +320,7 @@ class Graph(object): jobs = templates.load(job_path, {}) - job_graph = parse_commit(message, jobs) + job_graph, trigger_tests = parse_commit(message, jobs) cmdline_interactive = params.get('interactive', False) @@ -538,7 +538,12 @@ class Graph(object): treeherder_route ) - graph['tasks'].append(test_task) + # This will schedule test jobs N times + for i in range(0, trigger_tests): + graph['tasks'].append(test_task) + # If we're scheduling more tasks each have to be unique + test_task = copy.deepcopy(test_task) + test_task['taskId'] = slugid() define_task = DEFINE_TASK.format( test_task['task']['workerType'] diff --git a/testing/taskcluster/taskcluster_graph/commit_parser.py b/testing/taskcluster/taskcluster_graph/commit_parser.py index 7c41116b876..2a2416fabba 100644 --- a/testing/taskcluster/taskcluster_graph/commit_parser.py +++ b/testing/taskcluster/taskcluster_graph/commit_parser.py @@ -23,7 +23,7 @@ BUILD_TYPE_ALIASES = { class InvalidCommitException(Exception): pass -def escape_whitspace_in_brackets(input_str): +def escape_whitespace_in_brackets(input_str): ''' In tests you may restrict them by platform [] inside of the brackets whitespace may occur this is typically invalid shell syntax so we escape it @@ -241,7 +241,7 @@ def parse_commit(message, jobs): ''' # shlex used to ensure we split correctly when giving values to argparse. - parts = shlex.split(escape_whitspace_in_brackets(message)) + parts = shlex.split(escape_whitespace_in_brackets(message)) try_idx = None for idx, part in enumerate(parts): if part == TRY_DELIMITER: @@ -258,6 +258,8 @@ def parse_commit(message, jobs): parser.add_argument('-p', '--platform', nargs='?', dest='platforms', const='all', default='all') parser.add_argument('-u', '--unittests', nargs='?', dest='tests', const='all', default='all') parser.add_argument('-i', '--interactive', dest='interactive', action='store_true', default=False) + # In order to run test jobs multiple times + parser.add_argument('--trigger-tests', dest='trigger_tests', type=int, default=1) args, unknown = parser.parse_known_args(parts[try_idx:]) # Then builds... @@ -317,4 +319,7 @@ def parse_commit(message, jobs): 'interactive': args.interactive, }) - return result + # Times that test jobs will be scheduled + trigger_tests = args.trigger_tests + + return result, trigger_tests From 8bd23218d337e63b8380fa71ef8596279156b687 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Mon, 1 Feb 2016 16:11:00 +0100 Subject: [PATCH 06/91] Bug 1208226 - Don't crash when failing to map a segment of shared memory. r=sotaro, billm --- gfx/layers/client/TiledContentClient.h | 1 + gfx/layers/composite/TextureHost.cpp | 14 +++++++++++++- ipc/ipdl/ipdl/lower.py | 20 ++++++++++++++------ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h index bac1643aa85..c53b396fa3c 100644 --- a/gfx/layers/client/TiledContentClient.h +++ b/gfx/layers/client/TiledContentClient.h @@ -118,6 +118,7 @@ public: static already_AddRefed Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection) { + MOZ_RELEASE_ASSERT(aShmemSection.shmem().IsReadable()); RefPtr readLock = new gfxShmSharedReadLock(aAllocator, aShmemSection); return readLock.forget(); } diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index b05dc15a930..dcf9278717c 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -522,6 +522,8 @@ BufferTextureHost::Upload(nsIntRegion *aRegion) // We don't have a buffer; a possible cause is that the IPDL actor // is already dead. This inevitably happens as IPDL actors can die // at any time, so we want to silently return in this case. + // another possible cause is that IPDL failed to map the shmem when + // deserializing it. return false; } if (!mCompositor) { @@ -651,9 +653,19 @@ ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem, ISurfaceAllocator* aDeallocator, TextureFlags aFlags) : BufferTextureHost(aDesc, aFlags) -, mShmem(MakeUnique(aShmem)) , mDeallocator(aDeallocator) { + if (aShmem.IsReadable()) { + mShmem = MakeUnique(aShmem); + } else { + // This can happen if we failed to map the shmem on this process, perhaps + // because it was big and we didn't have enough contiguous address space + // available, even though we did on the child process. + // As a result this texture will be in an invalid state and Lock will + // always fail. + gfxCriticalError() << "Failed to create a valid ShmemTextureHost"; + } + MOZ_COUNT_CTOR(ShmemTextureHost); } diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index ebcc9b0c65f..4e7f9d7e740 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -4297,13 +4297,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): init=ExprCall(p.lookupSharedMemory(), args=[ idvar ])) ]) - failif = StmtIf(ExprNot(rawvar)) - failif.addifstmt(StmtReturn(_Result.ValuError)) + # Here we don't return an error if we failed to look the shmem up. This + # is because we don't have a way to know if it is because we failed to + # map the shmem or if the id is wrong. In the latter case it would be + # better to catch the error but the former case is legit... + lookupif = StmtIf(rawvar) + lookupif.addifstmt(StmtExpr(p.removeShmemId(idvar))) + lookupif.addifstmt(StmtExpr(_shmemDealloc(rawvar))) case.addstmts([ - failif, - StmtExpr(p.removeShmemId(idvar)), - StmtExpr(_shmemDealloc(rawvar)), + lookupif, StmtReturn(_Result.Processed) ]) @@ -4669,7 +4672,12 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name), init=_lookupShmem(idvar)), iffound, - StmtReturn.FALSE + # This is ugly: we failed to look the shmem up, most likely because + # we failed to map it the first time it was deserialized. we create + # an empty shmem and let the user of the shmem deal with it. + # if we returned false here we would crash. + StmtExpr(ExprAssn(ExprDeref(var), ExprCall(ExprVar('Shmem'), args=[]) )), + StmtReturn.TRUE ]) self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) From c14035091f913b9d0b3cac0fb34dd5f657b480c3 Mon Sep 17 00:00:00 2001 From: aleth Date: Mon, 1 Feb 2016 16:13:46 +0100 Subject: [PATCH 07/91] Bug 1244726 - Include mozIDOMWindow.h for nsDownloadManager. r=khuey --- toolkit/components/downloads/nsDownloadManager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/components/downloads/nsDownloadManager.h b/toolkit/components/downloads/nsDownloadManager.h index 488e4840d1d..566e3560add 100644 --- a/toolkit/components/downloads/nsDownloadManager.h +++ b/toolkit/components/downloads/nsDownloadManager.h @@ -24,6 +24,7 @@ #include "nsITimer.h" #include "nsString.h" +#include "mozIDOMWindow.h" #include "mozStorageHelper.h" #include "nsAutoPtr.h" #include "nsCOMArray.h" From a249626ef5156952ab699e3dd72d0d7f040a5b22 Mon Sep 17 00:00:00 2001 From: Chris H-C Date: Thu, 28 Jan 2016 09:19:00 +0100 Subject: [PATCH 08/91] Bug 1238121 - Properly guard Profiler's RAII classes r=BenWa f=mystor We can't use GuardObjects easily on the printf variant as va_list args and default args can't play together. --- tools/profiler/public/GeckoProfilerImpl.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/profiler/public/GeckoProfilerImpl.h b/tools/profiler/public/GeckoProfilerImpl.h index 2b565af15c5..9016be9a922 100644 --- a/tools/profiler/public/GeckoProfilerImpl.h +++ b/tools/profiler/public/GeckoProfilerImpl.h @@ -411,23 +411,26 @@ protected: const char* mInfo; }; -class MOZ_STACK_CLASS SamplerStackFrameRAII { +class MOZ_RAII SamplerStackFrameRAII { public: // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. SamplerStackFrameRAII(const char *aInfo, - js::ProfileEntry::Category aCategory, uint32_t line) + js::ProfileEntry::Category aCategory, uint32_t line + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line); } ~SamplerStackFrameRAII() { mozilla_sampler_call_exit(mHandle); } private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER void* mHandle; }; static const int SAMPLER_MAX_STRING = 128; -class MOZ_STACK_CLASS SamplerStackFramePrintfRAII { +class MOZ_RAII SamplerStackFramePrintfRAII { public: // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. SamplerStackFramePrintfRAII(const char *aInfo, From 4585df0fdd5c6c6dae48a9d61e541c23bbe834b4 Mon Sep 17 00:00:00 2001 From: "Dragana Damjanovic dd.mozilla@gmail.com" Date: Wed, 27 Jan 2016 01:47:00 +0100 Subject: [PATCH 09/91] Bug 1243312 - nsNetCID is missing in toolkit/system/unixproxy/nsLibProxySettings.cpp. r=jduell --- toolkit/system/unixproxy/nsLibProxySettings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/system/unixproxy/nsLibProxySettings.cpp b/toolkit/system/unixproxy/nsLibProxySettings.cpp index f6ec37773c9..e9179c11464 100644 --- a/toolkit/system/unixproxy/nsLibProxySettings.cpp +++ b/toolkit/system/unixproxy/nsLibProxySettings.cpp @@ -9,6 +9,7 @@ #include "nsIURI.h" #include "nsString.h" #include "nsCOMPtr.h" +#include "nsNetCID.h" #include "nspr.h" extern "C" { From c6677313e97aed11db4f18165052a098302b44be Mon Sep 17 00:00:00 2001 From: "Ciro S. Costa" Date: Fri, 20 Feb 2015 20:24:35 -0200 Subject: [PATCH 10/91] Bug 1089232 - Updates nsContentPrefService to take an extra isPrivate argument. r=adw --- browser/base/content/browser-fullZoom.js | 10 ++--- dom/interfaces/base/nsIContentPrefService.idl | 31 +++++++------ .../contentprefs/ContentPrefInstance.jsm | 5 ++- .../contentprefs/ContentPrefService2.jsm | 4 +- .../contentprefs/nsContentPrefService.js | 24 ++++++----- .../tests/unit/test_contentPrefs.js | 43 ++++++++++++------- .../tests/unit_cps2/test_observers.js | 24 ++++++++--- 7 files changed, 88 insertions(+), 53 deletions(-) diff --git a/browser/base/content/browser-fullZoom.js b/browser/base/content/browser-fullZoom.js index 090fc555524..14e43c4c0e0 100644 --- a/browser/base/content/browser-fullZoom.js +++ b/browser/base/content/browser-fullZoom.js @@ -111,8 +111,8 @@ var FullZoom = { // nsIContentPrefObserver - onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) { - this._onContentPrefChanged(aGroup, aValue); + onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue, aIsPrivate) { + this._onContentPrefChanged(aGroup, aValue, aIsPrivate); }, onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) { @@ -127,7 +127,7 @@ var FullZoom = { * @param aValue The new value of the changed preference. Pass undefined to * indicate the preference's removal. */ - _onContentPrefChanged: function FullZoom__onContentPrefChanged(aGroup, aValue) { + _onContentPrefChanged: function FullZoom__onContentPrefChanged(aGroup, aValue, aIsPrivate) { if (this._isNextContentPrefChangeInternal) { // Ignore changes that FullZoom itself makes. This works because the // content pref service calls callbacks before notifying observers, and it @@ -140,9 +140,10 @@ var FullZoom = { if (!browser.currentURI) return; + let ctxt = this._loadContextFromBrowser(browser); let domain = this._cps2.extractDomain(browser.currentURI.spec); if (aGroup) { - if (aGroup == domain) + if (aGroup == domain && ctxt.usePrivateBrowsing == aIsPrivate) this._applyPrefToZoom(aValue, browser); return; } @@ -154,7 +155,6 @@ var FullZoom = { // zoom should be set to the new global preference now that the global // preference has changed. let hasPref = false; - let ctxt = this._loadContextFromBrowser(browser); let token = this._getBrowserToken(browser); this._cps2.getByDomainAndName(browser.currentURI.spec, this.name, ctxt, { handleResult: function () { hasPref = true; }, diff --git a/dom/interfaces/base/nsIContentPrefService.idl b/dom/interfaces/base/nsIContentPrefService.idl index 60ac2ec3992..204650f211e 100644 --- a/dom/interfaces/base/nsIContentPrefService.idl +++ b/dom/interfaces/base/nsIContentPrefService.idl @@ -10,22 +10,27 @@ interface nsIContentURIGrouper; interface nsILoadContext; interface mozIStorageConnection; -[scriptable, uuid(746c7a02-f6c1-4869-b434-7c8b86e60e61)] +[scriptable, uuid(43635c53-b445-4c4e-8cc5-562697299b55)] interface nsIContentPrefObserver : nsISupports { /** * Called when a content pref is set to a different value. - * + * * @param aGroup the group to which the pref belongs, or null * if it's a global pref (applies to all sites) * @param aName the name of the pref that was set * @param aValue the new value of the pref + * @param aIsPrivate an optional flag determining whether the + * original context is private or not */ - void onContentPrefSet(in AString aGroup, in AString aName, in nsIVariant aValue); + void onContentPrefSet(in AString aGroup, + in AString aName, + in nsIVariant aValue, + [optional] in boolean aIsPrivate); /** * Called when a content pref is removed. - * + * * @param aGroup the group to which the pref belongs, or null * if it's a global pref (applies to all sites) * @param aName the name of the pref that was removed @@ -58,7 +63,7 @@ interface nsIContentPrefService : nsISupports * * @param aGroup the group for which to get the pref, as an nsIURI * from which the hostname will be used, a string - * (typically in the format of a hostname), or null + * (typically in the format of a hostname), or null * to get the global pref (applies to all sites) * @param aName the name of the pref to get * @param aPrivacyContext @@ -99,7 +104,7 @@ interface nsIContentPrefService : nsISupports * @throws NS_ERROR_ILLEGAL_VALUE if aName is null or an empty string */ void setPref(in nsIVariant aGroup, in AString aName, in nsIVariant aValue, in nsILoadContext aPrivacyContext); - + /** * Check whether or not a pref exists. * @@ -142,7 +147,7 @@ interface nsIContentPrefService : nsISupports * @param aGroup the group for which to remove the pref, as an nsIURI * from which the hostname will be used, a string * (typically in the format of a hostname), or null - * to remove the global pref (applies to all sites) + * to remove the global pref (applies to all sites) * @param aName the name of the pref to remove * @param aPrivacyContext * a context from which to determine the privacy status @@ -185,13 +190,13 @@ interface nsIContentPrefService : nsISupports * @param aGroup the group for which to retrieve prefs, as an nsIURI * from which the hostname will be used, a string * (typically in the format of a hostname), or null - * to get the global prefs (apply to all sites) + * to get the global prefs (apply to all sites) * @param aPrivacyContext * a context from which to determine the privacy status * of the pref (ie. whether to search for prefs in memory * or in permanent storage), obtained from a relevant * window or channel. - * + * * @returns a property bag of prefs * @throws NS_ERROR_ILLEGAL_VALUE if aGroup is not a string, nsIURI, or null */ @@ -206,21 +211,21 @@ interface nsIContentPrefService : nsISupports * of the pref (ie. whether to search for prefs in memory * or in permanent storage), obtained from a relevant * window or channel. - * + * * @returns a property bag of prefs * @throws NS_ERROR_ILLEGAL_VALUE if aName is null or an empty string */ nsIPropertyBag2 getPrefsByName(in AString aName, in nsILoadContext aContext); - + /** * Add an observer. - * + * * @param aName the setting to observe, or null to add * a generic observer that observes all settings * @param aObserver the observer to add */ void addObserver(in AString aName, in nsIContentPrefObserver aObserver); - + /** * Remove an observer. * diff --git a/toolkit/components/contentprefs/ContentPrefInstance.jsm b/toolkit/components/contentprefs/ContentPrefInstance.jsm index 1d99d03d884..395569995f7 100644 --- a/toolkit/components/contentprefs/ContentPrefInstance.jsm +++ b/toolkit/components/contentprefs/ContentPrefInstance.jsm @@ -24,8 +24,9 @@ ContentPrefInstance.prototype = { return this._contentPrefSvc.getPref(aName, aGroup, this._context, aCallback); }, - setPref: function ContentPrefInstance_setPref(aGroup, aName, aValue) { - return this._contentPrefSvc.setPref(aGroup, aName, aValue, this._context); + setPref: function ContentPrefInstance_setPref(aGroup, aName, aValue, aContext) { + return this._contentPrefSvc.setPref(aGroup, aName, aValue, + aContext ? aContext : this._context); }, hasPref: function ContentPrefInstance_hasPref(aGroup, aName) { diff --git a/toolkit/components/contentprefs/ContentPrefService2.jsm b/toolkit/components/contentprefs/ContentPrefService2.jsm index d21bfbb7177..e4273165e9b 100644 --- a/toolkit/components/contentprefs/ContentPrefService2.jsm +++ b/toolkit/components/contentprefs/ContentPrefService2.jsm @@ -254,7 +254,7 @@ ContentPrefService2.prototype = { this._pbStore.set(group, name, value); this._schedule(function () { cbHandleCompletion(callback, Ci.nsIContentPrefCallback2.COMPLETE_OK); - this._cps._notifyPrefSet(group, name, value); + this._cps._notifyPrefSet(group, name, value, context.usePrivateBrowsing); }); return; } @@ -327,7 +327,7 @@ ContentPrefService2.prototype = { this._cache.setWithCast(group, name, value); cbHandleCompletion(callback, reason); if (ok) - this._cps._notifyPrefSet(group, name, value); + this._cps._notifyPrefSet(group, name, value, context && context.usePrivateBrowsing); }, onError: function onError(nsresult) { cbHandleError(callback, nsresult); diff --git a/toolkit/components/contentprefs/nsContentPrefService.js b/toolkit/components/contentprefs/nsContentPrefService.js index 7c9da9493da..68ad98a1781 100644 --- a/toolkit/components/contentprefs/nsContentPrefService.js +++ b/toolkit/components/contentprefs/nsContentPrefService.js @@ -256,7 +256,7 @@ ContentPrefService.prototype = { if (aContext && aContext.usePrivateBrowsing) { this._privModeStorage.setWithCast(group, aName, aValue); - this._notifyPrefSet(group, aName, aValue); + this._notifyPrefSet(group, aName, aValue, aContext.usePrivateBrowsing); return; } @@ -278,7 +278,9 @@ ContentPrefService.prototype = { this._insertPref(groupID, settingID, aValue); this._cache.setWithCast(group, aName, aValue); - this._notifyPrefSet(group, aName, aValue); + + this._notifyPrefSet(group, aName, aValue, + aContext ? aContext.usePrivateBrowsing : false); }, hasPref: function ContentPrefService_hasPref(aGroup, aName, aContext) { @@ -382,19 +384,19 @@ ContentPrefService.prototype = { var settingID = this._selectSettingID(aName); if (!settingID) return; - + var selectGroupsStmt = this._dbCreateStatement(` SELECT groups.id AS groupID, groups.name AS groupName FROM prefs JOIN groups ON prefs.groupID = groups.id WHERE prefs.settingID = :setting `); - + var groupNames = []; var groupIDs = []; try { selectGroupsStmt.params.setting = settingID; - + while (selectGroupsStmt.executeStep()) { groupIDs.push(selectGroupsStmt.row["groupID"]); groupNames.push(selectGroupsStmt.row["groupName"]); @@ -403,7 +405,7 @@ ContentPrefService.prototype = { finally { selectGroupsStmt.reset(); } - + if (this.hasPref(null, aName)) { groupNames.push(null); } @@ -538,10 +540,10 @@ ContentPrefService.prototype = { /** * Notify all observers about a preference change. */ - _notifyPrefSet: function ContentPrefService__notifyPrefSet(aGroup, aName, aValue) { + _notifyPrefSet: function ContentPrefService__notifyPrefSet(aGroup, aName, aValue, aIsPrivate) { for (var observer of this._getObservers(aName)) { try { - observer.onContentPrefSet(aGroup, aName, aValue); + observer.onContentPrefSet(aGroup, aName, aValue, aIsPrivate); } catch(ex) { Cu.reportError(ex); @@ -985,7 +987,7 @@ ContentPrefService.prototype = { finally { this._stmtSelectPrefsByName.reset(); } - + var global = this._selectGlobalPref(aName); if (typeof global != "undefined") { prefs.setProperty(null, global); @@ -1261,7 +1263,7 @@ function HostnameGrouper() {} HostnameGrouper.prototype = { //**************************************************************************// // XPCOM Plumbing - + classID: Components.ID("{8df290ae-dcaa-4c11-98a5-2429a4dc97bb}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentURIGrouper]), @@ -1286,7 +1288,7 @@ HostnameGrouper.prototype = { // reference, and hash, if possible) as the group. This means that URIs // like about:mozilla and about:blank will be considered separate groups, // but at least they'll be grouped somehow. - + // This also means that each individual file: URL will be considered // its own group. This seems suboptimal, but so does treating the entire // file: URL space as a single group (especially if folks start setting diff --git a/toolkit/components/contentprefs/tests/unit/test_contentPrefs.js b/toolkit/components/contentprefs/tests/unit/test_contentPrefs.js index 9bb21b50962..7d6d2f2826d 100644 --- a/toolkit/components/contentprefs/tests/unit/test_contentPrefs.js +++ b/toolkit/components/contentprefs/tests/unit/test_contentPrefs.js @@ -186,7 +186,7 @@ function run_test() { //**************************************************************************// // getPrefs - + cps.setPref(uri, "test.getPrefs.a", 1); cps.setPref(uri, "test.getPrefs.b", 2); cps.setPref(uri, "test.getPrefs.c", 3); @@ -236,7 +236,7 @@ function run_test() { var specificObserver = { interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports], - + QueryInterface: function ContentPrefTest_QueryInterface(iid) { if (!this.interfaces.some( function(v) { return iid.equals(v) } )) throw Cr.NS_ERROR_NO_INTERFACE; @@ -262,7 +262,7 @@ function run_test() { var genericObserver = { interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports], - + QueryInterface: function ContentPrefTest_QueryInterface(iid) { if (!this.interfaces.some( function(v) { return iid.equals(v) } )) throw Cr.NS_ERROR_NO_INTERFACE; @@ -270,12 +270,16 @@ function run_test() { }, numTimesSetCalled: 0, - onContentPrefSet: function genericObserver_onContentPrefSet(group, name, value) { + onContentPrefSet: function genericObserver_onContentPrefSet(group, name, value, isPrivate) { ++this.numTimesSetCalled; do_check_eq(group, "www.example.com"); - if (name != "test.observer.1" && name != "test.observer.2") + if (name == "test.observer.private") + do_check_true(isPrivate); + else if (name == "test.observer.normal") + do_check_false(isPrivate); + else if (name != "test.observer.1" && name != "test.observer.2") do_throw("genericObserver.onContentPrefSet: " + - "name not in (test.observer.1, test.observer.2)"); + "name not in (test.observer.(1|2|normal|private))"); do_check_eq(value, "test value"); }, @@ -283,9 +287,11 @@ function run_test() { onContentPrefRemoved: function genericObserver_onContentPrefRemoved(group, name) { ++this.numTimesRemovedCalled; do_check_eq(group, "www.example.com"); - if (name != "test.observer.1" && name != "test.observer.2") + if (name != "test.observer.1" && name != "test.observer.2" && + name != "test.observer.normal" && name != "test.observer.private") { do_throw("genericObserver.onContentPrefSet: " + - "name not in (test.observer.1, test.observer.2)"); + "name not in (test.observer.(1|2|normal|private))"); + } } }; @@ -304,6 +310,13 @@ function run_test() { do_check_eq(specificObserver.numTimesRemovedCalled, 1); do_check_eq(genericObserver.numTimesRemovedCalled, 2); + // // Make sure information about private context is properly + // // retrieved by the observer. + cps.setPref(uri, "test.observer.private", "test value", {usePrivateBrowsing: true}); + cps.setPref(uri, "test.observer.normal", "test value", {usePrivateBrowsing: false}); + cps.removePref(uri, "test.observer.private"); + cps.removePref(uri, "test.observer.normal"); + // Make sure we can remove observers and they don't get notified // about changes anymore. cps.removeObserver("test.observer.1", specificObserver); @@ -311,14 +324,14 @@ function run_test() { cps.setPref(uri, "test.observer.1", "test value"); cps.removePref(uri, "test.observer.1", "test value"); do_check_eq(specificObserver.numTimesSetCalled, 1); - do_check_eq(genericObserver.numTimesSetCalled, 2); + do_check_eq(genericObserver.numTimesSetCalled, 4); do_check_eq(specificObserver.numTimesRemovedCalled, 1); - do_check_eq(genericObserver.numTimesRemovedCalled, 2); + do_check_eq(genericObserver.numTimesRemovedCalled, 3); //**************************************************************************// // Get/Remove Prefs By Name - + { var anObserver = { interfaces: [Ci.nsIContentPrefObserver, Ci.nsISupports], @@ -356,7 +369,7 @@ function run_test() { cps.setPref(uri4, "test.byname.1", 16); cps.setPref(null, "test.byname.1", 32); cps.setPref(null, "test.byname.2", false); - + function enumerateAndCheck(testName, expectedSum, expectedDomains) { var prefsByName = cps.getPrefsByName(testName); var enumerator = prefsByName.enumerator; @@ -374,7 +387,7 @@ function run_test() { // check all domains have been removed from the array do_check_eq(expectedDomains.length, 0); } - + enumerateAndCheck("test.byname.1", 53, ["foo.domain1.com", null, "www.domain1.com", "www.domain2.com"]); enumerateAndCheck("test.byname.2", 2, ["www.domain1.com", null]); @@ -394,9 +407,9 @@ function run_test() { do_check_eq(anObserver.numTimesRemovedCalled, 4); do_check_eq(anObserver.expectedDomains.length, 0); - + cps.removeObserver("test.byname.1", anObserver); - + // Clean up after ourselves cps.removePref(uri1, "test.byname.2"); cps.removePref(uri3, "test.byname.3"); diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js index 721d6677949..afb09794f38 100644 --- a/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js @@ -11,14 +11,28 @@ var tests = [ function* observerForName_set() { yield set("a.com", "foo", 1); let args = yield on("Set", ["foo", null, "bar"]); - observerArgsOK(args.foo, [["a.com", "foo", 1]]); - observerArgsOK(args.null, [["a.com", "foo", 1]]); + observerArgsOK(args.foo, [["a.com", "foo", 1, false]]); + observerArgsOK(args.null, [["a.com", "foo", 1, false]]); observerArgsOK(args.bar, []); yield setGlobal("foo", 2); args = yield on("Set", ["foo", null, "bar"]); - observerArgsOK(args.foo, [[null, "foo", 2]]); - observerArgsOK(args.null, [[null, "foo", 2]]); + observerArgsOK(args.foo, [[null, "foo", 2, false]]); + observerArgsOK(args.null, [[null, "foo", 2, false]]); + observerArgsOK(args.bar, []); + }, + + function* observerForName_set_private() { + yield set("a.com", "foo", 1, { usePrivateBrowsing: true }); + let args = yield on("Set", ["foo", null, "bar"]); + observerArgsOK(args.foo, [["a.com", "foo", 1, true]]); + observerArgsOK(args.null, [["a.com", "foo", 1, true]]); + observerArgsOK(args.bar, []); + + yield setGlobal("foo", 2, { usePrivateBrowsing: true }); + args = yield on("Set", ["foo", null, "bar"]); + observerArgsOK(args.foo, [[null, "foo", 2, true]]); + observerArgsOK(args.null, [[null, "foo", 2, true]]); observerArgsOK(args.bar, []); }, @@ -125,7 +139,7 @@ var tests = [ yield set("a.com", "foo", 1); yield wait(); observerArgsOK(args.foo, []); - observerArgsOK(args.null, [["a.com", "foo", 1]]); + observerArgsOK(args.null, [["a.com", "foo", 1, false]]); observerArgsOK(args.bar, []); args.reset(); From 58c47947649e737eb7631628b782ed1e71837ef1 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 1 Feb 2016 10:16:25 -0600 Subject: [PATCH 11/91] Bug 1244403 - Baldr: move kind out of WasmAstNode and into WasmAstExpr (r=bbouvier) --- js/src/asmjs/WasmText.cpp | 66 +++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 193aa608630..f99131749a5 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -88,40 +88,32 @@ class WasmAstSig : public WasmAstBase } }; -enum class WasmAstKind +class WasmAstNode : public WasmAstBase +{}; + +enum class WasmAstExprKind { BinaryOperator, Block, Call, Const, - Export, - Func, GetLocal, - Import, - Module, Nop, SetLocal }; -class WasmAstNode : public WasmAstBase -{ - const WasmAstKind kind_; - - public: - explicit WasmAstNode(WasmAstKind kind) - : kind_(kind) - {} - WasmAstKind kind() const { return kind_; } -}; - class WasmAstExpr : public WasmAstNode { + const WasmAstExprKind kind_; + protected: - explicit WasmAstExpr(WasmAstKind kind) - : WasmAstNode(kind) + explicit WasmAstExpr(WasmAstExprKind kind) + : kind_(kind) {} public: + WasmAstExprKind kind() const { return kind_; } + template T& as() { MOZ_ASSERT(kind() == T::Kind); @@ -132,7 +124,7 @@ class WasmAstExpr : public WasmAstNode struct WasmAstNop : WasmAstExpr { WasmAstNop() - : WasmAstExpr(WasmAstKind::Nop) + : WasmAstExpr(WasmAstExprKind::Nop) {} }; @@ -141,7 +133,7 @@ class WasmAstConst : public WasmAstExpr const Val val_; public: - static const WasmAstKind Kind = WasmAstKind::Const; + static const WasmAstExprKind Kind = WasmAstExprKind::Const; explicit WasmAstConst(Val val) : WasmAstExpr(Kind), val_(val) @@ -154,7 +146,7 @@ class WasmAstGetLocal : public WasmAstExpr uint32_t localIndex_; public: - static const WasmAstKind Kind = WasmAstKind::GetLocal; + static const WasmAstExprKind Kind = WasmAstExprKind::GetLocal; explicit WasmAstGetLocal(uint32_t localIndex) : WasmAstExpr(Kind), localIndex_(localIndex) @@ -170,7 +162,7 @@ class WasmAstSetLocal : public WasmAstExpr WasmAstExpr& value_; public: - static const WasmAstKind Kind = WasmAstKind::SetLocal; + static const WasmAstExprKind Kind = WasmAstExprKind::SetLocal; WasmAstSetLocal(uint32_t localIndex, WasmAstExpr& value) : WasmAstExpr(Kind), localIndex_(localIndex), @@ -189,7 +181,7 @@ class WasmAstBlock : public WasmAstExpr WasmAstExprVector exprs_; public: - static const WasmAstKind Kind = WasmAstKind::Block; + static const WasmAstExprKind Kind = WasmAstExprKind::Block; explicit WasmAstBlock(WasmAstExprVector&& exprs) : WasmAstExpr(Kind), exprs_(Move(exprs)) @@ -205,7 +197,7 @@ class WasmAstCall : public WasmAstExpr WasmAstExprVector args_; public: - static const WasmAstKind Kind = WasmAstKind::Call; + static const WasmAstExprKind Kind = WasmAstExprKind::Call; WasmAstCall(Expr expr, uint32_t index, WasmAstExprVector&& args) : WasmAstExpr(Kind), expr_(expr), index_(index), args_(Move(args)) {} @@ -223,8 +215,7 @@ class WasmAstFunc : public WasmAstNode public: WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExpr* maybeBody) - : WasmAstNode(WasmAstKind::Func), - sigIndex_(sigIndex), + : sigIndex_(sigIndex), varTypes_(Move(varTypes)), maybeBody_(maybeBody) {} @@ -241,7 +232,7 @@ class WasmAstImport : public WasmAstNode public: WasmAstImport(TwoByteChars module, TwoByteChars func, uint32_t sigIndex) - : WasmAstNode(WasmAstKind::Import), module_(module), func_(func), sigIndex_(sigIndex) + : module_(module), func_(func), sigIndex_(sigIndex) {} TwoByteChars module() const { return module_; } TwoByteChars func() const { return func_; } @@ -255,7 +246,7 @@ class WasmAstExport : public WasmAstNode public: WasmAstExport(TwoByteChars name, uint32_t funcIndex) - : WasmAstNode(WasmAstKind::Export), name_(name), funcIndex_(funcIndex) + : name_(name), funcIndex_(funcIndex) {} TwoByteChars name() const { return name_; } size_t funcIndex() const { return funcIndex_; } @@ -278,8 +269,7 @@ class WasmAstModule : public WasmAstNode public: explicit WasmAstModule(LifoAlloc& lifo) - : WasmAstNode(WasmAstKind::Module), - lifo_(lifo), + : lifo_(lifo), funcs_(lifo), imports_(lifo), exports_(lifo), @@ -329,7 +319,7 @@ class WasmAstBinaryOperator final : public WasmAstExpr WasmAstExpr* rhs_; public: - static const WasmAstKind Kind = WasmAstKind::BinaryOperator; + static const WasmAstExprKind Kind = WasmAstExprKind::BinaryOperator; explicit WasmAstBinaryOperator(Expr expr, WasmAstExpr* lhs, WasmAstExpr* rhs) : WasmAstExpr(Kind), expr_(expr), lhs_(lhs), rhs_(rhs) @@ -1227,19 +1217,19 @@ static bool EncodeExpr(Encoder& e, WasmAstExpr& expr) { switch (expr.kind()) { - case WasmAstKind::Nop: + case WasmAstExprKind::Nop: return e.writeExpr(Expr::Nop); - case WasmAstKind::BinaryOperator: + case WasmAstExprKind::BinaryOperator: return EncodeBinaryOperator(e, expr.as()); - case WasmAstKind::Block: + case WasmAstExprKind::Block: return EncodeBlock(e, expr.as()); - case WasmAstKind::Call: + case WasmAstExprKind::Call: return EncodeCall(e, expr.as()); - case WasmAstKind::Const: + case WasmAstExprKind::Const: return EncodeConst(e, expr.as()); - case WasmAstKind::GetLocal: + case WasmAstExprKind::GetLocal: return EncodeGetLocal(e, expr.as()); - case WasmAstKind::SetLocal: + case WasmAstExprKind::SetLocal: return EncodeSetLocal(e, expr.as()); default:; } From d50242b90e9e5c4267a6d203f364ecce83c17195 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 1 Feb 2016 10:16:25 -0600 Subject: [PATCH 12/91] Bug 1244403 - Baldr: remove unnecessary 'end' argument from (r=bbouvier) --- js/src/asmjs/WasmText.cpp | 108 +++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index f99131749a5..4b55c48c664 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -494,10 +494,10 @@ class WasmTokenStream uint32_t lookaheadDepth_; WasmToken lookahead_[LookaheadSize]; - bool consume(const char16_t* end, const char16_t* match) { + bool consume(const char16_t* match) { const char16_t* p = cur_; for (; *match; p++, match++) { - if (p == end || *p != *match) + if (p == end_ || *p != *match) return false; } cur_ = p; @@ -551,90 +551,90 @@ class WasmTokenStream } case 'b': - if (consume(end_, MOZ_UTF16("lock"))) + if (consume(MOZ_UTF16("lock"))) return WasmToken(WasmToken::Block, begin, cur_); break; case 'c': - if (consume(end_, MOZ_UTF16("all"))) { - if (consume(end_, MOZ_UTF16("_import"))) + if (consume(MOZ_UTF16("all"))) { + if (consume(MOZ_UTF16("_import"))) return WasmToken(WasmToken::CallImport, begin, cur_); return WasmToken(WasmToken::Call, begin, cur_); } break; case 'e': - if (consume(end_, MOZ_UTF16("xport"))) + if (consume(MOZ_UTF16("xport"))) return WasmToken(WasmToken::Export, begin, cur_); break; case 'f': - if (consume(end_, MOZ_UTF16("unc"))) + if (consume(MOZ_UTF16("unc"))) return WasmToken(WasmToken::Func, begin, cur_); - if (consume(end_, MOZ_UTF16("32"))) { - if (!consume(end_, MOZ_UTF16("."))) + if (consume(MOZ_UTF16("32"))) { + if (!consume(MOZ_UTF16("."))) return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_); switch (*cur_) { case 'a': - if (consume(end_, MOZ_UTF16("add"))) + if (consume(MOZ_UTF16("add"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Add, begin, cur_); break; case 'c': - if (consume(end_, MOZ_UTF16("const"))) + if (consume(MOZ_UTF16("const"))) return WasmToken(WasmToken::Const, ValType::F32, begin, cur_); - if (consume(end_, MOZ_UTF16("copysign"))) + if (consume(MOZ_UTF16("copysign"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32CopySign, begin, cur_); break; case 'd': - if (consume(end_, MOZ_UTF16("div"))) + if (consume(MOZ_UTF16("div"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Div, begin, cur_); break; case 'm': - if (consume(end_, MOZ_UTF16("max"))) + if (consume(MOZ_UTF16("max"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_); - if (consume(end_, MOZ_UTF16("min"))) + if (consume(MOZ_UTF16("min"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_); - if (consume(end_, MOZ_UTF16("mul"))) + if (consume(MOZ_UTF16("mul"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_); break; case 's': - if (consume(end_, MOZ_UTF16("sub"))) + if (consume(MOZ_UTF16("sub"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_); break; } break; } - if (consume(end_, MOZ_UTF16("64"))) { - if (!consume(end_, MOZ_UTF16("."))) + if (consume(MOZ_UTF16("64"))) { + if (!consume(MOZ_UTF16("."))) return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_); switch (*cur_) { case 'a': - if (consume(end_, MOZ_UTF16("add"))) + if (consume(MOZ_UTF16("add"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Add, begin, cur_); break; case 'c': - if (consume(end_, MOZ_UTF16("const"))) + if (consume(MOZ_UTF16("const"))) return WasmToken(WasmToken::Const, ValType::F64, begin, cur_); - if (consume(end_, MOZ_UTF16("copysign"))) + if (consume(MOZ_UTF16("copysign"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64CopySign, begin, cur_); break; case 'd': - if (consume(end_, MOZ_UTF16("div"))) + if (consume(MOZ_UTF16("div"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Div, begin, cur_); break; case 'm': - if (consume(end_, MOZ_UTF16("max"))) + if (consume(MOZ_UTF16("max"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_); - if (consume(end_, MOZ_UTF16("min"))) + if (consume(MOZ_UTF16("min"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_); - if (consume(end_, MOZ_UTF16("mul"))) + if (consume(MOZ_UTF16("mul"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_); break; case 's': - if (consume(end_, MOZ_UTF16("sub"))) + if (consume(MOZ_UTF16("sub"))) return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_); break; } @@ -642,106 +642,106 @@ class WasmTokenStream break; case 'g': - if (consume(end_, MOZ_UTF16("et_local"))) + if (consume(MOZ_UTF16("et_local"))) return WasmToken(WasmToken::GetLocal, begin, cur_); break; case 'i': - if (consume(end_, MOZ_UTF16("32"))) { - if (!consume(end_, MOZ_UTF16("."))) + if (consume(MOZ_UTF16("32"))) { + if (!consume(MOZ_UTF16("."))) return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_); switch (*cur_) { case 'a': - if (consume(end_, MOZ_UTF16("add"))) + if (consume(MOZ_UTF16("add"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32Add, begin, cur_); - if (consume(end_, MOZ_UTF16("and"))) + if (consume(MOZ_UTF16("and"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32And, begin, cur_); break; case 'c': - if (consume(end_, MOZ_UTF16("const"))) + if (consume(MOZ_UTF16("const"))) return WasmToken(WasmToken::Const, ValType::I32, begin, cur_); break; case 'd': - if (consume(end_, MOZ_UTF16("div_s"))) + if (consume(MOZ_UTF16("div_s"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivS, begin, cur_); - if (consume(end_, MOZ_UTF16("div_u"))) + if (consume(MOZ_UTF16("div_u"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivU, begin, cur_); break; case 'm': - if (consume(end_, MOZ_UTF16("mul"))) + if (consume(MOZ_UTF16("mul"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_); break; case 'o': - if (consume(end_, MOZ_UTF16("or"))) + if (consume(MOZ_UTF16("or"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32Or, begin, cur_); break; case 'r': - if (consume(end_, MOZ_UTF16("rem_s"))) + if (consume(MOZ_UTF16("rem_s"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemS, begin, cur_); - if (consume(end_, MOZ_UTF16("rem_u"))) + if (consume(MOZ_UTF16("rem_u"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemU, begin, cur_); break; case 's': - if (consume(end_, MOZ_UTF16("sub"))) + if (consume(MOZ_UTF16("sub"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_); - if (consume(end_, MOZ_UTF16("shl"))) + if (consume(MOZ_UTF16("shl"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_); - if (consume(end_, MOZ_UTF16("shr_s"))) + if (consume(MOZ_UTF16("shr_s"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_); - if (consume(end_, MOZ_UTF16("shr_u"))) + if (consume(MOZ_UTF16("shr_u"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_); break; case 'x': - if (consume(end_, MOZ_UTF16("xor"))) + if (consume(MOZ_UTF16("xor"))) return WasmToken(WasmToken::BinaryOpcode, Expr::I32Xor, begin, cur_); break; } break; } - if (consume(end_, MOZ_UTF16("64"))) { - if (!consume(end_, MOZ_UTF16("."))) + if (consume(MOZ_UTF16("64"))) { + if (!consume(MOZ_UTF16("."))) return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_); switch (*cur_) { case 'c': - if (consume(end_, MOZ_UTF16("const"))) + if (consume(MOZ_UTF16("const"))) return WasmToken(WasmToken::Const, ValType::I64, begin, cur_); break; } break; } - if (consume(end_, MOZ_UTF16("mport"))) + if (consume(MOZ_UTF16("mport"))) return WasmToken(WasmToken::Import, begin, cur_); break; case 'l': - if (consume(end_, MOZ_UTF16("ocal"))) + if (consume(MOZ_UTF16("ocal"))) return WasmToken(WasmToken::Local, begin, cur_); break; case 'm': - if (consume(end_, MOZ_UTF16("odule"))) + if (consume(MOZ_UTF16("odule"))) return WasmToken(WasmToken::Module, begin, cur_); break; case 'n': - if (consume(end_, MOZ_UTF16("op"))) + if (consume(MOZ_UTF16("op"))) return WasmToken(WasmToken::Nop, begin, cur_); break; case 'p': - if (consume(end_, MOZ_UTF16("aram"))) + if (consume(MOZ_UTF16("aram"))) return WasmToken(WasmToken::Param, begin, cur_); break; case 'r': - if (consume(end_, MOZ_UTF16("esult"))) + if (consume(MOZ_UTF16("esult"))) return WasmToken(WasmToken::Result, begin, cur_); break; case 's': - if (consume(end_, MOZ_UTF16("et_local"))) + if (consume(MOZ_UTF16("et_local"))) return WasmToken(WasmToken::SetLocal, begin, cur_); break; From 5a692e24ea2b34ab5aa05d52bb201c7991e8947d Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 1 Feb 2016 10:16:25 -0600 Subject: [PATCH 13/91] Bug 1244403 - Baldr: expose export func entry offsets directly (r=bbouvier) --- js/src/asmjs/WasmGenerator.cpp | 6 ++++++ js/src/asmjs/WasmGenerator.h | 2 +- js/src/asmjs/WasmStubs.cpp | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index 03ee024218e..f34170d39b0 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -372,6 +372,12 @@ ModuleGenerator::exportFuncIndex(uint32_t index) const return exportFuncIndices_[index]; } +uint32_t +ModuleGenerator::exportEntryOffset(uint32_t index) const +{ + return funcEntryOffsets_[exportFuncIndices_[index]]; +} + const Sig& ModuleGenerator::exportSig(uint32_t index) const { diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index dce99c6a575..dd9cb824fa1 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -171,7 +171,6 @@ class MOZ_STACK_CLASS ModuleGenerator bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; } CompileArgs args() const { return module_->compileArgs; } jit::MacroAssembler& masm() { return masm_; } - const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; } // asm.js global variables: bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index); @@ -197,6 +196,7 @@ class MOZ_STACK_CLASS ModuleGenerator bool declareExport(uint32_t funcIndex, uint32_t* exportIndex); uint32_t numExports() const; uint32_t exportFuncIndex(uint32_t index) const; + uint32_t exportEntryOffset(uint32_t index) const; const Sig& exportSig(uint32_t index) const; bool defineExport(uint32_t index, Offsets offsets); diff --git a/js/src/asmjs/WasmStubs.cpp b/js/src/asmjs/WasmStubs.cpp index cd120e17017..e12a98cab23 100644 --- a/js/src/asmjs/WasmStubs.cpp +++ b/js/src/asmjs/WasmStubs.cpp @@ -236,7 +236,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap) // Call into the real function. masm.assertStackAlignment(AsmJSStackAlignment); Label target; - target.bind(mg.funcEntryOffsets()[mg.exportFuncIndex(exportIndex)]); + target.bind(mg.exportEntryOffset(exportIndex)); masm.call(CallSiteDesc(CallSiteDesc::Relative), &target); // Recover the stack pointer value before dynamic alignment. From 3e8a39f135929d0db566f6407a126b31a9b4e02d Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 1 Feb 2016 10:16:25 -0600 Subject: [PATCH 14/91] Bug 1244403 - Baldr: factor out DynamicLinkData (r=bbouvier) --- js/src/asmjs/AsmJS.cpp | 25 ++++----- js/src/asmjs/Wasm.cpp | 99 +++++++++++++++++++--------------- js/src/asmjs/WasmGenerator.cpp | 19 ++++--- js/src/asmjs/WasmGenerator.h | 9 ++-- 4 files changed, 84 insertions(+), 68 deletions(-) diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 39cf6117d86..d0fe96c6cee 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -1787,7 +1787,14 @@ class MOZ_STACK_CLASS ModuleValidator return false; } - return mg_.init(Move(genData), ModuleKind::AsmJS); + CacheableChars filename; + if (parser_.ss->filename()) { + filename = DuplicateString(parser_.ss->filename()); + if (!filename) + return false; + } + + return mg_.init(Move(genData), Move(filename), ModuleKind::AsmJS); } ExclusiveContext* cx() const { return cx_; } @@ -2223,18 +2230,8 @@ class MOZ_STACK_CLASS ModuleValidator return mg_.finishFuncDefs(); } bool finish(MutableHandle moduleObj, SlowFunctionVector* slowFuncs) { - HeapUsage heap = arrayViews_.empty() - ? HeapUsage::None - : atomicsPresent_ - ? HeapUsage::Shared - : HeapUsage::Unshared; - - CacheableChars filename; - if (parser_.ss->filename()) { - filename = DuplicateString(parser_.ss->filename()); - if (!filename) - return false; - } + if (!arrayViews_.empty()) + mg_.initHeapUsage(atomicsPresent_ ? HeapUsage::Shared : HeapUsage::Unshared); CacheableCharsVector funcNames; for (const Func* func : functions_) { @@ -2253,7 +2250,7 @@ class MOZ_STACK_CLASS ModuleValidator UniqueModuleData base; UniqueStaticLinkData link; - if (!mg_.finish(heap, Move(filename), Move(funcNames), &base, &link, slowFuncs)) + if (!mg_.finish(Move(funcNames), &base, &link, slowFuncs)) return false; moduleObj.set(WasmModuleObject::create(cx_)); diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index a2cb40c6641..a75c1d7a9ae 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -349,6 +349,32 @@ DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator return true; } +/*****************************************************************************/ +// dynamic link data + +struct ImportName +{ + UniqueChars module; + UniqueChars func; + + ImportName(UniqueChars module, UniqueChars func) + : module(Move(module)), func(Move(func)) + {} + ImportName(ImportName&& rhs) + : module(Move(rhs.module)), func(Move(rhs.func)) + {} +}; + +typedef Vector ImportNameVector; + +struct DynamicLinkData +{ + ImportNameVector importNames; + ExportMap exportMap; +}; + +typedef UniquePtr UniqueDynamicLinkData; + /*****************************************************************************/ // wasm decoding and generation @@ -448,23 +474,8 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) return true; } -struct ImportName -{ - UniqueChars module; - UniqueChars func; - - ImportName(UniqueChars module, UniqueChars func) - : module(Move(module)), func(Move(func)) - {} - ImportName(ImportName&& rhs) - : module(Move(rhs.module)), func(Move(rhs.func)) - {} -}; - -typedef Vector ImportNameVector; - static bool -DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames) +DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, DynamicLinkData* link) { if (!d.readCStringIf(FuncSubsection)) return Fail(cx, d, "expected 'func' tag"); @@ -495,11 +506,11 @@ DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVec if (!funcName) return false; - return importNames->emplaceBack(Move(moduleName), Move(funcName)); + return link->importNames.emplaceBack(Move(moduleName), Move(funcName)); } static bool -DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* imports) +DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, DynamicLinkData* link) { if (!d.readCStringIf(ImportSection)) return true; @@ -516,7 +527,7 @@ DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, Import return Fail(cx, d, "too many imports"); for (uint32_t i = 0; i < numImports; i++) { - if (!DecodeImport(cx, d, init, imports)) + if (!DecodeImport(cx, d, init, link)) return false; } @@ -527,7 +538,7 @@ DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, Import } static bool -DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap) +DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, DynamicLinkData* link) { if (!d.readCStringIf(FuncSubsection)) return Fail(cx, d, "expected 'func' tag"); @@ -543,27 +554,29 @@ DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMa if (!mg.declareExport(funcIndex, &exportIndex)) return false; - MOZ_ASSERT(exportIndex <= exportMap->exportNames.length()); - if (exportIndex == exportMap->exportNames.length()) { + ExportMap& exportMap = link->exportMap; + + MOZ_ASSERT(exportIndex <= exportMap.exportNames.length()); + if (exportIndex == exportMap.exportNames.length()) { UniqueChars funcName(JS_smprintf("%u", unsigned(funcIndex))); - if (!funcName || !exportMap->exportNames.emplaceBack(Move(funcName))) + if (!funcName || !exportMap.exportNames.emplaceBack(Move(funcName))) return false; } - if (!exportMap->fieldsToExports.append(exportIndex)) + if (!exportMap.fieldsToExports.append(exportIndex)) return false; const char* chars; if (!d.readCString(&chars)) return Fail(cx, d, "expected export external name string"); - return exportMap->fieldNames.emplaceBack(DuplicateString(chars)); + return exportMap.fieldNames.emplaceBack(DuplicateString(chars)); } typedef HashSet CStringSet; static bool -DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap) +DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, DynamicLinkData* link) { if (!d.readCStringIf(ExportSection)) return true; @@ -580,7 +593,7 @@ DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* return Fail(cx, d, "too many exports"); for (uint32_t i = 0; i < numExports; i++) { - if (!DecodeExport(cx, d, mg, exportMap)) + if (!DecodeExport(cx, d, mg, link)) return false; } @@ -590,7 +603,7 @@ DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* CStringSet dupSet(cx); if (!dupSet.init()) return false; - for (const UniqueChars& prevName : exportMap->fieldNames) { + for (const UniqueChars& prevName : link->exportMap.fieldNames) { CStringSet::AddPtr p = dupSet.lookupForAdd(prevName.get()); if (p) return Fail(cx, d, "duplicate export"); @@ -699,8 +712,7 @@ DecodeUnknownSection(JSContext* cx, Decoder& d) static bool DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t length, - ImportNameVector* importNames, ExportMap* exportMap, - MutableHandle moduleObj) + UniqueDynamicLinkData* dynamicLink, MutableHandle moduleObj) { Decoder d(bytes, bytes + length); @@ -721,18 +733,20 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t if (!DecodeDeclarationSection(cx, d, init.get())) return false; - if (!DecodeImportSection(cx, d, init.get(), importNames)) + *dynamicLink = MakeUnique(); + if (!*dynamicLink) + return false; + + if (!DecodeImportSection(cx, d, init.get(), dynamicLink->get())) return false; ModuleGenerator mg(cx); - if (!mg.init(Move(init))) + if (!mg.init(Move(init), Move(filename))) return false; - if (!DecodeExportsSection(cx, d, mg, exportMap)) + if (!DecodeExportsSection(cx, d, mg, dynamicLink->get())) return false; - HeapUsage heapUsage = HeapUsage::None; - if (!DecodeCodeSection(cx, d, mg)) return false; @@ -747,9 +761,9 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t return Fail(cx, d, "failed to consume all bytes of module"); UniqueModuleData module; - UniqueStaticLinkData link; + UniqueStaticLinkData staticLink; SlowFunctionVector slowFuncs(cx); - if (!mg.finish(heapUsage, Move(filename), Move(funcNames), &module, &link, &slowFuncs)) + if (!mg.finish(Move(funcNames), &module, &staticLink, &slowFuncs)) return false; moduleObj.set(WasmModuleObject::create(cx)); @@ -759,7 +773,7 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t if (!moduleObj->init(cx->new_(Move(module)))) return false; - return moduleObj->module().staticallyLink(cx, *link); + return moduleObj->module().staticallyLink(cx, *staticLink); } /*****************************************************************************/ @@ -887,10 +901,9 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp) if (!DescribeScriptedCaller(cx, &filename)) return false; - ImportNameVector importNames; - ExportMap exportMap; + UniqueDynamicLinkData link; Rooted moduleObj(cx); - if (!DecodeModule(cx, Move(filename), bytes, length, &importNames, &exportMap, &moduleObj)) { + if (!DecodeModule(cx, Move(filename), bytes, length, &link, &moduleObj)) { if (!cx->isExceptionPending()) ReportOutOfMemory(cx); return false; @@ -903,11 +916,11 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp) return Fail(cx, "Heap not implemented yet"); Rooted imports(cx, FunctionVector(cx)); - if (!ImportFunctions(cx, importObj, importNames, &imports)) + if (!ImportFunctions(cx, importObj, link->importNames, &imports)) return false; RootedObject exportObj(cx); - if (!module.dynamicallyLink(cx, moduleObj, heap, imports, exportMap, &exportObj)) + if (!module.dynamicallyLink(cx, moduleObj, heap, imports, link->exportMap, &exportObj)) return false; args.rval().setObject(*exportObj); diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index f34170d39b0..1c9e623cec1 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -103,7 +103,7 @@ ParallelCompilationEnabled(ExclusiveContext* cx) } bool -ModuleGenerator::init(UniqueModuleGeneratorData shared, ModuleKind kind) +ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename, ModuleKind kind) { if (!funcIndexToExport_.init()) return false; @@ -115,6 +115,8 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, ModuleKind kind) module_->globalBytes = InitialGlobalDataBytes; module_->compileArgs = CompileArgs(cx_); module_->kind = kind; + module_->heapUsage = HeapUsage::None; + module_->filename = Move(filename); link_ = MakeUnique(); if (!link_) @@ -268,6 +270,13 @@ ModuleGenerator::allocateGlobalVar(ValType type, bool isConst, uint32_t* index) return shared_->globals.append(AsmJSGlobalVariable(ToExprType(type), offset, isConst)); } +void +ModuleGenerator::initHeapUsage(HeapUsage heapUsage) +{ + MOZ_ASSERT(module_->heapUsage == HeapUsage::None); + module_->heapUsage = heapUsage; +} + void ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig) { @@ -610,9 +619,7 @@ ModuleGenerator::defineOutOfBoundsStub(Offsets offsets) } bool -ModuleGenerator::finish(HeapUsage heapUsage, - CacheableChars filename, - CacheableCharsVector&& prettyFuncNames, +ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames, UniqueModuleData* module, UniqueStaticLinkData* linkData, SlowFunctionVector* slowFuncs) @@ -620,11 +627,9 @@ ModuleGenerator::finish(HeapUsage heapUsage, MOZ_ASSERT(!activeFunc_); MOZ_ASSERT(finishedFuncs_); - module_->heapUsage = heapUsage; - module_->filename = Move(filename); module_->prettyFuncNames = Move(prettyFuncNames); - if (!GenerateStubs(*this, UsesHeap(heapUsage))) + if (!GenerateStubs(*this, UsesHeap(module_->heapUsage))) return false; masm_.finish(); diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index dd9cb824fa1..694b3401424 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -166,7 +166,7 @@ class MOZ_STACK_CLASS ModuleGenerator explicit ModuleGenerator(ExclusiveContext* cx); ~ModuleGenerator(); - bool init(UniqueModuleGeneratorData shared, ModuleKind = ModuleKind::Wasm); + bool init(UniqueModuleGeneratorData shared, UniqueChars filename, ModuleKind = ModuleKind::Wasm); bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; } CompileArgs args() const { return module_->compileArgs; } @@ -176,6 +176,9 @@ class MOZ_STACK_CLASS ModuleGenerator bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index); const AsmJSGlobalVariable& globalVar(unsigned index) const { return shared_->globals[index]; } + // Heap usage: + void initHeapUsage(HeapUsage heapUsage); + // Signatures: void initSig(uint32_t sigIndex, Sig&& sig); uint32_t numSigs() const { return numSigs_; } @@ -220,9 +223,7 @@ class MOZ_STACK_CLASS ModuleGenerator // Return a ModuleData object which may be used to construct a Module, the // StaticLinkData required to call Module::staticallyLink, and the list of // functions that took a long time to compile. - bool finish(HeapUsage heapUsage, - CacheableChars filename, - CacheableCharsVector&& prettyFuncNames, + bool finish(CacheableCharsVector&& prettyFuncNames, UniqueModuleData* module, UniqueStaticLinkData* staticLinkData, SlowFunctionVector* slowFuncs); From 9cda019a642dea9d4222b4534912adef0aa7ddef Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 1 Feb 2016 10:16:26 -0600 Subject: [PATCH 15/91] Bug 1244403 - Baldr: put import section first (r=bbouvier) --- js/src/asmjs/Wasm.cpp | 19 ++++++++++++++----- js/src/asmjs/WasmBinary.h | 2 +- js/src/asmjs/WasmText.cpp | 4 ++-- js/src/jit-test/tests/wasm/binary.js | 7 +++++++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index a75c1d7a9ae..5d90fbacf48 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -700,10 +700,19 @@ DecodeCodeSection(JSContext* cx, Decoder& d, ModuleGenerator& mg) static bool DecodeUnknownSection(JSContext* cx, Decoder& d) { - const char* unused; - if (!d.readCString(&unused)) + const char* sectionName; + if (!d.readCString(§ionName)) return Fail(cx, d, "failed to read section name"); + if (!strcmp(sectionName, SigSection) || + !strcmp(sectionName, ImportSection) || + !strcmp(sectionName, DeclSection) || + !strcmp(sectionName, ExportSection) || + !strcmp(sectionName, CodeSection)) + { + return Fail(cx, d, "known section out of order"); + } + if (!d.skipSection()) return Fail(cx, d, "unable to skip unknown section"); @@ -730,9 +739,6 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t if (!DecodeSignatureSection(cx, d, init.get())) return false; - if (!DecodeDeclarationSection(cx, d, init.get())) - return false; - *dynamicLink = MakeUnique(); if (!*dynamicLink) return false; @@ -740,6 +746,9 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t if (!DecodeImportSection(cx, d, init.get(), dynamicLink->get())) return false; + if (!DecodeDeclarationSection(cx, d, init.get())) + return false; + ModuleGenerator mg(cx); if (!mg.init(Move(init), Move(filename))) return false; diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index cc4a4b97e73..2b70effc5e1 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -41,8 +41,8 @@ static const uint32_t EncodingVersion = -1; // experimental // Module section names: static const char SigSection[] = "sig"; -static const char DeclSection[] = "decl"; static const char ImportSection[] = "import"; +static const char DeclSection[] = "decl"; static const char ExportSection[] = "export"; static const char CodeSection[] = "code"; static const char EndSection[] = ""; diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 4b55c48c664..7c75a7c9cd3 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -1466,10 +1466,10 @@ AstToBinary(WasmAstModule& module) if (!EncodeSignatureSection(e, module)) return nullptr; - if (!EncodeDeclarationSection(e, module)) + if (!EncodeImportSection(e, module)) return nullptr; - if (!EncodeImportSection(e, module)) + if (!EncodeDeclarationSection(e, module)) return nullptr; if (!EncodeExportSection(e, module)) diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index 1192b34ffbb..53acda0f2c9 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -167,3 +167,10 @@ assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([importSection([{sigI assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([{sigIndex:1, module:"a", func:"b"}])]))), TypeError, /signature index out of range/); wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([])]))); wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([{sigIndex:0, module:"a", func:""}])])), {a:()=>{}}); + +Math.sin(); +wasmEval(toBuf(moduleWithSections([ + trivialSigSection, + importSection([{sigIndex:0, module:"a", func:""}]), + trivialDeclSection, + trivialCodeSection])), {a:()=>{}}); From 0a9a8759b0500afbb2cbd228b5bcd9ee754e9184 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Fri, 29 Jan 2016 10:44:57 -0600 Subject: [PATCH 16/91] Bug 1238541 - Don't die in SharedSurface_EGLImage::ProducerReadReleaseImpl() if there is an existing fence r=jgilbert --- gfx/gl/SharedSurfaceEGL.cpp | 9 +++++++++ gfx/gl/SharedSurfaceEGL.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp index 45ecf6a5c33..e6708dc4060 100644 --- a/gfx/gl/SharedSurfaceEGL.cpp +++ b/gfx/gl/SharedSurfaceEGL.cpp @@ -140,6 +140,15 @@ SharedSurface_EGLImage::ProducerReleaseImpl() mGL->fFinish(); } +void +SharedSurface_EGLImage::ProducerReadAcquireImpl() +{ + // Wait on the fence, because presumably we're going to want to read this surface + if (mSync) { + mEGL->fClientWaitSync(Display(), mSync, 0, LOCAL_EGL_FOREVER); + } +} + EGLDisplay SharedSurface_EGLImage::Display() const { diff --git a/gfx/gl/SharedSurfaceEGL.h b/gfx/gl/SharedSurfaceEGL.h index 53f4bb084ab..4a60885d182 100644 --- a/gfx/gl/SharedSurfaceEGL.h +++ b/gfx/gl/SharedSurfaceEGL.h @@ -71,6 +71,9 @@ public: virtual void ProducerAcquireImpl() override {} virtual void ProducerReleaseImpl() override; + virtual void ProducerReadAcquireImpl() override; + virtual void ProducerReadReleaseImpl() override {}; + virtual GLuint ProdTexture() override { return mProdTex; } From 4d5abb28bfbabda662a408d0c255fb7a701d48b8 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Wed, 27 Jan 2016 09:54:53 -0800 Subject: [PATCH 17/91] Bug 1243888 - Derive RootKind automatically from TraceKind; r=sfink --- js/public/RootingAPI.h | 36 +++++--------------- js/public/TraceKind.h | 68 ++++++++++++++++++++++++++++++++++++-- js/src/gc/RootMarking.cpp | 37 +++++++++------------ js/src/jit/IonCode.h | 2 +- js/src/jscntxt.cpp | 5 +-- js/src/jsgc.cpp | 2 +- js/src/jsobj.h | 2 +- js/src/jspubtd.h | 69 +++++++++------------------------------ js/src/jsscript.h | 4 +-- js/src/vm/ObjectGroup.h | 2 +- js/src/vm/Shape.h | 7 ++-- js/src/vm/String.h | 2 +- js/src/vm/Symbol.h | 2 +- 13 files changed, 117 insertions(+), 121 deletions(-) diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index 696454e11d1..9bf511938d0 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -509,23 +509,6 @@ class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase namespace js { -/** - * By default, things should use the inheritance hierarchy to find their - * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that - * Rooted may be used without the class definition being available. - */ -template -struct RootKind -{ - static ThingRootKind rootKind() { return T::rootKind(); } -}; - -template -struct RootKind -{ - static ThingRootKind rootKind() { return T::rootKind(); } -}; - template struct BarrierMethods { @@ -607,8 +590,6 @@ namespace JS { // |static void trace(T*, JSTracer*)| class Traceable { - public: - static js::ThingRootKind rootKind() { return js::THING_ROOT_TRACEABLE; } }; } /* namespace JS */ @@ -694,8 +675,7 @@ class MOZ_RAII Rooted : public js::RootedBase /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */ void registerWithRootLists(js::RootLists& roots) { - js::ThingRootKind kind = js::RootKind::rootKind(); - this->stack = &roots.stackRoots_[kind]; + this->stack = &roots.stackRoots_[JS::MapTypeToRootKind::kind]; this->prev = *stack; *stack = reinterpret_cast*>(this); } @@ -1012,16 +992,16 @@ class PersistentRooted : public js::PersistentRootedBase, void registerWithRootLists(js::RootLists& roots) { MOZ_ASSERT(!initialized()); - js::ThingRootKind kind = js::RootKind::rootKind(); + JS::RootKind kind = JS::MapTypeToRootKind::kind; roots.heapRoots_[kind].insertBack(reinterpret_cast*>(this)); // Until marking and destruction support the full set, we assert that // we don't try to add any unsupported types. - MOZ_ASSERT(kind == js::THING_ROOT_OBJECT || - kind == js::THING_ROOT_SCRIPT || - kind == js::THING_ROOT_STRING || - kind == js::THING_ROOT_ID || - kind == js::THING_ROOT_VALUE || - kind == js::THING_ROOT_TRACEABLE); + MOZ_ASSERT(kind == JS::RootKind::Object || + kind == JS::RootKind::Script || + kind == JS::RootKind::String || + kind == JS::RootKind::Id || + kind == JS::RootKind::Value || + kind == JS::RootKind::Traceable); } public: diff --git a/js/public/TraceKind.h b/js/public/TraceKind.h index c7e1a40dac1..4a5f809f820 100644 --- a/js/public/TraceKind.h +++ b/js/public/TraceKind.h @@ -60,6 +60,16 @@ static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mas static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set"); static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set"); +// When this header is imported inside SpiderMonkey, the class definitions are +// available and we can query those definitions to find the correct kind +// directly from the class hierarchy. +template +struct MapTypeToTraceKind { + static const JS::TraceKind kind = T::TraceKind; +}; + +// When this header is used outside SpiderMonkey, the class definitions are not +// available, so the following table containing all public GC types is used. #define JS_FOR_EACH_TRACEKIND(D) \ /* PrettyName TypeName AddToCCKind */ \ D(BaseShape, js::BaseShape, true) \ @@ -72,8 +82,7 @@ static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "ma D(String, JSString, false) \ D(Symbol, JS::Symbol, false) -// Map from base trace type to the trace kind. -template struct MapTypeToTraceKind {}; +// Map from all public types to their trace kind. #define JS_EXPAND_DEF(name, type, _) \ template <> struct MapTypeToTraceKind { \ static const JS::TraceKind kind = JS::TraceKind::name; \ @@ -81,6 +90,61 @@ template struct MapTypeToTraceKind {}; JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF); #undef JS_EXPAND_DEF +// RootKind is closely related to TraceKind. Whereas TraceKind's indices are +// laid out for convenient embedding as a pointer tag, the indicies of RootKind +// are designed for use as array keys via EnumeratedArray. +enum class RootKind : int8_t +{ + // These map 1:1 with trace kinds. + BaseShape = 0, + JitCode, + LazyScript, + Object, + ObjectGroup, + Script, + Shape, + String, + Symbol, + + // These tagged pointers are special-cased for performance. + Id, + Value, + + // Everything else. + Traceable, + + Limit +}; + +// Most RootKind correspond directly to a trace kind. +template struct MapTraceKindToRootKind {}; +#define JS_EXPAND_DEF(name, _0, _1) \ + template <> struct MapTraceKindToRootKind { \ + static const JS::RootKind kind = JS::RootKind::name; \ + }; +JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF) +#undef JS_EXPAND_DEF + +// Specify the RootKind for all types. Value and jsid map to special cases; +// pointer types we can derive directly from the TraceKind; everything else +// should go in the Traceable list and use GCPolicy::trace for tracing. +template +struct MapTypeToRootKind { + static const JS::RootKind kind = JS::RootKind::Traceable; +}; +template +struct MapTypeToRootKind { + static const JS::RootKind kind = \ + JS::MapTraceKindToRootKind::kind>::kind; +}; +template <> struct MapTypeToRootKind { + static const JS::RootKind kind = JS::RootKind::Value; +}; +template <> struct MapTypeToRootKind { + static const JS::RootKind kind = JS::RootKind::Id; +}; +template <> struct MapTypeToRootKind : public MapTypeToRootKind {}; + // Fortunately, few places in the system need to deal with fully abstract // cells. In those places that do, we generally want to move to a layout // templated function as soon as possible. This template wraps the upcast diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 926fc851415..a7efbb8c8f5 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -42,43 +42,36 @@ typedef RootedValueMap::Enum RootEnum; template using TraceFunction = void (*)(JSTracer* trc, T* ref, const char* name); -template TraceFn = TraceNullableRoot, class Source> +template TraceFn = TraceNullableRoot> static inline void -MarkExactStackRootList(JSTracer* trc, Source* s, const char* name) +MarkExactStackRootList(JSTracer* trc, JS::Rooted* rooter, const char* name) { - Rooted* rooter = s->roots.template gcRooters(); while (rooter) { - T* addr = rooter->address(); + T* addr = reinterpret_cast*>(rooter)->address(); TraceFn(trc, addr, name); rooter = rooter->previous(); } } -template -static void -MarkExactStackRootsAcrossTypes(T context, JSTracer* trc) +void +js::RootLists::traceStackRoots(JSTracer* trc) { - MarkExactStackRootList(trc, context, "exact-object"); - MarkExactStackRootList(trc, context, "exact-shape"); - MarkExactStackRootList(trc, context, "exact-baseshape"); - MarkExactStackRootList(trc, context, "exact-objectgroup"); - MarkExactStackRootList(trc, context, "exact-string"); - MarkExactStackRootList(trc, context, "exact-symbol"); - MarkExactStackRootList(trc, context, "exact-jitcode"); - MarkExactStackRootList(trc, context, "exact-script"); - MarkExactStackRootList(trc, context, "exact-lazy-script"); - MarkExactStackRootList(trc, context, "exact-id"); - MarkExactStackRootList(trc, context, "exact-value"); +#define MARK_ROOTS(name, type, _) \ + MarkExactStackRootList(trc, stackRoots_[JS::RootKind::name], "exact-" #name); +JS_FOR_EACH_TRACEKIND(MARK_ROOTS) +#undef MARK_ROOTS + MarkExactStackRootList(trc, stackRoots_[JS::RootKind::Id], "exact-id"); + MarkExactStackRootList(trc, stackRoots_[JS::RootKind::Value], "exact-value"); MarkExactStackRootList::TraceWrapped>( - trc, context, "Traceable"); + trc, stackRoots_[JS::RootKind::Traceable], "Traceable"); } static void MarkExactStackRoots(JSRuntime* rt, JSTracer* trc) { for (ContextIter cx(rt); !cx.done(); cx.next()) - MarkExactStackRootsAcrossTypes(cx.get(), trc); - MarkExactStackRootsAcrossTypes(&rt->mainThread, trc); + cx->roots.traceStackRoots(trc); + rt->mainThread.roots.traceStackRoots(trc); } inline void @@ -246,7 +239,7 @@ js::gc::MarkPersistentRootedChainsInLists(RootLists& roots, JSTracer* trc) PersistentRootedMarker::markChain< js::DispatchWrapper::TraceWrapped>(trc, reinterpret_cast>&>( - roots.heapRoots_[THING_ROOT_TRACEABLE]), + roots.heapRoots_[JS::RootKind::Traceable]), "PersistentRooted"); } diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h index ba9a95968b7..3ec01edfd68 100644 --- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -160,7 +160,7 @@ class JitCode : public gc::TenuredCell ExecutablePool* pool, CodeKind kind); public: - static inline ThingRootKind rootKind() { return THING_ROOT_JIT_CODE; } + static const JS::TraceKind TraceKind = JS::TraceKind::JitCode; }; class SnapshotWriter; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index d757e69d84e..2486688b517 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -186,8 +186,9 @@ js::DestroyContext(JSContext* cx, DestroyContextMode mode) void RootLists::checkNoGCRooters() { #ifdef DEBUG - for (int i = 0; i < THING_ROOT_LIMIT; ++i) - MOZ_ASSERT(stackRoots_[i] == nullptr); + for (auto const& stackRootPtr : stackRoots_) { + MOZ_ASSERT(stackRootPtr == nullptr); + } #endif } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 26d6491ffed..cf8db835936 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1389,7 +1389,7 @@ js::gc::FinishPersistentRootedChains(RootLists& roots) FinishPersistentRootedChain(roots.getPersistentRootedList()); FinishPersistentRootedChain(roots.getPersistentRootedList()); FinishPersistentRootedChain(roots.getPersistentRootedList()); - FinishPersistentRootedChain(roots.heapRoots_[THING_ROOT_TRACEABLE]); + FinishPersistentRootedChain(roots.heapRoots_[JS::RootKind::Traceable]); } void diff --git a/js/src/jsobj.h b/js/src/jsobj.h index f53e5edcdfd..c89ef552c33 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -292,7 +292,7 @@ class JSObject : public js::gc::Cell void fixupAfterMovingGC(); - static js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; } + static const JS::TraceKind TraceKind = JS::TraceKind::Object; static const size_t MaxTagBits = 3; static bool isNullLike(const JSObject* obj) { return uintptr_t(obj) < (1 << MaxTagBits); } diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 82c38206e35..a76908e1cce 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -12,12 +12,14 @@ */ #include "mozilla/Assertions.h" +#include "mozilla/EnumeratedArray.h" #include "mozilla/LinkedList.h" #include "mozilla/PodOperations.h" #include "jsprototypes.h" #include "jstypes.h" +#include "js/TraceKind.h" #include "js/TypeDecls.h" #if defined(JS_GC_ZEAL) || defined(DEBUG) @@ -262,52 +264,12 @@ enum StackKind StackKindCount }; -enum ThingRootKind -{ - THING_ROOT_OBJECT, - THING_ROOT_SHAPE, - THING_ROOT_BASE_SHAPE, - THING_ROOT_OBJECT_GROUP, - THING_ROOT_STRING, - THING_ROOT_SYMBOL, - THING_ROOT_JIT_CODE, - THING_ROOT_SCRIPT, - THING_ROOT_LAZY_SCRIPT, - THING_ROOT_ID, - THING_ROOT_VALUE, - THING_ROOT_TRACEABLE, - THING_ROOT_LIMIT -}; - -template -struct RootKind; - -/* - * Specifically mark the ThingRootKind of externally visible types, so that - * JSAPI users may use JSRooted... types without having the class definition - * available. - */ -template -struct SpecificRootKind -{ - static ThingRootKind rootKind() { return Kind; } -}; - -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; -template <> struct RootKind : SpecificRootKind {}; - // Abstracts JS rooting mechanisms so they can be shared between the JSContext // and JSRuntime. class RootLists { // Stack GC roots for stack-allocated GC heap pointers. - JS::Rooted* stackRoots_[THING_ROOT_LIMIT]; + mozilla::EnumeratedArray*> stackRoots_; template friend class JS::Rooted; // Stack GC roots for stack-allocated AutoFooRooter classes. @@ -316,14 +278,12 @@ class RootLists public: RootLists() : autoGCRooters_(nullptr) { - mozilla::PodArrayZero(stackRoots_); + for (auto& stackRootPtr : stackRoots_) { + stackRootPtr = nullptr; + } } - template - inline JS::Rooted* gcRooters() { - js::ThingRootKind kind = RootKind::rootKind(); - return reinterpret_cast*>(stackRoots_[kind]); - } + void traceStackRoots(JSTracer* trc); void checkNoGCRooters(); @@ -334,7 +294,8 @@ class RootLists friend void js::gc::MarkPersistentRootedChainsInLists(RootLists&, JSTracer*); friend void js::gc::FinishPersistentRootedChains(RootLists&); - mozilla::LinkedList> heapRoots_[THING_ROOT_LIMIT]; + mozilla::EnumeratedArray>> heapRoots_; /* Specializations of this return references to the appropriate list. */ template @@ -345,42 +306,42 @@ template<> inline mozilla::LinkedList& RootLists::getPersistentRootedList() { return reinterpret_cast>&>( - heapRoots_[THING_ROOT_OBJECT]); + heapRoots_[JS::RootKind::Object]); } template<> inline mozilla::LinkedList& RootLists::getPersistentRootedList() { return reinterpret_cast>&>( - heapRoots_[THING_ROOT_OBJECT]); + heapRoots_[JS::RootKind::Object]); } template<> inline mozilla::LinkedList& RootLists::getPersistentRootedList() { return reinterpret_cast>&>( - heapRoots_[THING_ROOT_ID]); + heapRoots_[JS::RootKind::Id]); } template<> inline mozilla::LinkedList& RootLists::getPersistentRootedList() { return reinterpret_cast>&>( - heapRoots_[THING_ROOT_SCRIPT]); + heapRoots_[JS::RootKind::Script]); } template<> inline mozilla::LinkedList& RootLists::getPersistentRootedList() { return reinterpret_cast>&>( - heapRoots_[THING_ROOT_STRING]); + heapRoots_[JS::RootKind::String]); } template<> inline mozilla::LinkedList& RootLists::getPersistentRootedList() { return reinterpret_cast>&>( - heapRoots_[THING_ROOT_VALUE]); + heapRoots_[JS::RootKind::Value]); } struct ContextFriendFields diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 68caa28cfb7..201177462a3 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1947,7 +1947,7 @@ class JSScript : public js::gc::TenuredCell void finalize(js::FreeOp* fop); void fixupAfterMovingGC() {} - static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; } + static const JS::TraceKind TraceKind = JS::TraceKind::Script; void traceChildren(JSTracer* trc); @@ -2389,7 +2389,7 @@ class LazyScript : public gc::TenuredCell void finalize(js::FreeOp* fop); void fixupAfterMovingGC() {} - static inline js::ThingRootKind rootKind() { return js::THING_ROOT_LAZY_SCRIPT; } + static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript; size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index c9a9e913e17..5fefd785b2a 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -421,7 +421,7 @@ class ObjectGroup : public gc::TenuredCell void finalize(FreeOp* fop); void fixupAfterMovingGC() {} - static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; } + static const JS::TraceKind TraceKind = JS::TraceKind::ObjectGroup; static inline uint32_t offsetOfClasp() { return offsetof(ObjectGroup, clasp_); diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 8039df65f62..9df058447f0 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -446,7 +446,7 @@ class BaseShape : public gc::TenuredCell /* For JIT usage */ static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); } - static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; } + static const JS::TraceKind TraceKind = JS::TraceKind::BaseShape; void traceChildren(JSTracer* trc); @@ -947,7 +947,7 @@ class Shape : public gc::TenuredCell void finalize(FreeOp* fop); void removeChild(Shape* child); - static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; } + static const JS::TraceKind TraceKind = JS::TraceKind::Shape; void traceChildren(JSTracer* trc); @@ -1406,9 +1406,6 @@ Shape::matches(const StackShape& other) const other.rawGetter, other.rawSetter); } -template<> struct RootKind : SpecificRootKind {}; -template<> struct RootKind : SpecificRootKind {}; - // Property lookup hooks on objects are required to return a non-nullptr shape // to signify that the property has been found. For cases where the property is // not actually represented by a Shape, use a dummy value. This includes all diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 4433002fbd9..710a516e739 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -492,7 +492,7 @@ class JSString : public js::gc::TenuredCell return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte); } - static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; } + static const JS::TraceKind TraceKind = JS::TraceKind::String; #ifdef DEBUG void dump(); diff --git a/js/src/vm/Symbol.h b/js/src/vm/Symbol.h index c201ed127c5..10e27be8f81 100644 --- a/js/src/vm/Symbol.h +++ b/js/src/vm/Symbol.h @@ -56,7 +56,7 @@ class Symbol : public js::gc::TenuredCell bool isWellKnownSymbol() const { return uint32_t(code_) < WellKnownSymbolLimit; } - static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SYMBOL; } + static const JS::TraceKind TraceKind = JS::TraceKind::Symbol; inline void traceChildren(JSTracer* trc) { if (description_) js::TraceManuallyBarrieredEdge(trc, &description_, "description"); From f03395c04bc9e78a3a94d470b322a11e453ea14d Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Mon, 1 Feb 2016 17:55:09 +0100 Subject: [PATCH 18/91] Bug 1244454 - Fixed skia compilation on mingw. r=lsalzman --- gfx/skia/skia/include/core/SkPostConfig.h | 2 +- gfx/skia/skia/include/private/SkTLogic.h | 7 ++----- gfx/skia/skia/src/ports/SkOSLibrary_win.cpp | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/gfx/skia/skia/include/core/SkPostConfig.h b/gfx/skia/skia/include/core/SkPostConfig.h index d23eddf2e62..717230ea06c 100644 --- a/gfx/skia/skia/include/core/SkPostConfig.h +++ b/gfx/skia/skia/include/core/SkPostConfig.h @@ -300,7 +300,7 @@ # endif #endif -#if defined(SK_BUILD_FOR_WIN) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 +#if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 #define SK_VECTORCALL __vectorcall #elif defined(SK_CPU_ARM32) #define SK_VECTORCALL __attribute__((pcs("aapcs-vfp"))) diff --git a/gfx/skia/skia/include/private/SkTLogic.h b/gfx/skia/skia/include/private/SkTLogic.h index 9c21db67568..604d9aa59da 100644 --- a/gfx/skia/skia/include/private/SkTLogic.h +++ b/gfx/skia/skia/include/private/SkTLogic.h @@ -119,15 +119,12 @@ template struct is_function : false_type { }; #if !defined(SK_BUILD_FOR_WIN) template struct is_function : true_type {}; #else -#if defined(_M_IX86) template struct is_function : true_type {}; +#if defined(_M_IX86) template struct is_function : true_type {}; template struct is_function : true_type {}; -#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 -template struct is_function : true_type {}; #endif -#else -template struct is_function : true_type {}; +#if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 template struct is_function : true_type {}; #endif #endif diff --git a/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp b/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp index a1249f893b0..9defb7ef15c 100644 --- a/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp +++ b/gfx/skia/skia/src/ports/SkOSLibrary_win.cpp @@ -16,7 +16,7 @@ void* DynamicLoadLibrary(const char* libraryName) { } void* GetProcedureAddress(void* library, const char* functionName) { - return ::GetProcAddress((HMODULE)library, functionName); + return reinterpret_cast(::GetProcAddress((HMODULE)library, functionName)); } #endif//defined(SK_BUILD_FOR_WIN32) From f8086c076c39444625581f7a65d044a73f6f8fde Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 26 Jan 2016 18:38:03 +0100 Subject: [PATCH 19/91] Bug 1027734 - Convert mozPay to WebIDL. r=bzbarsky. --- dom/base/Navigator.cpp | 35 +++++ dom/base/Navigator.h | 7 + dom/payment/Payment.js | 145 ++++++++---------- dom/payment/Payment.manifest | 5 +- dom/payment/interfaces/moz.build | 2 +- ...idl => nsIPaymentContentHelperService.idl} | 7 +- .../mochitest/MockPaymentsUIChromeScript.js | 91 +++++++++++ dom/payment/tests/mochitest/mochitest.ini | 5 +- .../mochitest/test_mozpay_callbacks.html | 10 +- dom/webidl/Navigator.webidl | 11 ++ testing/marionette/jar.mn | 1 - .../content/MockPaymentsUIGlue.jsm | 110 ------------- .../specialpowers/content/specialpowersAPI.js | 5 - testing/specialpowers/jar.mn | 1 - 14 files changed, 225 insertions(+), 210 deletions(-) rename dom/payment/interfaces/{nsINavigatorPayment.idl => nsIPaymentContentHelperService.idl} (72%) create mode 100644 dom/payment/tests/mochitest/MockPaymentsUIChromeScript.js delete mode 100644 testing/specialpowers/content/MockPaymentsUIGlue.jsm diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index d63f8efe654..3065657a38c 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -128,6 +128,11 @@ #include #endif +#ifdef MOZ_PAY +#include "nsIPaymentContentHelperService.h" +#include "mozilla/dom/DOMRequest.h" +#endif + namespace mozilla { namespace dom { @@ -2672,6 +2677,36 @@ Navigator::MozE10sEnabled() return true; } +#ifdef MOZ_PAY +already_AddRefed +Navigator::MozPay(JSContext* aCx, + JS::Handle aJwts, + ErrorResult& aRv) +{ + if (!mWindow || !mWindow->GetDocShell()) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + nsresult rv; + nsCOMPtr service = + do_GetService("@mozilla.org/payment/content-helper-service;1", &rv); + if (!service) { + aRv.Throw(rv); + return nullptr; + } + + RefPtr request; + rv = service->Pay(mWindow, aJwts, getter_AddRefs(request)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + return request.forget().downcast(); +} +#endif // MOZ_PAY + /* static */ already_AddRefed Navigator::GetWindowFromGlobal(JSObject* aGlobal) diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 4ed32d5038e..17b40535a2a 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -42,6 +42,7 @@ class WakeLock; class ArrayBufferViewOrBlobOrStringOrFormData; struct MobileIdOptions; class ServiceWorkerContainer; +class DOMRequest; } // namespace dom } // namespace mozilla @@ -315,6 +316,12 @@ public: bool MozE10sEnabled(); +#ifdef MOZ_PAY + already_AddRefed MozPay(JSContext* aCx, + JS::Handle aJwts, + ErrorResult& aRv); +#endif // MOZ_PAY + static void GetAcceptLanguages(nsTArray& aLanguages); // WebIDL helper methods diff --git a/dom/payment/Payment.js b/dom/payment/Payment.js index e17565fc44e..95e4dc074e7 100644 --- a/dom/payment/Payment.js +++ b/dom/payment/Payment.js @@ -10,9 +10,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); -const PAYMENTCONTENTHELPER_CID = - Components.ID("{a920adc0-c36e-4fd0-8de0-aac1ac6ebbd0}"); - const PAYMENT_IPC_MSG_NAMES = ["Payment:Success", "Payment:Failed"]; @@ -22,39 +19,82 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", "nsIMessageSender"); -function PaymentContentHelper() { +var _debug; +try { + _debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL + && Services.prefs.getBoolPref(PREF_DEBUG); +} catch(e) { + _debug = false; +} + +function LOG(s) { + if (!_debug) { + return; + } + dump("-*- PaymentContentHelper: " + s + "\n"); +} + +function PaymentContentHelper(aWindow) { + this.initDOMRequestHelper(aWindow, PAYMENT_IPC_MSG_NAMES); }; PaymentContentHelper.prototype = { __proto__: DOMRequestIpcHelper.prototype, - QueryInterface: XPCOMUtils.generateQI([Ci.nsINavigatorPayment, - Ci.nsIDOMGlobalPropertyInitializer, - Ci.nsISupportsWeakReference, - Ci.nsIObserver]), - classID: PAYMENTCONTENTHELPER_CID, - classInfo: XPCOMUtils.generateCI({ - classID: PAYMENTCONTENTHELPER_CID, - contractID: "@mozilla.org/payment/content-helper;1", - classDescription: "Payment Content Helper", - flags: Ci.nsIClassInfo.DOM_OBJECT, - interfaces: [Ci.nsINavigatorPayment] - }), + receiveMessage: function receiveMessage(aMessage) { + let name = aMessage.name; + let msg = aMessage.json; + if (_debug) { + LOG("Received message '" + name + "': " + JSON.stringify(msg)); + } + let requestId = msg.requestId; + let request = this.takeRequest(requestId); + if (!request) { + return; + } + switch (name) { + case "Payment:Success": + Services.DOMRequest.fireSuccess(request, msg.result); + break; + case "Payment:Failed": + Services.DOMRequest.fireError(request, msg.errorMsg); + break; + } + }, +}; + +function PaymentContentHelperService() { +}; + +PaymentContentHelperService.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentContentHelperService]), + classID: Components.ID("{80035846-6732-4fcc-961b-f336b65218f4}"), + contractID: "@mozilla.org/payment/content-helper-service;1", + + _xpcom_factory: XPCOMUtils.generateSingletonFactory(PaymentContentHelperService), + + // keys are windows and values are PaymentContentHelpers + helpers: new WeakMap(), // nsINavigatorPayment + pay: function pay(aWindow, aJwts) { + let requestHelper = this.helpers.get(aWindow); + if (!requestHelper) { + requestHelper = new PaymentContentHelper(aWindow); + this.helpers.set(aWindow, requestHelper); + } + let request = requestHelper.createRequest(); + let requestId = requestHelper.getRequestId(request); - pay: function pay(aJwts) { - let request = this.createRequest(); - let requestId = this.getRequestId(request); - - let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor) + let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell); if (!docShell.isActive) { - if (this._debug) { - this.LOG("The caller application is a background app. No request " + - "will be sent"); + if (_debug) { + LOG("The caller application is a background app. No request will be " + + "sent"); } + let runnable = { run: function run() { Services.DOMRequest.fireError(request, "BACKGROUND_APP"); @@ -75,61 +115,6 @@ PaymentContentHelper.prototype = { }); return request; }, - - // nsIDOMGlobalPropertyInitializer - - init: function(aWindow) { - try { - if (!Services.prefs.getBoolPref("dom.mozPay.enabled")) { - return null; - } - } catch (e) { - return null; - } - - this._window = aWindow; - this.initDOMRequestHelper(aWindow, PAYMENT_IPC_MSG_NAMES); - - try { - this._debug = - Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL - && Services.prefs.getBoolPref(PREF_DEBUG); - } catch(e) { - this._debug = false; - } - - return Cu.exportFunction(this.pay.bind(this), aWindow); - }, - - // nsIFrameMessageListener - - receiveMessage: function receiveMessage(aMessage) { - let name = aMessage.name; - let msg = aMessage.json; - if (this._debug) { - this.LOG("Received message '" + name + "': " + JSON.stringify(msg)); - } - let requestId = msg.requestId; - let request = this.takeRequest(requestId); - if (!request) { - return; - } - switch (name) { - case "Payment:Success": - Services.DOMRequest.fireSuccess(request, msg.result); - break; - case "Payment:Failed": - Services.DOMRequest.fireError(request, msg.errorMsg); - break; - } - }, - - LOG: function LOG(s) { - if (!this._debug) { - return; - } - dump("-*- PaymentContentHelper: " + s + "\n"); - } }; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentContentHelper]); +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentContentHelperService]); diff --git a/dom/payment/Payment.manifest b/dom/payment/Payment.manifest index 90fc3409004..42d8ccd5fd3 100644 --- a/dom/payment/Payment.manifest +++ b/dom/payment/Payment.manifest @@ -1,6 +1,5 @@ -component {a920adc0-c36e-4fd0-8de0-aac1ac6ebbd0} Payment.js -contract @mozilla.org/payment/content-helper;1 {a920adc0-c36e-4fd0-8de0-aac1ac6ebbd0} -category JavaScript-navigator-property mozPay @mozilla.org/payment/content-helper;1 +component {80035846-6732-4fcc-961b-f336b65218f4} Payment.js +contract @mozilla.org/payment/content-helper-service;1 {80035846-6732-4fcc-961b-f336b65218f4} component {b8bce4e7-fbf0-4719-a634-b1bf9018657c} PaymentFlowInfo.js contract @mozilla.org/payment/flow-info;1 {b8bce4e7-fbf0-4719-a634-b1bf9018657c} diff --git a/dom/payment/interfaces/moz.build b/dom/payment/interfaces/moz.build index 7046187080e..3c199ed30db 100644 --- a/dom/payment/interfaces/moz.build +++ b/dom/payment/interfaces/moz.build @@ -5,7 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. XPIDL_SOURCES += [ - 'nsINavigatorPayment.idl', + 'nsIPaymentContentHelperService.idl', 'nsIPaymentFlowInfo.idl', 'nsIPaymentProviderStrategy.idl', 'nsIPaymentUIGlue.idl', diff --git a/dom/payment/interfaces/nsINavigatorPayment.idl b/dom/payment/interfaces/nsIPaymentContentHelperService.idl similarity index 72% rename from dom/payment/interfaces/nsINavigatorPayment.idl rename to dom/payment/interfaces/nsIPaymentContentHelperService.idl index 091cec982c1..a02bc55a581 100644 --- a/dom/payment/interfaces/nsINavigatorPayment.idl +++ b/dom/payment/interfaces/nsIPaymentContentHelperService.idl @@ -5,13 +5,14 @@ #include "domstubs.idl" interface nsIDOMDOMRequest; +interface mozIDOMWindow; -[scriptable, uuid(44fb7308-7d7b-4975-8a27-e01fe9623bdb)] -interface nsINavigatorPayment : nsISupports +[scriptable, uuid(80035846-6732-4fcc-961b-f336b65218f4)] +interface nsIPaymentContentHelperService : nsISupports { // The 'jwts' parameter can be either a single DOMString or an array of // DOMStrings. In both cases, it represents the base64url encoded and // digitally signed payment information. Each payment provider should // define its supported JWT format. - nsIDOMDOMRequest pay(in jsval jwts); + nsIDOMDOMRequest pay(in mozIDOMWindow window, in jsval jwts); }; diff --git a/dom/payment/tests/mochitest/MockPaymentsUIChromeScript.js b/dom/payment/tests/mochitest/MockPaymentsUIChromeScript.js new file mode 100644 index 00000000000..7d991784d2a --- /dev/null +++ b/dom/payment/tests/mochitest/MockPaymentsUIChromeScript.js @@ -0,0 +1,91 @@ +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cm = Components.manager; +const Cu = Components.utils; + +const CONTRACT_ID = "@mozilla.org/payment/ui-glue;1"; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); +var oldClassID, oldFactory; +var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID(); +var newFactory = { + createInstance: function(aOuter, aIID) { + if (aOuter) { + throw Components.results.NS_ERROR_NO_AGGREGATION; + } + return new MockPaymentsUIGlueInstance().QueryInterface(aIID); + }, + lockFactory: function(aLock) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) +}; + +addMessageListener("MockPaymentsUIGlue.init", function (message) { + try { + oldClassID = registrar.contractIDToCID(CONTRACT_ID); + oldFactory = Cm.getClassObject(oldClassID, Ci.nsIFactory); + } catch (ex) { + oldClassID = ""; + oldFactory = null; + dump("TEST-INFO | can't get payments ui glue registered component, " + + "assuming there is none\n"); + } + if (oldFactory) { + registrar.unregisterFactory(oldClassID, oldFactory); + } + registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory);}); + +addMessageListener("MockPaymentsUIGlue.cleanup", function (message) { + if (oldClassID) { + registrar.registerFactory(oldClassID, "", CONTRACT_ID, null); + } +}); + +var payments = new Map(); + +function MockPaymentsUIGlueInstance() { +}; + +MockPaymentsUIGlueInstance.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]), + + confirmPaymentRequest: function(aRequestId, + aRequests, + aSuccessCb, + aErrorCb) { + aSuccessCb.onresult(aRequestId, aRequests[0].type); + }, + + showPaymentFlow: function(aRequestId, + aPaymentFlowInfo, + aErrorCb) { + let win = Services.ww.openWindow(null, + null, + "_blank", + "chrome,dialog=no,resizable,scrollbars,centerscreen", + null); + + payments.set(aRequestId, win); + let docshell = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + docshell.paymentRequestId = aRequestId; + + win.document.location = aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt; + }, + + closePaymentFlow: function(aRequestId) { + payments.get(aRequestId).close(); + payments.delete(aRequestId); + + return Promise.resolve(); + }, +}; diff --git a/dom/payment/tests/mochitest/mochitest.ini b/dom/payment/tests/mochitest/mochitest.ini index 3d02e22a172..46c9ebd4003 100644 --- a/dom/payment/tests/mochitest/mochitest.ini +++ b/dom/payment/tests/mochitest/mochitest.ini @@ -1,12 +1,11 @@ [DEFAULT] -skip-if=true # This test uses MockPaymentsUIGlue which uses __exposedProps__ - # we need to move this to a chrome test, but these are not - # available in b2g or android for now. +skip-if=buildapp != 'b2g' && toolkit != 'android' support-files= file_mozpayproviderchecker.html file_payprovidersuccess.html file_payproviderfailure.html + MockPaymentsUIChromeScript.js [test_mozpaymentprovider.html] [test_mozpay_callbacks.html] diff --git a/dom/payment/tests/mochitest/test_mozpay_callbacks.html b/dom/payment/tests/mochitest/test_mozpay_callbacks.html index 7d6439b6143..e373f786085 100644 --- a/dom/payment/tests/mochitest/test_mozpay_callbacks.html +++ b/dom/payment/tests/mochitest/test_mozpay_callbacks.html @@ -79,7 +79,11 @@ function runTest() { tests.shift()(); } -SpecialPowers.MockPaymentsUIGlue.init(window); +const uiGlue = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('MockPaymentsUIChromeScript.js')); +SimpleTest.registerCleanupFunction(() => { + uiGlue.sendAsyncMessage("MockPaymentsUIGlue.cleanup", {}); +}); +uiGlue.sendAsyncMessage("MockPaymentsUIGlue.init", {}); SpecialPowers.pushPrefEnv({ "set": [ @@ -88,13 +92,13 @@ SpecialPowers.pushPrefEnv({ ["dom.payment.provider.1.name", "SuccessProvider"], ["dom.payment.provider.1.description", ""], ["dom.payment.provider.1.uri", - "http://mochi.test:8888/tests/dom/payment/tests/mochitest/file_payprovidersuccess.html?req="], + "https://example.com:443/tests/dom/payment/tests/mochitest/file_payprovidersuccess.html?req="], ["dom.payment.provider.1.type", "mozilla/payments/test/success"], ["dom.payment.provider.1.requestMethod", "GET"], ["dom.payment.provider.2.name", "FailureProvider"], ["dom.payment.provider.2.description", ""], ["dom.payment.provider.2.uri", - "http://mochi.test:8888/tests/dom/payment/tests/mochitest/file_payproviderfailure.html?req="], + "https://example.com:443/tests/dom/payment/tests/mochitest/file_payproviderfailure.html?req="], ["dom.payment.provider.2.type", "mozilla/payments/test/failure"], ["dom.payment.provider.2.requestMethod", "GET"], ] diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index b24149a46c1..cb08be68149 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -465,3 +465,14 @@ partial interface Navigator { readonly attribute boolean mozE10sEnabled; }; #endif + +#ifdef MOZ_PAY +partial interface Navigator { + [Throws, NewObject, Pref="dom.mozPay.enabled"] + // The 'jwts' parameter can be either a single DOMString or an array of + // DOMStrings. In both cases, it represents the base64url encoded and + // digitally signed payment information. Each payment provider should + // define its supported JWT format. + DOMRequest mozPay(any jwts); +}; +#endif diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index 3d3b34358c8..4cbb9585f14 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -42,5 +42,4 @@ marionette.jar: content/MockFilePicker.jsm (../specialpowers/content/MockFilePicker.jsm) content/MockColorPicker.jsm (../specialpowers/content/MockColorPicker.jsm) content/MockPermissionPrompt.jsm (../specialpowers/content/MockPermissionPrompt.jsm) - content/MockPaymentsUIGlue.jsm (../specialpowers/content/MockPaymentsUIGlue.jsm) content/Assert.jsm (../modules/Assert.jsm) diff --git a/testing/specialpowers/content/MockPaymentsUIGlue.jsm b/testing/specialpowers/content/MockPaymentsUIGlue.jsm deleted file mode 100644 index c9d7f69ed27..00000000000 --- a/testing/specialpowers/content/MockPaymentsUIGlue.jsm +++ /dev/null @@ -1,110 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -this.EXPORTED_SYMBOLS = ["MockPaymentsUIGlue"]; - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cm = Components.manager; -const Cu = Components.utils; - -const CONTRACT_ID = "@mozilla.org/payment/ui-glue;1"; - -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); -var classID; -var oldFactory; -var newFactory = function(window) { - return { - createInstance: function(aOuter, aIID) { - if (aOuter) { - throw Components.results.NS_ERROR_NO_AGGREGATION; - } - return new MockPaymentsUIGlueInstance(window).QueryInterface(aIID); - }, - lockFactory: function(aLock) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) - }; -}; - -this.MockPaymentsUIGlue = { - init: function(aWindow) { - try { - classID = registrar.contractIDToCID(CONTRACT_ID); - oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory); - } catch (ex) { - oldClassID = ""; - oldFactory = null; - dump("TEST-INFO | can't get payments ui glue registered component, " + - "assuming there is none"); - } - if (oldFactory) { - registrar.unregisterFactory(classID, oldFactory); - } - registrar.registerFactory(classID, "", CONTRACT_ID, - new newFactory(aWindow)); - }, - - reset: function() { - }, - - cleanup: function() { - this.reset(); - if (oldFactory) { - registrar.unregisterFactory(classID, newFactory); - registrar.registerFactory(classID, "", CONTRACT_ID, oldFactory); - } - } -}; - -function MockPaymentsUIGlueInstance(aWindow) { - this.window = aWindow; -}; - -MockPaymentsUIGlueInstance.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]), - - confirmPaymentRequest: function(aRequestId, - aRequests, - aSuccessCb, - aErrorCb) { - aSuccessCb.onresult(aRequestId, aRequests[0].type); - }, - - showPaymentFlow: function(aRequestId, - aPaymentFlowInfo, - aErrorCb) { - let document = this.window.document; - let frame = document.createElement("iframe"); - frame.setAttribute("mozbrowser", true); - frame.setAttribute("remote", true); - document.body.appendChild(frame); - let docshell = frame.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); - docshell.paymentRequestId = aRequestId; - frame.src = aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt; - }, - - closePaymentFlow: function(aRequestId) { - return Promise.resolve(); - } -}; -// Expose everything to content. We call reset() here so that all of the relevant -// lazy expandos get added. -MockPaymentsUIGlue.reset(); -function exposeAll(obj) { - var props = {}; - for (var prop in obj) - props[prop] = 'rw'; - obj.__exposedProps__ = props; -} -exposeAll(MockPaymentsUIGlue); -exposeAll(MockPaymentsUIGlueInstance.prototype); diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index d0cb19fb0a4..a8c86ac1637 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -14,7 +14,6 @@ var Cu = Components.utils; Cu.import("chrome://specialpowers/content/MockFilePicker.jsm"); Cu.import("chrome://specialpowers/content/MockColorPicker.jsm"); Cu.import("chrome://specialpowers/content/MockPermissionPrompt.jsm"); -Cu.import("chrome://specialpowers/content/MockPaymentsUIGlue.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -461,10 +460,6 @@ SpecialPowersAPI.prototype = { return MockPermissionPrompt; }, - get MockPaymentsUIGlue() { - return MockPaymentsUIGlue; - }, - loadChromeScript: function (url) { // Create a unique id for this chrome script let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] diff --git a/testing/specialpowers/jar.mn b/testing/specialpowers/jar.mn index 90793bb33de..5e7ad8716b9 100644 --- a/testing/specialpowers/jar.mn +++ b/testing/specialpowers/jar.mn @@ -8,5 +8,4 @@ specialpowers.jar: content/MockFilePicker.jsm (content/MockFilePicker.jsm) content/MockColorPicker.jsm (content/MockColorPicker.jsm) content/MockPermissionPrompt.jsm (content/MockPermissionPrompt.jsm) - content/MockPaymentsUIGlue.jsm (content/MockPaymentsUIGlue.jsm) content/Assert.jsm (../modules/Assert.jsm) From 1e50c3a5388a7ad7fcbf2b2f086fc60d24e2f09c Mon Sep 17 00:00:00 2001 From: John Shih Date: Tue, 8 Apr 2014 14:42:12 +0800 Subject: [PATCH 20/91] Bug 993311 - Convert Network Stats API to WebIDL. r=bzbarsky. --- dom/network/NetworkStatsManager.js | 110 +++--------------- dom/network/NetworkStatsManager.manifest | 1 - dom/network/interfaces/moz.build | 1 - .../tests/test_networkstats_alarms.html | 22 ++-- .../tests/test_networkstats_basics.html | 4 +- .../tests/test_networkstats_disabled.html | 6 +- .../test_networkstats_enabled_no_perm.html | 22 +--- .../tests/test_networkstats_enabled_perm.html | 4 +- .../tests/unit_stats/test_networkstats_db.js | 4 +- .../unit_stats/test_networkstats_service.js | 4 +- .../tests/test_networkstats-manage.html | 2 +- dom/webidl/MozNetworkStats.webidl | 6 +- .../MozNetworkStatsManager.webidl} | 44 +++---- dom/webidl/moz.build | 1 + 14 files changed, 69 insertions(+), 162 deletions(-) rename dom/{network/interfaces/nsIDOMNetworkStatsManager.idl => webidl/MozNetworkStatsManager.webidl} (65%) diff --git a/dom/network/NetworkStatsManager.js b/dom/network/NetworkStatsManager.js index 4f714339f13..b963aba2b57 100644 --- a/dom/network/NetworkStatsManager.js +++ b/dom/network/NetworkStatsManager.js @@ -111,7 +111,6 @@ NetworkStatsAlarm.prototype = { const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1"; const NETWORKSTATSMANAGER_CID = Components.ID("{ceb874cd-cc1a-4e65-b404-cc2d3e42425f}"); -const nsIDOMMozNetworkStatsManager = Ci.nsIDOMMozNetworkStatsManager; function NetworkStatsManager() { if (DEBUG) { @@ -122,40 +121,25 @@ function NetworkStatsManager() { NetworkStatsManager.prototype = { __proto__: DOMRequestIpcHelper.prototype, - checkPrivileges: function checkPrivileges() { - if (!this.hasPrivileges) { - throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE); - } - }, - getSamples: function getSamples(aNetwork, aStart, aEnd, aOptions) { - this.checkPrivileges(); - - if (aStart.constructor.name !== "Date" || - aEnd.constructor.name !== "Date" || - !(aNetwork instanceof this.window.MozNetworkStatsInterface) || - aStart > aEnd) { + if (aStart > aEnd) { throw Components.results.NS_ERROR_INVALID_ARG; } - let appManifestURL = null; - let browsingTrafficOnly = false; - let serviceType = null; - if (aOptions) { - // appManifestURL is used to query network statistics by app; - // serviceType is used to query network statistics by system service. - // It is illegal to specify both of them at the same time. - if (aOptions.appManifestURL && aOptions.serviceType) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } - // browsingTrafficOnly is meaningful only when querying by app. - if (!aOptions.appManifestURL && aOptions.browsingTrafficOnly) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - } - appManifestURL = aOptions.appManifestURL; - serviceType = aOptions.serviceType; - browsingTrafficOnly = aOptions.browsingTrafficOnly || false; + // appManifestURL is used to query network statistics by app; + // serviceType is used to query network statistics by system service. + // It is illegal to specify both of them at the same time. + if (aOptions.appManifestURL && aOptions.serviceType) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; } + // browsingTrafficOnly is meaningful only when querying by app. + if (!aOptions.appManifestURL && aOptions.browsingTrafficOnly) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + } + + let appManifestURL = aOptions.appManifestURL; + let serviceType = aOptions.serviceType; + let browsingTrafficOnly = aOptions.browsingTrafficOnly; // TODO Bug 929410 Date object cannot correctly pass through cpmm/ppmm IPC // This is just a work-around by passing timestamp numbers. @@ -175,12 +159,6 @@ NetworkStatsManager.prototype = { }, clearStats: function clearStats(aNetwork) { - this.checkPrivileges(); - - if (!aNetwork instanceof this.window.MozNetworkStatsInterface) { - throw Components.results.NS_ERROR_INVALID_ARG; - } - let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:Clear", { network: aNetwork.toJSON(), @@ -189,8 +167,6 @@ NetworkStatsManager.prototype = { }, clearAllStats: function clearAllStats() { - this.checkPrivileges(); - let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:ClearAll", {id: this.getRequestId(request)}); @@ -198,17 +174,6 @@ NetworkStatsManager.prototype = { }, addAlarm: function addAlarm(aNetwork, aThreshold, aOptions) { - this.checkPrivileges(); - - if (!aOptions) { - aOptions = Object.create(null); - } - - if (aOptions.startTime && aOptions.startTime.constructor.name !== "Date" || - !(aNetwork instanceof this.window.MozNetworkStatsInterface)) { - throw Components.results.NS_ERROR_INVALID_ARG; - } - let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:SetAlarm", {id: this.getRequestId(request), @@ -222,13 +187,8 @@ NetworkStatsManager.prototype = { }, getAllAlarms: function getAllAlarms(aNetwork) { - this.checkPrivileges(); - let network = null; if (aNetwork) { - if (!aNetwork instanceof this.window.MozNetworkStatsInterface) { - throw Components.results.NS_ERROR_INVALID_ARG; - } network = aNetwork.toJSON(); } @@ -241,8 +201,6 @@ NetworkStatsManager.prototype = { }, removeAlarms: function removeAlarms(aAlarmId) { - this.checkPrivileges(); - if (aAlarmId == 0) { aAlarmId = -1; } @@ -257,8 +215,6 @@ NetworkStatsManager.prototype = { }, getAvailableNetworks: function getAvailableNetworks() { - this.checkPrivileges(); - let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:GetAvailableNetworks", { id: this.getRequestId(request) }); @@ -266,8 +222,6 @@ NetworkStatsManager.prototype = { }, getAvailableServiceTypes: function getAvailableServiceTypes() { - this.checkPrivileges(); - let request = this.createRequest(); cpmm.sendAsyncMessage("NetworkStats:GetAvailableServiceTypes", { id: this.getRequestId(request) }); @@ -275,12 +229,10 @@ NetworkStatsManager.prototype = { }, get sampleRate() { - this.checkPrivileges(); return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0]; }, get maxStorageAge() { - this.checkPrivileges(); return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0]; }, @@ -390,27 +342,7 @@ NetworkStatsManager.prototype = { }, init: function(aWindow) { - // Set navigator.mozNetworkStats to null. - if (!Services.prefs.getBoolPref("dom.mozNetworkStats.enabled")) { - return null; - } - let principal = aWindow.document.nodePrincipal; - let secMan = Services.scriptSecurityManager; - let perm = principal == secMan.getSystemPrincipal() ? - Ci.nsIPermissionManager.ALLOW_ACTION : - Services.perms.testExactPermissionFromPrincipal(principal, - "networkstats-manage"); - - // Only pages with perm set can use the netstats. - this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION; - if (DEBUG) { - debug("has privileges: " + this.hasPrivileges); - } - - if (!this.hasPrivileges) { - return null; - } this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return", "NetworkStats:GetAvailableNetworks:Return", @@ -443,16 +375,10 @@ NetworkStatsManager.prototype = { }, classID : NETWORKSTATSMANAGER_CID, - QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsManager, - Ci.nsIDOMGlobalPropertyInitializer, - Ci.nsISupportsWeakReference, - Ci.nsIObserver]), - - classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID, - contractID: NETWORKSTATSMANAGER_CONTRACTID, - classDescription: "NetworkStatsManager", - interfaces: [nsIDOMMozNetworkStatsManager], - flags: nsIClassInfo.DOM_OBJECT}) + contractID : NETWORKSTATSMANAGER_CONTRACTID, + QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer, + Ci.nsISupportsWeakReference, + Ci.nsIObserver]), } this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsAlarm, diff --git a/dom/network/NetworkStatsManager.manifest b/dom/network/NetworkStatsManager.manifest index 6231285731a..8e8700910ff 100644 --- a/dom/network/NetworkStatsManager.manifest +++ b/dom/network/NetworkStatsManager.manifest @@ -12,4 +12,3 @@ contract @mozilla.org/networkstatsalarm;1 {a93ea13e-409c-4189-9b1e-95fff220be55} component {ceb874cd-cc1a-4e65-b404-cc2d3e42425f} NetworkStatsManager.js contract @mozilla.org/networkStatsManager;1 {ceb874cd-cc1a-4e65-b404-cc2d3e42425f} -category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1 diff --git a/dom/network/interfaces/moz.build b/dom/network/interfaces/moz.build index a503f951e81..8c82fe85474 100644 --- a/dom/network/interfaces/moz.build +++ b/dom/network/interfaces/moz.build @@ -12,7 +12,6 @@ XPIDL_SOURCES += [ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': XPIDL_SOURCES += [ - 'nsIDOMNetworkStatsManager.idl', 'nsINetworkStatsServiceProxy.idl', ] diff --git a/dom/network/tests/test_networkstats_alarms.html b/dom/network/tests/test_networkstats_alarms.html index 5fcaef568ed..b02879738eb 100644 --- a/dom/network/tests/test_networkstats_alarms.html +++ b/dom/network/tests/test_networkstats_alarms.html @@ -48,32 +48,30 @@ var steps = [ }; req.onerror = function () { - ok(req.error.name == "InvalidInterface", "Get InvalidInterface error"); + is(req.error.name, "InvalidInterface", "Get InvalidInterface error"); next(); } }, function () { ok(true, "Calling addAlarm() with invalid network or parameters."); + var msg = "TypeError: Not enough arguments to MozNetworkStatsManager.addAlarm."; try { navigator.mozNetworkStats.addAlarm(); } catch(ex) { - ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS, - "addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no parameters"); + is(ex.toString(), msg, "addAlarm() throws \"" + msg + "\" when no parameters"); } try { navigator.mozNetworkStats.addAlarm(100000); } catch(ex) { - ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS, - "addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no network"); + is(ex.toString(), msg, "addAlarm() throws " + msg + " when no network"); } try { navigator.mozNetworkStats.addAlarm(new window.MozNetworkStatsInterface(wifi)); } catch(ex) { - ok(ex.result == SpecialPowers.Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS, - "addAlarm() throws NS_ERROR_XPC_NOT_ENOUGH_ARGS exception when no threshold"); + is(ex.toString(), msg, "addAlarm() throws " + msg + " when no threshold"); } req = navigator.mozNetworkStats @@ -84,7 +82,7 @@ var steps = [ }; req.onerror = function () { - ok(req.error.name == "InvalidThresholdValue", "Get InvalidThresholdValue error"); + is(req.error.name, "InvalidThresholdValue", "Get InvalidThresholdValue error"); next(); } }, @@ -109,8 +107,8 @@ var steps = [ .getAllAlarms(new window.MozNetworkStatsInterface(wifi)); req.onsuccess = function () { - ok(req.result.length == 1, "Only one alarm"); - ok(req.result[0].alarmId == 1, "Get correct alarmId"); + is(req.result.length, 1, "Only one alarm"); + is(req.result[0].alarmId, 1, "Get correct alarmId"); next(); }; @@ -226,8 +224,8 @@ SpecialPowers.pushPrefEnv({'set': [["dom.mozNetworkStats.enabled", true]]}, ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should exist"); - ok(navigator.mozNetworkStats instanceof SpecialPowers.Ci.nsIDOMMozNetworkStatsManager, - "navigator.mozNetworkStats should be a nsIDOMMozNetworkStatsManager object"); + ok(navigator.mozNetworkStats instanceof MozNetworkStatsManager, + "navigator.mozNetworkStats should be a MozNetworkStatsManager object"); test(); }); diff --git a/dom/network/tests/test_networkstats_basics.html b/dom/network/tests/test_networkstats_basics.html index 2f6df9bc97e..df6f49c9c1b 100644 --- a/dom/network/tests/test_networkstats_basics.html +++ b/dom/network/tests/test_networkstats_basics.html @@ -333,8 +333,8 @@ SpecialPowers.pushPrefEnv({'set': [["dom.mozNetworkStats.enabled", true]]}, ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should exist"); - ok(navigator.mozNetworkStats instanceof SpecialPowers.Ci.nsIDOMMozNetworkStatsManager, - "navigator.mozNetworkStats should be a nsIDOMMozNetworkStatsManager object"); + ok(navigator.mozNetworkStats instanceof MozNetworkStatsManager, + "navigator.mozNetworkStats should be a MozNetworkStatsManager object"); test(); }); diff --git a/dom/network/tests/test_networkstats_disabled.html b/dom/network/tests/test_networkstats_disabled.html index f515d778c6a..59fe68f3b58 100644 --- a/dom/network/tests/test_networkstats_disabled.html +++ b/dom/network/tests/test_networkstats_disabled.html @@ -22,10 +22,8 @@ SpecialPowers.pushPrefEnv({'set': [["dom.mozNetworkStats.enabled", false]]}, ok(!SpecialPowers.getBoolPref("dom.mozNetworkStats.enabled"), "Preference 'dom.mozNetworkStats.enabled' is false."); - ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should exist"); - - is(navigator.mozNetworkStats, null, - "mozNetworkStats should be null when not enabled."); + ok(!('mozNetworkStats' in navigator), + "navigator.mozNetworkStats should not exist when pref not set"); SimpleTest.finish(); }); diff --git a/dom/network/tests/test_networkstats_enabled_no_perm.html b/dom/network/tests/test_networkstats_enabled_no_perm.html index 93c3e3e4c86..1ec321acc08 100644 --- a/dom/network/tests/test_networkstats_enabled_no_perm.html +++ b/dom/network/tests/test_networkstats_enabled_no_perm.html @@ -27,25 +27,11 @@ ok(!SpecialPowers.hasPermission("networkstats-manage", document), "Has no permission 'networkstats-manage'."); - ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should exist"); + ok(!('mozNetworkStats' in navigator), + "navigator.mozNetworkStats should not exist when permission not set"); - is(navigator.mozNetworkStats, null, - "mozNetworkStats should be null when no permission."); - - var error; - try { - navigator.mozNetworkStats.getAvailableNetworks; - - ok(false, - "Accessing navigator.mozNetworkStats.getAvailableNetworks should throw!"); - } catch (ex) { - error = ex; - } - - ok(error, - "Got an exception accessing navigator.mozNetworkStats.getAvailableNetworks"); - SimpleTest.finish(); -} + SimpleTest.finish(); + } diff --git a/dom/network/tests/test_networkstats_enabled_perm.html b/dom/network/tests/test_networkstats_enabled_perm.html index ceb32abe456..c22f8ecda68 100644 --- a/dom/network/tests/test_networkstats_enabled_perm.html +++ b/dom/network/tests/test_networkstats_enabled_perm.html @@ -30,8 +30,8 @@ SpecialPowers.pushPrefEnv({'set': [["dom.mozNetworkStats.enabled", true]]}, ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should exist"); - ok(navigator.mozNetworkStats instanceof SpecialPowers.Ci.nsIDOMMozNetworkStatsManager, - "navigator.mozNetworkStats should be a nsIDOMMozNetworkStatsManager object"); + ok(navigator.mozNetworkStats instanceof MozNetworkStatsManager, + "navigator.mozNetworkStats should be a MozNetworkStatsManager object"); SimpleTest.finish(); }); diff --git a/dom/network/tests/unit_stats/test_networkstats_db.js b/dom/network/tests/unit_stats/test_networkstats_db.js index 4b59ae58077..50a5dc18672 100644 --- a/dom/network/tests/unit_stats/test_networkstats_db.js +++ b/dom/network/tests/unit_stats/test_networkstats_db.js @@ -41,8 +41,8 @@ function filterTimestamp(date) { } function getNetworks() { - return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI }, - { id: '1234', type: Ci.nsIDOMMozNetworkStatsManager.MOBILE }]; + return [{ id: '0', type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI }, + { id: '1234', type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE }]; } function compareNetworks(networkA, networkB) { diff --git a/dom/network/tests/unit_stats/test_networkstats_service.js b/dom/network/tests/unit_stats/test_networkstats_service.js index 1f6eda43ce5..8c43a9b54bf 100644 --- a/dom/network/tests/unit_stats/test_networkstats_service.js +++ b/dom/network/tests/unit_stats/test_networkstats_service.js @@ -158,12 +158,12 @@ add_test(function test_queue() { }; // Fill networks with fake network interfaces to enable netd async requests. - var network = {id: "1234", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE}; + var network = {id: "1234", type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE}; var netId1 = NetworkStatsService.getNetworkId(network.id, network.type); NetworkStatsService._networks[netId1] = { network: network, interfaceName: "net1" }; - network = {id: "5678", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE}; + network = {id: "5678", type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE}; var netId2 = NetworkStatsService.getNetworkId(network.id, network.type); NetworkStatsService._networks[netId2] = { network: network, interfaceName: "net2" }; diff --git a/dom/permission/tests/test_networkstats-manage.html b/dom/permission/tests/test_networkstats-manage.html index 9eefe6a8a36..4fdc847717d 100644 --- a/dom/permission/tests/test_networkstats-manage.html +++ b/dom/permission/tests/test_networkstats-manage.html @@ -20,7 +20,7 @@ var gData = [ { perm: ["networkstats-manage"], obj: "mozNetworkStats", - idl: "nsIDOMMozNetworkStatsManager", + webidl: "MozNetworkStatsManager", settings: [["dom.mozNetworkStats.enabled", true]], }, ] diff --git a/dom/webidl/MozNetworkStats.webidl b/dom/webidl/MozNetworkStats.webidl index eb9713b617d..c6303cadb6b 100644 --- a/dom/webidl/MozNetworkStats.webidl +++ b/dom/webidl/MozNetworkStats.webidl @@ -14,15 +14,15 @@ dictionary NetworkStatsGetOptions * Note that, these two options cannot be specified at the same time for now; * others, an NS_ERROR_NOT_IMPLMENTED exception will be thrown. */ - DOMString appManifestURL; - DOMString serviceType; + DOMString appManifestURL? = null; + DOMString serviceType = ""; /** * If it is set as true, only the browsing traffic, which is generated from * the mozbrowser iframe element within an app, is returned in result. * If it is set as false or not set, the total traffic, which is generated * from both the mozapp and mozbrowser iframe elements, is returned. */ - boolean browsingTrafficOnly; + boolean browsingTrafficOnly = false; }; dictionary NetworkStatsAlarmOptions diff --git a/dom/network/interfaces/nsIDOMNetworkStatsManager.idl b/dom/webidl/MozNetworkStatsManager.webidl similarity index 65% rename from dom/network/interfaces/nsIDOMNetworkStatsManager.idl rename to dom/webidl/MozNetworkStatsManager.webidl index 9db848af102..0295e407877 100644 --- a/dom/network/interfaces/nsIDOMNetworkStatsManager.idl +++ b/dom/webidl/MozNetworkStatsManager.webidl @@ -1,14 +1,14 @@ +/* -*- 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/. */ + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ -#include "nsISupports.idl" - -interface nsIDOMDOMRequest; - -[scriptable, uuid(72c4e583-389d-4d1b-9424-702feabb6055)] -interface nsIDOMMozNetworkStatsManager : nsISupports -{ +[NavigatorProperty="mozNetworkStats", + JSImplementation="@mozilla.org/networkStatsManager;1", + CheckAnyPermissions="networkstats-manage", + Pref="dom.mozNetworkStats.enabled"] +interface MozNetworkStatsManager { /** * Constants for known interface types. */ @@ -21,12 +21,12 @@ interface nsIDOMMozNetworkStatsManager : nsISupports * If options is provided, per-app or per-system service usage will be * retrieved; otherwise the target will be overall system usage. * - * If success, the request result will be an nsIDOMMozNetworkStats object. + * If success, the request result will be an MozNetworkStats object. */ - nsIDOMDOMRequest getSamples(in nsISupports network, - in jsval start, - in jsval end, - [optional] in jsval options /* NetworkStatsGetOptions */); + DOMRequest getSamples(MozNetworkStatsInterface network, + Date start, + Date end, + optional NetworkStatsGetOptions options); /** * Install an alarm on a network. The network must be in the return of @@ -39,9 +39,9 @@ interface nsIDOMMozNetworkStatsManager : nsISupports * * If success, the |result| field of the DOMRequest keeps the alarm Id. */ - nsIDOMDOMRequest addAlarm(in nsISupports network, - in long long threshold, - [optional] in jsval options /* NetworkStatsAlarmOptions */); + DOMRequest addAlarm(MozNetworkStatsInterface network, + long long threshold, + optional NetworkStatsAlarmOptions options); /** * Obtain all alarms for those networks returned by getAvailableNetworks(). @@ -55,33 +55,33 @@ interface nsIDOMMozNetworkStatsManager : nsISupports * - threshold * - data */ - nsIDOMDOMRequest getAllAlarms([optional] in nsISupports network); + DOMRequest getAllAlarms(optional MozNetworkStatsInterface network); /** * Remove all network alarms. If an |alarmId| is provided, then only that * alarm is removed. */ - nsIDOMDOMRequest removeAlarms([optional] in long alarmId); + DOMRequest removeAlarms(optional unsigned long alarmId = 0); /** * Remove all stats related with the provided network from DB. */ - nsIDOMDOMRequest clearStats(in nsISupports network); + DOMRequest clearStats(MozNetworkStatsInterface network); /** * Remove all stats in the database. */ - nsIDOMDOMRequest clearAllStats(); + DOMRequest clearAllStats(); /** * Return available networks that used to be saved in the database. */ - nsIDOMDOMRequest getAvailableNetworks(); // array of MozNetworkStatsInterface. + DOMRequest getAvailableNetworks(); // array of MozNetworkStatsInterface. /** * Return available service types that used to be saved in the database. */ - nsIDOMDOMRequest getAvailableServiceTypes(); // array of string. + DOMRequest getAvailableServiceTypes(); // array of string. /** * Minimum time in milliseconds between samples stored in the database. diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 39d334ab394..ceec6ad016d 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -750,6 +750,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': 'MozNetworkStatsAlarm.webidl', 'MozNetworkStatsData.webidl', 'MozNetworkStatsInterface.webidl', + 'MozNetworkStatsManager.webidl', 'MozSpeakerManager.webidl', 'MozWifiCapabilities.webidl', 'MozWifiManager.webidl', From d26fa15b88fd7239c8c8a82eff9a86a5fc109518 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 1 Feb 2016 09:44:52 -0800 Subject: [PATCH 21/91] Bug 1204784: Handle cases in nsThreadPool where Gecko doesn't own the thread that we're running on. r=froydnj --- xpcom/threads/nsThreadPool.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index 0516e0cd4e6..173afbdd027 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -122,7 +122,7 @@ nsThreadPool::PutEvent(already_AddRefed&& aEvent) if (killThread) { // We never dispatched any events to the thread, so we can shut it down // asynchronously without worrying about anything. - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->AsyncShutdown())); + ShutdownThread(thread); } else { thread->Dispatch(this, NS_DISPATCH_NORMAL); } @@ -135,12 +135,18 @@ nsThreadPool::ShutdownThread(nsIThread* aThread) { LOG(("THRD-P(%p) shutdown async [%p]\n", this, aThread)); - // This method is responsible for calling Shutdown on |aThread|. This must be - // done from some other thread, so we use the main thread of the application. - - MOZ_ASSERT(!NS_IsMainThread(), "wrong thread"); - - nsCOMPtr r = NS_NewRunnableMethod(aThread, &nsIThread::Shutdown); + // This is either called by a threadpool thread that is out of work, or + // a thread that attempted to create a threadpool thread and raced in + // such a way that the newly created thread is no longer necessary. + // In the first case, we must go to another thread to shut aThread down + // (because it is the current thread). In the second case, we cannot + // synchronously shut down the current thread (because then Dispatch() would + // spin the event loop, and that could blow up the world), and asynchronous + // shutdown requires this thread have an event loop (and it may not, see bug + // 10204784). The simplest way to cover all cases is to asynchronously + // shutdown aThread from the main thread. + nsCOMPtr r = NS_NewRunnableMethod(aThread, + &nsIThread::AsyncShutdown); NS_DispatchToMainThread(r); } From 0a4115365e2c2f5e23bb3f9bba5a127651482026 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 1 Feb 2016 09:44:52 -0800 Subject: [PATCH 22/91] Bug 1244582: Add back in a null check that was accidentally removed. r=smaug --- dom/events/EventStateManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 2df384e7cc0..a324f80eb75 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -2006,6 +2006,7 @@ EventStateManager::GetContentViewer(nsIContentViewer** aCv) nsCOMPtr focusedWindow; fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); + if (!focusedWindow) return NS_ERROR_FAILURE; auto* ourWindow = nsPIDOMWindowOuter::From(focusedWindow); From b08fdb7586d8bf3cf1baa72700122cb42ca2bcbb Mon Sep 17 00:00:00 2001 From: Rail Aliiev Date: Mon, 1 Feb 2016 13:24:53 -0500 Subject: [PATCH 23/91] Bug 1166464 - Bump version after shipping release builds r=jlund npotb DONTBUILD --- .../configs/releases/postrelease_beta.py | 14 ++ .../configs/releases/postrelease_date.py | 14 ++ .../configs/releases/postrelease_esr38.py | 19 ++ .../configs/releases/postrelease_release.py | 18 ++ .../mozharness/mozharness/mozilla/merge.py | 139 +++++++++++++ .../scripts/merge_day/gecko_migration.py | 182 ++++-------------- .../release/postrelease_version_bump.py | 141 ++++++++++++++ 7 files changed, 378 insertions(+), 149 deletions(-) create mode 100644 testing/mozharness/configs/releases/postrelease_beta.py create mode 100644 testing/mozharness/configs/releases/postrelease_date.py create mode 100644 testing/mozharness/configs/releases/postrelease_esr38.py create mode 100644 testing/mozharness/configs/releases/postrelease_release.py create mode 100644 testing/mozharness/mozharness/mozilla/merge.py create mode 100644 testing/mozharness/scripts/release/postrelease_version_bump.py diff --git a/testing/mozharness/configs/releases/postrelease_beta.py b/testing/mozharness/configs/releases/postrelease_beta.py new file mode 100644 index 00000000000..78ff4fc2322 --- /dev/null +++ b/testing/mozharness/configs/releases/postrelease_beta.py @@ -0,0 +1,14 @@ +config = { + "log_name": "bump_beta", + "version_files": [{"file": "browser/config/version_display.txt"}], + "repo": { + "repo": "https://hg.mozilla.org/releases/mozilla-beta", + "revision": "default", + "dest": "mozilla-beta", + "vcs": "hg", + }, + "push_dest": "ssh://hg.mozilla.org/releases/mozilla-beta", + "ignore_no_changes": True, + "ssh_user": "ffxbld", + "ssh_key": "~/.ssh/ffxbld_rsa", +} diff --git a/testing/mozharness/configs/releases/postrelease_date.py b/testing/mozharness/configs/releases/postrelease_date.py new file mode 100644 index 00000000000..e23b033e687 --- /dev/null +++ b/testing/mozharness/configs/releases/postrelease_date.py @@ -0,0 +1,14 @@ +config = { + "log_name": "bump_date", + "version_files": [{"file": "browser/config/version_display.txt"}], + "repo": { + "repo": "https://hg.mozilla.org/projects/date", + "revision": "default", + "dest": "date", + "vcs": "hg", + }, + "push_dest": "ssh://hg.mozilla.org/projects/date", + "ignore_no_changes": True, + "ssh_user": "ffxbld", + "ssh_key": "~/.ssh/ffxbld_rsa", +} diff --git a/testing/mozharness/configs/releases/postrelease_esr38.py b/testing/mozharness/configs/releases/postrelease_esr38.py new file mode 100644 index 00000000000..97a658c0d57 --- /dev/null +++ b/testing/mozharness/configs/releases/postrelease_esr38.py @@ -0,0 +1,19 @@ +config = { + "log_name": "bump_esr38", + "version_files": [ + {"file": "browser/config/version.txt"}, + # TODO: enable this for esr45 + # {"file": "browser/config/version_display.txt"}, + {"file": "config/milestone.txt"}, + ], + "repo": { + "repo": "https://hg.mozilla.org/releases/mozilla-esr38", + "revision": "default", + "dest": "mozilla-esr38", + "vcs": "hg", + }, + "push_dest": "ssh://hg.mozilla.org/releases/mozilla-esr38", + "ignore_no_changes": True, + "ssh_user": "ffxbld", + "ssh_key": "~/.ssh/ffxbld_rsa", +} diff --git a/testing/mozharness/configs/releases/postrelease_release.py b/testing/mozharness/configs/releases/postrelease_release.py new file mode 100644 index 00000000000..e0d2ee93201 --- /dev/null +++ b/testing/mozharness/configs/releases/postrelease_release.py @@ -0,0 +1,18 @@ +config = { + "log_name": "bump_release", + "version_files": [ + {"file": "browser/config/version.txt"}, + {"file": "browser/config/version_display.txt"}, + {"file": "config/milestone.txt"}, + ], + "repo": { + "repo": "https://hg.mozilla.org/releases/mozilla-release", + "revision": "default", + "dest": "mozilla-release", + "vcs": "hg", + }, + "push_dest": "ssh://hg.mozilla.org/releases/mozilla-release", + "ignore_no_changes": True, + "ssh_user": "ffxbld", + "ssh_key": "~/.ssh/ffxbld_rsa", +} diff --git a/testing/mozharness/mozharness/mozilla/merge.py b/testing/mozharness/mozharness/mozilla/merge.py new file mode 100644 index 00000000000..f47c963e0d4 --- /dev/null +++ b/testing/mozharness/mozharness/mozilla/merge.py @@ -0,0 +1,139 @@ +import os + +from mozharness.base.errors import HgErrorList +from mozharness.base.log import FATAL, INFO +from mozharness.base.vcs.mercurial import MercurialVCS + + +class GeckoMigrationMixin(object): + + def get_version(self, repo_root, + version_file="browser/config/version.txt"): + version_path = os.path.join(repo_root, version_file) + contents = self.read_from_file(version_path, error_level=FATAL) + lines = [l for l in contents.splitlines() if l and + not l.startswith("#")] + return lines[-1].split(".") + + def replace(self, file_name, from_, to_): + """ Replace text in a file. + """ + text = self.read_from_file(file_name, error_level=FATAL) + new_text = text.replace(from_, to_) + if text == new_text: + self.fatal("Cannot replace '%s' to '%s' in '%s'" % + (from_, to_, file_name)) + self.write_to_file(file_name, new_text, error_level=FATAL) + + def query_hg_revision(self, path): + """ Avoid making 'pull' a required action every run, by being able + to fall back to figuring out the revision from the cloned repo + """ + m = MercurialVCS(log_obj=self.log_obj, config=self.config) + revision = m.get_revision_from_path(path) + return revision + + def hg_commit(self, cwd, message, user=None, ignore_no_changes=False): + """ Commit changes to hg. + """ + cmd = self.query_exe('hg', return_type='list') + [ + 'commit', '-m', message] + if user: + cmd.extend(['-u', user]) + success_codes = [0] + if ignore_no_changes: + success_codes.append(1) + self.run_command( + cmd, cwd=cwd, error_list=HgErrorList, + halt_on_failure=True, + success_codes=success_codes + ) + return self.query_hg_revision(cwd) + + def clean_repos(self): + """ We may end up with contaminated local repos at some point, but + we don't want to have to clobber and reclone from scratch every + time. + + This is an attempt to clean up the local repos without needing a + clobber. + """ + dirs = self.query_abs_dirs() + hg = self.query_exe("hg", return_type="list") + hg_repos = self.query_gecko_repos() + hg_strip_error_list = [{ + 'substr': r'''abort: empty revision set''', 'level': INFO, + 'explanation': "Nothing to clean up; we're good!", + }] + HgErrorList + for repo_config in hg_repos: + repo_name = repo_config["dest"] + repo_path = os.path.join(dirs['abs_work_dir'], repo_name) + if os.path.exists(repo_path): + # hg up -C to discard uncommitted changes + self.run_command( + hg + ["up", "-C", "-r", repo_config['revision']], + cwd=repo_path, + error_list=HgErrorList, + halt_on_failure=True, + ) + # discard unpushed commits + status = self.retry( + self.run_command, + args=(hg + ["--config", "extensions.mq=", "strip", + "--no-backup", "outgoing()"], ), + kwargs={ + 'cwd': repo_path, + 'error_list': hg_strip_error_list, + 'return_type': 'num_errors', + 'success_codes': (0, 255), + }, + ) + if status not in [0, 255]: + self.fatal("Issues stripping outgoing revisions!") + # 2nd hg up -C to make sure we're not on a stranded head + # which can happen when reverting debugsetparents + self.run_command( + hg + ["up", "-C", "-r", repo_config['revision']], + cwd=repo_path, + error_list=HgErrorList, + halt_on_failure=True, + ) + + def commit_changes(self): + """ Do the commit. + """ + hg = self.query_exe("hg", return_type="list") + for cwd in self.query_commit_dirs(): + self.run_command(hg + ["diff"], cwd=cwd) + self.hg_commit( + cwd, user=self.config['hg_user'], + message=self.query_commit_message(), + ignore_no_changes=self.config.get("ignore_no_changes", False) + ) + self.info("Now verify |hg out| and |hg out --patch| if you're paranoid, and --push") + + def push(self): + """ + """ + error_message = """Push failed! If there was a push race, try rerunning +the script (--clean-repos --pull --migrate). The second run will be faster.""" + hg = self.query_exe("hg", return_type="list") + for cwd in self.query_push_dirs(): + if not cwd: + self.warning("Skipping %s" % cwd) + continue + push_cmd = hg + ['push'] + self.query_push_args(cwd) + if self.config.get("push_dest"): + push_cmd.append(self.config["push_dest"]) + status = self.run_command( + push_cmd, + cwd=cwd, + error_list=HgErrorList, + success_codes=[0, 1], + ) + if status == 1: + self.warning("No changes for %s!" % cwd) + elif status: + self.fatal(error_message) + + diff --git a/testing/mozharness/scripts/merge_day/gecko_migration.py b/testing/mozharness/scripts/merge_day/gecko_migration.py index be592014450..f8ef9cdf41e 100755 --- a/testing/mozharness/scripts/merge_day/gecko_migration.py +++ b/testing/mozharness/scripts/merge_day/gecko_migration.py @@ -16,21 +16,21 @@ and http://hg.mozilla.org/build/tools/file/084bc4e2fc76/release/merge_helper.py """ -from getpass import getpass import os import pprint import subprocess import sys +from getpass import getpass sys.path.insert(1, os.path.dirname(os.path.dirname(sys.path[0]))) from mozharness.base.errors import HgErrorList -from mozharness.base.log import INFO, FATAL from mozharness.base.python import VirtualenvMixin, virtualenv_config_options from mozharness.base.vcs.vcsbase import MercurialScript -from mozharness.base.vcs.mercurial import MercurialVCS from mozharness.mozilla.selfserve import SelfServeMixin from mozharness.mozilla.updates.balrog import BalrogMixin +from mozharness.mozilla.buildbot import BuildbotMixin +from mozharness.mozilla.merge import GeckoMigrationMixin VALID_MIGRATION_BEHAVIORS = ( "beta_to_release", "aurora_to_beta", "central_to_aurora", "release_to_esr", @@ -39,7 +39,8 @@ VALID_MIGRATION_BEHAVIORS = ( # GeckoMigration {{{1 -class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMixin): +class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, + SelfServeMixin, BuildbotMixin, GeckoMigrationMixin): config_options = [ [['--hg-user', ], { "action": "store", @@ -160,13 +161,26 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix self.info(pprint.pformat(self.gecko_repos)) return self.gecko_repos - def query_hg_revision(self, path): - """ Avoid making 'pull' a required action every run, by being able - to fall back to figuring out the revision from the cloned repo - """ - m = MercurialVCS(log_obj=self.log_obj, config=self.config) - revision = m.get_revision_from_path(path) - return revision + def query_commit_dirs(self): + dirs = self.query_abs_dirs() + commit_dirs = [dirs['abs_to_dir']] + if self.config['migration_behavior'] == 'central_to_aurora': + commit_dirs.append(dirs['abs_from_dir']) + return commit_dirs + + def query_commit_message(self): + return "Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release" + + def query_push_dirs(self): + dirs = self.query_abs_dirs() + return dirs.get('abs_from_dir'), dirs.get('abs_to_dir') + + def query_push_args(self, cwd): + if cwd == self.query_abs_dirs()['abs_to_dir'] and \ + self.config['migration_behavior'] == 'beta_to_release': + return ['--new-branch'] + else: + return [] def query_from_revision(self): """ Shortcut to get the revision for the from repo @@ -180,11 +194,6 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix dirs = self.query_abs_dirs() return self.query_hg_revision(dirs['abs_to_dir']) - def get_fx_version(self, path): - version_path = os.path.join(path, "browser", "config", "version.txt") - contents = self.read_from_file(version_path, error_level=FATAL) - return contents.split(".") - def hg_tag(self, cwd, tags, user=None, message=None, revision=None, force=None, halt_on_failure=True): if isinstance(tags, basestring): @@ -210,23 +219,6 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix error_list=HgErrorList ) - def hg_commit(self, cwd, message, user=None, ignore_no_changes=False): - """ Commit changes to hg. - """ - cmd = self.query_exe('hg', return_type='list') + [ - 'commit', '-m', message] - if user: - cmd.extend(['-u', user]) - success_codes = [0] - if ignore_no_changes: - success_codes.append(1) - self.run_command( - cmd, cwd=cwd, error_list=HgErrorList, - halt_on_failure=True, - success_codes=success_codes - ) - return self.query_hg_revision(cwd) - def hg_merge_via_debugsetparents(self, cwd, old_head, new_head, preserve_tags=True, user=None): """ Merge 2 heads avoiding non-fastforward commits @@ -290,16 +282,6 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix else: self.info(".hgtags file is identical, no need to commit") - def replace(self, file_name, from_, to_): - """ Replace text in a file. - """ - text = self.read_from_file(file_name, error_level=FATAL) - new_text = text.replace(from_, to_) - if text == new_text: - self.fatal("Cannot replace '%s' to '%s' in '%s'" % - (from_, to_, file_name)) - self.write_to_file(file_name, new_text, error_level=FATAL) - def remove_locales(self, file_name, locales): """ Remove locales from shipped-locales (m-r only) """ @@ -369,11 +351,11 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix error_list=HgErrorList, halt_on_failure=True, ) - next_ma_version = self.get_fx_version(dirs['abs_to_dir'])[0] + next_ma_version = self.get_version(dirs['abs_to_dir'])[0] self.bump_version(dirs['abs_to_dir'], next_ma_version, next_ma_version, "a1", "a2") self.apply_replacements() # bump m-c version - curr_mc_version = self.get_fx_version(dirs['abs_from_dir'])[0] + curr_mc_version = self.get_version(dirs['abs_from_dir'])[0] next_mc_version = str(int(curr_mc_version) + 1) self.bump_version( dirs['abs_from_dir'], curr_mc_version, next_mc_version, "a1", "a1", @@ -392,7 +374,7 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix staging beta user repo migrations. """ dirs = self.query_abs_dirs() - mb_version = self.get_fx_version(dirs['abs_to_dir'])[0] + mb_version = self.get_version(dirs['abs_to_dir'])[0] self.bump_version(dirs['abs_to_dir'], mb_version, mb_version, "a2", "") self.apply_replacements() self.touch_clobber_file(dirs['abs_to_dir']) @@ -473,12 +455,13 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix halt_on_failure=True, ) +# Actions {{{1 def bump_second_digit(self, *args, **kwargs): """Bump second digit. ESR need only the second digit bumped as a part of merge day.""" dirs = self.query_abs_dirs() - version = self.get_fx_version(dirs['abs_to_dir']) + version = self.get_version(dirs['abs_to_dir']) curr_version = ".".join(version) next_version = list(version) # bump the second digit @@ -492,56 +475,6 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix curr_version, next_version + f["suffix"]) self.touch_clobber_file(dirs['abs_to_dir']) -# Actions {{{1 - def clean_repos(self): - """ We may end up with contaminated local repos at some point, but - we don't want to have to clobber and reclone from scratch every - time. - - This is an attempt to clean up the local repos without needing a - clobber. - """ - dirs = self.query_abs_dirs() - hg = self.query_exe("hg", return_type="list") - hg_repos = self.query_gecko_repos() - hg_strip_error_list = [{ - 'substr': r'''abort: empty revision set''', 'level': INFO, - 'explanation': "Nothing to clean up; we're good!", - }] + HgErrorList - for repo_config in hg_repos: - repo_name = repo_config["dest"] - repo_path = os.path.join(dirs['abs_work_dir'], repo_name) - if os.path.exists(repo_path): - # hg up -C to discard uncommitted changes - self.run_command( - hg + ["up", "-C", "-r", repo_config['revision']], - cwd=repo_path, - error_list=HgErrorList, - halt_on_failure=True, - ) - # discard unpushed commits - status = self.retry( - self.run_command, - args=(hg + ["--config", "extensions.mq=", "strip", - "--no-backup", "outgoing()"], ), - kwargs={ - 'cwd': repo_path, - 'error_list': hg_strip_error_list, - 'return_type': 'num_errors', - 'success_codes': (0, 255), - }, - ) - if status not in [0, 255]: - self.fatal("Issues stripping outgoing revisions!") - # 2nd hg up -C to make sure we're not on a stranded head - # which can happen when reverting debugsetparents - self.run_command( - hg + ["up", "-C", "-r", repo_config['revision']], - cwd=repo_path, - error_list=HgErrorList, - halt_on_failure=True, - ) - def pull(self): """ Pull tools first, then use hgtool for the gecko repos """ @@ -560,8 +493,8 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix """ Perform the migration. """ dirs = self.query_abs_dirs() - from_fx_major_version = self.get_fx_version(dirs['abs_from_dir'])[0] - to_fx_major_version = self.get_fx_version(dirs['abs_to_dir'])[0] + from_fx_major_version = self.get_version(dirs['abs_from_dir'])[0] + to_fx_major_version = self.get_version(dirs['abs_to_dir'])[0] base_from_rev = self.query_from_revision() base_to_rev = self.query_to_revision() base_tag = self.config['base_tag'] % {'major_version': from_fx_major_version} @@ -599,54 +532,6 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, SelfServeMix getattr(self, self.config['migration_behavior'])(end_tag=end_tag) self.info("Verify the diff, and apply any manual changes, such as disabling features, and --commit-changes") - def commit_changes(self): - """ Do the commit. - """ - hg = self.query_exe("hg", return_type="list") - dirs = self.query_abs_dirs() - commit_dirs = [dirs['abs_to_dir']] - if self.config['migration_behavior'] == 'central_to_aurora': - commit_dirs.append(dirs['abs_from_dir']) - for cwd in commit_dirs: - self.run_command(hg + ["diff"], cwd=cwd) - self.hg_commit( - cwd, user=self.config['hg_user'], - message="Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release" - ) - self.info("Now verify |hg out| and |hg out --patch| if you're paranoid, and --push") - - def push(self): - """ - """ - error_message = """Push failed! If there was a push race, try rerunning -the script (--clean-repos --pull --migrate). The second run will be faster.""" - dirs = self.query_abs_dirs() - hg = self.query_exe("hg", return_type="list") - for cwd in (dirs.get('abs_from_dir'), dirs.get('abs_to_dir')): - if not cwd: - self.warning("Skipping %s" % cwd) - continue - push_cmd = hg + ['push'] - if cwd == dirs['abs_to_dir'] and self.config['migration_behavior'] == 'beta_to_release': - push_cmd.append('--new-branch') - status = self.run_command( - push_cmd, - cwd=cwd, - error_list=HgErrorList, - success_codes=[0, 1], - ) - if status == 1: - self.warning("No changes for %s!" % cwd) - elif status: - if cwd == dirs['abs_from_dir'] and self.config['migration_behavior'] == 'central_to_aurora': - message = """m-c push failed! -You may be able to fix by |hg rebase| and rerunning --push if successful. -If not, try rerunning the script (--clean-repos --pull --migrate). -The second run will be faster.""" - else: - message = error_message - self.fatal(message) - def trigger_builders(self): """Triggers builders that should be run directly after a merge. There are two different types of things we trigger: @@ -675,5 +560,4 @@ The second run will be faster.""" # __main__ {{{1 if __name__ == '__main__': - gecko_migration = GeckoMigration() - gecko_migration.run_and_exit() + GeckoMigration().run_and_exit() diff --git a/testing/mozharness/scripts/release/postrelease_version_bump.py b/testing/mozharness/scripts/release/postrelease_version_bump.py new file mode 100644 index 00000000000..ea9c63ef18f --- /dev/null +++ b/testing/mozharness/scripts/release/postrelease_version_bump.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# lint_ignore=E501 +# ***** BEGIN LICENSE BLOCK ***** +# 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/. +# ***** END LICENSE BLOCK ***** +""" postrelease_version_bump.py + +A script to increase in-tree version number after shipping a release. +""" + +import os +import sys + +sys.path.insert(1, os.path.dirname(os.path.dirname(sys.path[0]))) +from mozharness.base.vcs.vcsbase import MercurialScript +from mozharness.mozilla.buildbot import BuildbotMixin +from mozharness.mozilla.merge import GeckoMigrationMixin + + +# PostReleaseVersionBump {{{1 +class PostReleaseVersionBump(MercurialScript, BuildbotMixin, + GeckoMigrationMixin): + config_options = [ + [['--hg-user', ], { + "action": "store", + "dest": "hg_user", + "type": "string", + "default": "ffxbld ", + "help": "Specify what user to use to commit to hg.", + }], + [['--next-version', ], { + "action": "store", + "dest": "next_version", + "type": "string", + "help": "Next version used in version bump", + }], + [['--ssh-user', ], { + "action": "store", + "dest": "ssh_user", + "type": "string", + "help": "SSH username with hg.mozilla.org permissions", + }], + [['--ssh-key', ], { + "action": "store", + "dest": "ssh_key", + "type": "string", + "help": "Path to SSH key.", + }], + ] + + def __init__(self, require_config_file=True): + super(PostReleaseVersionBump, self).__init__( + config_options=self.config_options, + all_actions=[ + 'clobber', + 'clean-repos', + 'pull', + 'bump_postrelease', + 'commit-changes', + 'push', + ], + default_actions=[ + 'clean-repos', + 'pull', + 'bump_postrelease', + 'commit-changes', + 'push', + ], + config={ + 'buildbot_json_path': 'buildprops.json', + }, + require_config_file=require_config_file + ) + + def _pre_config_lock(self, rw_config): + super(PostReleaseVersionBump, self)._pre_config_lock(rw_config) + # override properties from buildbot properties here as defined by + # taskcluster properties + self.read_buildbot_config() + if not self.buildbot_config: + self.warning("Skipping buildbot properties overrides") + else: + next_version = self.buildbot_config["properties"].get("next_version") + if next_version: + if self.config.get("next_version"): + self.warning("Overriding next_version %s by %s" % + (self.config["next_version"], next_version)) + self.config["next_version"] = next_version + if not self.config.get("next_version"): + self.fatal("Next version has to be set. Use --next-version or " + "pass `next_version' via buildbot properties.") + + def query_abs_dirs(self): + """ Allow for abs_from_dir and abs_to_dir + """ + if self.abs_dirs: + return self.abs_dirs + self.abs_dirs = super(PostReleaseVersionBump, self).query_abs_dirs() + self.abs_dirs["abs_gecko_dir"] = os.path.join( + self.abs_dirs['abs_work_dir'], self.config["repo"]["dest"]) + return self.abs_dirs + + def query_gecko_repos(self): + """Build a list of repos to clone.""" + return [self.config["repo"]] + + def query_commit_dirs(self): + return [self.query_abs_dirs()["abs_gecko_dir"]] + + def query_commit_message(self): + return "Automatic version bump. CLOSED TREE NO BUG a=release" + + def query_push_dirs(self): + return self.query_commit_dirs() + + def query_push_args(self, cwd): + # cwd is not used here + hg_ssh_opts = "ssh -l {user} -i {key}".format( + user=self.config["ssh_user"], + key=os.path.expanduser(self.config["ssh_key"]) + ) + return ["-e", hg_ssh_opts] + + def pull(self): + super(PostReleaseVersionBump, self).pull( + repos=self.query_gecko_repos()) + + def bump_postrelease(self, *args, **kwargs): + """Bump version""" + dirs = self.query_abs_dirs() + for f in self.config["version_files"]: + curr_version = ".".join( + self.get_version(dirs['abs_gecko_dir'], f["file"])) + self.replace(os.path.join(dirs['abs_gecko_dir'], f["file"]), + curr_version, self.config["next_version"]) + +# __main__ {{{1 +if __name__ == '__main__': + PostReleaseVersionBump().run_and_exit() From e9ef26ec74d36ed6d396477b3742bd1b6fbaefb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20N=C3=A4slund?= Date: Mon, 1 Feb 2016 12:31:17 -0600 Subject: [PATCH 24/91] Bug 1239710 - Use CountingSort for Uint8Array and Int8Array; r=mrrrgn --- js/src/builtin/Sorting.js | 37 ++++++++++++++++++++++++++++++++++++ js/src/builtin/TypedArray.js | 7 +++++++ js/src/vm/SelfHosting.cpp | 30 +++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/js/src/builtin/Sorting.js b/js/src/builtin/Sorting.js index ade8fe1954d..2dcbf3af94c 100644 --- a/js/src/builtin/Sorting.js +++ b/js/src/builtin/Sorting.js @@ -6,6 +6,43 @@ // consolidated here to avoid confusion and re-implementation of existing // algorithms. +// For sorting values with limited range; uint8 and int8. +function CountingSort(array, len, signed) { + var buffer = new List(); + var min = 0; + + // Map int8 values onto the uint8 range when storing in buffer. + if (signed) { + min = -128; + } + + for (var i = 0; i < 256; i++) { + buffer[i] = 0; + } + + // Populate the buffer + for (var i = 0; i < len; i++) { + var val = array[i]; + buffer[val - min]++ + } + + // Traverse the buffer in order and write back elements to array + var val = 0; + for (var i = 0; i < len; i++) { + // Invariant: sum(buffer[val:]) == len-i + while (true) { + if (buffer[val] > 0) { + array[i] = val + min; + buffer[val]--; + break; + } else { + val++; + } + } + } + return array; +} + // For sorting small arrays. function InsertionSort(array, from, to, comparefn) { var item, swap; diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 3966b1ad40e..cb5280077d9 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -987,6 +987,13 @@ function TypedArraySort(comparefn) { var len = TypedArrayLength(obj); if (comparefn === undefined) { + // CountingSort doesn't invoke the comparefn + if (IsUint8TypedArray(obj)) { + return CountingSort(obj, len, false /* signed */); + } else if (IsInt8TypedArray(obj)) { + return CountingSort(obj, len, true /* signed */); + } + comparefn = TypedArrayCompare; } else { // To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26 diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index bcd41a3e6d1..86c8059475c 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -780,6 +780,34 @@ intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_IsSpecificTypedArray(JSContext* cx, unsigned argc, Value* vp, Scalar::Type type) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + JSObject* obj = &args[0].toObject(); + + bool isArray = JS_GetArrayBufferViewType(obj) == type; + + args.rval().setBoolean(isArray); + return true; +} + +static bool +intrinsic_IsUint8TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint8) || + intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint8Clamped); +} + +static bool +intrinsic_IsInt8TypedArray(JSContext* cx, unsigned argc, Value* vp) +{ + return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int8); +} + static bool intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp) { @@ -1769,6 +1797,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("ArrayBufferByteLength", intrinsic_ArrayBufferByteLength, 1,0), JS_FN("ArrayBufferCopyData", intrinsic_ArrayBufferCopyData, 4,0), + JS_FN("IsUint8TypedArray", intrinsic_IsUint8TypedArray, 1,0), + JS_FN("IsInt8TypedArray", intrinsic_IsInt8TypedArray, 1,0), JS_INLINABLE_FN("IsTypedArray", intrinsic_IsInstanceOfBuiltin, 1,0, IntrinsicIsTypedArray), From a37a2c62e4438ef9d4b96a47691285ee22b25a7a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 29 Jan 2016 16:21:39 -0600 Subject: [PATCH 25/91] Bug 1244304 - Remove unused appId / inBrowser from permission manager. r=ehsan --- extensions/cookie/nsPermissionManager.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index bdd41af81e3..f0e7bd1f8d2 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -1773,14 +1773,6 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, // If requested, create the entry in the DB. if (aDBOperation == eWriteToDB) { - uint32_t appId; - rv = aPrincipal->GetAppId(&appId); - NS_ENSURE_SUCCESS(rv, rv); - - bool isInBrowserElement; - rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); - NS_ENSURE_SUCCESS(rv, rv); - UpdateDB(eOperationAdding, mStmtInsert, id, origin, aType, aPermission, aExpireType, aExpireTime, aModificationTime); } @@ -2948,4 +2940,3 @@ nsPermissionManager::FetchPermissions() { } return NS_OK; } - From 0755e26b0147c93d7c97ddb2eac1a65dd25202af Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Mon, 1 Feb 2016 19:48:15 +0000 Subject: [PATCH 26/91] Bug 1244774: Correct wchar_t/char16_t VS2015 compilation problem caused by patches for bug 1173371. r=jimm --- ipc/glue/GeckoChildProcessHost.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 109c196f313..f5c44c81147 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -637,12 +637,12 @@ AddContentSandboxAllowedFiles(int32_t aSandboxLevel, // Convert network share path to format for sandbox policy. if (Substring(binDirPath, 0, 2).Equals(L"\\\\")) { - binDirPath.InsertLiteral(L"??\\UNC", 1); + binDirPath.InsertLiteral(MOZ_UTF16("??\\UNC"), 1); } - binDirPath.AppendLiteral(L"\\*"); + binDirPath.AppendLiteral(MOZ_UTF16("\\*")); - aAllowedFilesRead.push_back(binDirPath.get()); + aAllowedFilesRead.push_back(std::wstring(binDirPath.get())); } #endif From 254d1333f9bcc909e42186b46481a940b7fb2eef Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Mon, 1 Feb 2016 13:49:08 -0500 Subject: [PATCH 27/91] Bug 1244720 - TaskCluster Linux64 mochitest devtools are running without --chunk-by-runtime. DONTBUILD. r=jmaher --- testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml | 2 +- .../taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml index 4f7c9bb5950..bc4bb795894 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt.yml @@ -18,7 +18,7 @@ task: total: 10 suite: name: mochitest - flavor: mochitest-devtools-chrome + flavor: mochitest-devtools-chrome-chunked treeherder: groupName: Desktop mochitests groupSymbol: tc-M diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml index f34f1381ac1..3148635ea46 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml @@ -21,7 +21,7 @@ task: total: 10 suite: name: mochitest - flavor: mochitest-devtools-chrome + flavor: mochitest-devtools-chrome-chunked treeherder: groupName: Desktop mochitests groupSymbol: tc-M-e10s From af5f79a39425066f570515e1a3d3d08f8381e58b Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 1 Feb 2016 12:29:40 -0800 Subject: [PATCH 28/91] Bug 1244122 P1 Always support SW intercept even when http cache is disabled. r=mayhemer --- netwerk/cache2/CacheStorage.cpp | 6 ++++-- netwerk/cache2/nsICacheStorage.idl | 6 ++++++ netwerk/protocol/http/nsHttpChannel.cpp | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/netwerk/cache2/CacheStorage.cpp b/netwerk/cache2/CacheStorage.cpp index b69a009e5b8..5630071cce9 100644 --- a/netwerk/cache2/CacheStorage.cpp +++ b/netwerk/cache2/CacheStorage.cpp @@ -48,12 +48,14 @@ NS_IMETHODIMP CacheStorage::AsyncOpenURI(nsIURI *aURI, if (!CacheStorageService::Self()) return NS_ERROR_NOT_INITIALIZED; - if (MOZ_UNLIKELY(!CacheObserver::UseDiskCache()) && mWriteToDisk) { + if (MOZ_UNLIKELY(!CacheObserver::UseDiskCache()) && mWriteToDisk && + !(aFlags & OPEN_INTERCEPTED)) { aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_NOT_AVAILABLE); return NS_OK; } - if (MOZ_UNLIKELY(!CacheObserver::UseMemoryCache()) && !mWriteToDisk) { + if (MOZ_UNLIKELY(!CacheObserver::UseMemoryCache()) && !mWriteToDisk && + !(aFlags & OPEN_INTERCEPTED)) { aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_NOT_AVAILABLE); return NS_OK; } diff --git a/netwerk/cache2/nsICacheStorage.idl b/netwerk/cache2/nsICacheStorage.idl index 49812a590da..f500a300b9e 100644 --- a/netwerk/cache2/nsICacheStorage.idl +++ b/netwerk/cache2/nsICacheStorage.idl @@ -56,6 +56,12 @@ interface nsICacheStorage : nsISupports */ const uint32_t OPEN_SECRETLY = 1 << 5; + /** + * Entry is being opened as part of a service worker interception. Do not + * allow the cache to be disabled in this case. + */ + const uint32_t OPEN_INTERCEPTED = 1 << 6; + /** * Asynchronously opens a cache entry for the specified URI. * Result is fetched asynchronously via the callback. diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 320f6b8fd6a..4f085cbfd8e 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -2939,6 +2939,7 @@ nsHttpChannel::OpenCacheEntry(bool isHttps) intercepted->NotifyController(); } else { if (mInterceptCache == INTERCEPTED) { + cacheEntryOpenFlags |= nsICacheStorage::OPEN_INTERCEPTED; DebugOnly exists; MOZ_ASSERT(NS_SUCCEEDED(cacheStorage->Exists(openURI, extension, &exists)) && exists, "The entry must exist in the cache after we create it here"); From 97a1a3f7e67333c8c2395a52843b277508d88cad Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Mon, 1 Feb 2016 12:29:40 -0800 Subject: [PATCH 29/91] Bug 1244122 P2 Perform refresh testing with http cache disabled. r=ehsan --- .../serviceworkers/browser_force_refresh.js | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/dom/workers/test/serviceworkers/browser_force_refresh.js b/dom/workers/test/serviceworkers/browser_force_refresh.js index 5f235f6f5fa..e8fc77653f9 100644 --- a/dom/workers/test/serviceworkers/browser_force_refresh.js +++ b/dom/workers/test/serviceworkers/browser_force_refresh.js @@ -30,7 +30,9 @@ function test() { ['dom.serviceWorkers.exemptFromPerDomainMax', true], ['dom.serviceWorkers.testing.enabled', true], ['dom.serviceWorkers.interception.enabled', true], - ['dom.caches.enabled', true]]}, + ['dom.caches.enabled', true], + ['browser.cache.disk.enable', false], + ['browser.cache.memory.enable', false]]}, function() { var url = gTestRoot + 'browser_base_force_refresh.html'; var tab = gBrowser.addTab(); @@ -39,24 +41,37 @@ function test() { tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); gBrowser.loadURI(url); + function done() { + tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler); + + gBrowser.removeTab(tab); + executeSoon(finish); + } + var cachedLoad = false; + var baseLoadCount = 0; function eventHandler(msg) { if (msg.data.type === 'base-load') { + baseLoadCount += 1; if (cachedLoad) { - tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler); - - gBrowser.removeTab(tab); - executeSoon(finish); + is(baseLoadCount, 2, 'cached load should occur before second base load'); + return done(); + } + if (baseLoadCount !== 1) { + ok(false, 'base load without cached load should only occur once'); + return done(); } } else if (msg.data.type === 'base-register') { ok(!cachedLoad, 'cached load should not occur before base register'); - refresh(); + is(baseLoadCount, 1, 'register should occur after first base load'); } else if (msg.data.type === 'base-sw-ready') { ok(!cachedLoad, 'cached load should not occur before base ready'); + is(baseLoadCount, 1, 'ready should occur after first base load'); refresh(); } else if (msg.data.type === 'cached-load') { ok(!cachedLoad, 'cached load should not occur twice'); + is(baseLoadCount, 1, 'cache load occur after first base load'); cachedLoad = true; forceRefresh(); } From 9ed57d651f34d2898b2d5cee0b8dfca0432e4456 Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Mon, 1 Feb 2016 12:31:28 -0800 Subject: [PATCH 30/91] Bug 1243448 - beta release source builder failing to run configure, NPOTB DONTBUILD r=callek --- browser/config/mozconfigs/linux64/source | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 browser/config/mozconfigs/linux64/source diff --git a/browser/config/mozconfigs/linux64/source b/browser/config/mozconfigs/linux64/source new file mode 100644 index 00000000000..4a3c6996b4a --- /dev/null +++ b/browser/config/mozconfigs/linux64/source @@ -0,0 +1,4 @@ +# The source "build" only needs a mozconfig because we use the build system as +# our script for generating it. This allows us to run configure without any +# extra dependencies on specific toolchains, e.g. gtk3. +ac_add_options --disable-compile-environment From 76a71db8075a50764db7961fcc33acc4f91bbe3e Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 28 Jan 2016 16:09:12 -0800 Subject: [PATCH 31/91] Bug 1079844 - Additional changes to use detachment terminology for ArrayBuffers rather than obsolete neutering terminology. r=bz --- dom/base/nsXMLHttpRequest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index 0ffc37f36f7..9501c6c917d 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -3803,8 +3803,8 @@ ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) } mMapPtr = nullptr; - // The memory-mapped contents will be released when obj been finalized(GCed - // or neutered). + // The memory-mapped contents will be released when the ArrayBuffer becomes + // detached or is GC'd. return obj; } From dcf86108f1c5be6ef0a69d4b5eb57dc8dbdb9c3b Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 1 Feb 2016 12:48:03 -0800 Subject: [PATCH 32/91] Bug 1216150 - Split xpc::InitGlobalObject into an options-setting component and a global-object-modifying component, with the options-setting component being called before global object creation in all callers. r=bz --- dom/base/nsGlobalWindow.cpp | 3 ++ dom/bindings/BindingUtils.h | 5 +++ js/xpconnect/src/nsXPConnect.cpp | 59 +++++++++++++++++++------------- js/xpconnect/src/xpcprivate.h | 19 ++++++++-- 4 files changed, 61 insertions(+), 25 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index f8553e2b7e2..4b7bbfb2f86 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2340,6 +2340,7 @@ CreateNativeGlobalForInner(JSContext* aCx, if (aNewInner->GetOuterWindow()) { top = aNewInner->GetTopInternal(); } + JS::CompartmentOptions options; // Sometimes add-ons load their own XUL windows, either as separate top-level @@ -2353,6 +2354,8 @@ CreateNativeGlobalForInner(JSContext* aCx, options.creationOptions().setSameZoneAs(top->GetGlobalJSObject()); } + xpc::InitGlobalObjectOptions(options, aPrincipal); + // Determine if we need the Components object. bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) || TreatAsRemoteXUL(aPrincipal); diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 758e0d53909..3ce76ae1c6a 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3023,6 +3023,11 @@ RegisterDOMNames(); // The return value is whatever the ProtoHandleGetter we used // returned. This should be the DOM prototype for the global. +// +// Typically this method's caller will want to ensure that +// xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is +// called after, this method, to ensure that this global object and its +// compartment are consistent with other global objects. template JS::Handle CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index cf5c614b01c..2ed0a81114b 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -389,12 +389,44 @@ CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal, return global; } +void +InitGlobalObjectOptions(JS::CompartmentOptions& aOptions, + nsIPrincipal* aPrincipal) +{ + bool shouldDiscardSystemSource = ShouldDiscardSystemSource(); + bool extraWarningsForSystemJS = ExtraWarningsForSystemJS(); + + bool isSystem = false; + if (shouldDiscardSystemSource || extraWarningsForSystemJS) + isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal); + + if (shouldDiscardSystemSource) { + bool discardSource = isSystem; + if (!discardSource) { + short status = aPrincipal->GetAppStatus(); + discardSource = status == nsIPrincipal::APP_STATUS_PRIVILEGED || + status == nsIPrincipal::APP_STATUS_CERTIFIED; + } + + aOptions.behaviors().setDiscardSource(discardSource); + } + + if (extraWarningsForSystemJS) { + if (isSystem) + aOptions.behaviors().extraWarningsOverride().set(true); + } +} + bool InitGlobalObject(JSContext* aJSContext, JS::Handle aGlobal, uint32_t aFlags) { - // Immediately enter the global's compartment, so that everything else we - // create ends up there. + // Immediately enter the global's compartment so that everything we create + // ends up there. JSAutoCompartment ac(aJSContext, aGlobal); + + // Stuff coming through this path always ends up as a DOM global. + MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL); + if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) { // XPCCallContext gives us an active request needed to save/restore. if (!CompartmentPrivate::Get(aGlobal)->scope->AttachComponentsObject(aJSContext) || @@ -403,27 +435,6 @@ InitGlobalObject(JSContext* aJSContext, JS::Handle aGlobal, uint32_t } } - if (ShouldDiscardSystemSource()) { - nsIPrincipal* prin = GetObjectPrincipal(aGlobal); - bool isSystem = nsContentUtils::IsSystemPrincipal(prin); - if (!isSystem) { - short status = prin->GetAppStatus(); - isSystem = status == nsIPrincipal::APP_STATUS_PRIVILEGED || - status == nsIPrincipal::APP_STATUS_CERTIFIED; - } - JS::CompartmentBehaviorsRef(aGlobal).setDiscardSource(isSystem); - } - - if (ExtraWarningsForSystemJS()) { - nsIPrincipal* prin = GetObjectPrincipal(aGlobal); - bool isSystem = nsContentUtils::IsSystemPrincipal(prin); - if (isSystem) - JS::CompartmentBehaviorsRef(aGlobal).extraWarningsOverride().set(true); - } - - // Stuff coming through this path always ends up as a DOM global. - MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL); - if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK)) JS_FireOnNewGlobalObject(aJSContext, aGlobal); @@ -448,6 +459,8 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, // we need to have a principal. MOZ_ASSERT(aPrincipal); + InitGlobalObjectOptions(aOptions, aPrincipal); + // Call into XPCWrappedNative to make a new global object, scope, and global // prototype. xpcObjectHelper helper(aCOMObj); diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index e87c71ba2c6..9924943b47b 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3591,8 +3591,23 @@ JSObject* CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal, JS::CompartmentOptions& aOptions); -// InitGlobalObject enters the compartment of aGlobal, so it doesn't matter what -// compartment aJSContext is in. +// Modify the provided compartment options, consistent with |aPrincipal| and +// with globally-cached values of various preferences. +// +// Call this function *before* |aOptions| is used to create the corresponding +// global object, as not all of the options it sets can be modified on an +// existing global object. (The type system should make this obvious, because +// you can't get a *mutable* JS::CompartmentOptions& from an existing global +// object.) +void +InitGlobalObjectOptions(JS::CompartmentOptions& aOptions, + nsIPrincipal* aPrincipal); + +// Finish initializing an already-created, not-yet-exposed-to-script global +// object. This will attach a Components object (if necessary) and call +// |JS_FireOnNewGlobalObject| (if necessary). +// +// If you must modify compartment options, see InitGlobalObjectOptions above. bool InitGlobalObject(JSContext* aJSContext, JS::Handle aGlobal, uint32_t aFlags); From 51da5a7f7bd141624c4e5142c07f3157c0132382 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 1 Feb 2016 12:48:58 -0800 Subject: [PATCH 33/91] Bug 1216150 - Turn on the experimental Intl.DateTimeFormat.prototype.formatToParts in b2g certified apps. r=fabrice --- dom/workers/WorkerScope.cpp | 3 +++ js/xpconnect/src/nsXPConnect.cpp | 17 +++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index c8caac8e880..793d75621cc 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -437,8 +437,11 @@ DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx, behaviors.setDiscardSource(discardSource) .extraWarningsOverride().set(extraWarnings); + const bool inCertifiedApp = mWorkerPrivate->IsInCertifiedApp(); + JS::CompartmentCreationOptions& creationOptions = options.creationOptions(); creationOptions.setSharedMemoryAndAtomicsEnabled(xpc::SharedMemoryEnabled()); + .setExperimentalDateTimeFormatFormatToPartsEnabled(inCertifiedApp); return DedicatedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, options, diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 2ed0a81114b..2342a49e234 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -400,13 +400,18 @@ InitGlobalObjectOptions(JS::CompartmentOptions& aOptions, if (shouldDiscardSystemSource || extraWarningsForSystemJS) isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal); + short status = aPrincipal->GetAppStatus(); + + // Enable the ECMA-402 experimental formatToParts in certified apps. + if (status == nsIPrincipal::APP_STATUS_CERTIFIED) { + aOptions.creationOptions() + .setExperimentalDateTimeFormatFormatToPartsEnabled(true); + } + if (shouldDiscardSystemSource) { - bool discardSource = isSystem; - if (!discardSource) { - short status = aPrincipal->GetAppStatus(); - discardSource = status == nsIPrincipal::APP_STATUS_PRIVILEGED || - status == nsIPrincipal::APP_STATUS_CERTIFIED; - } + bool discardSource = isSystem || + (status == nsIPrincipal::APP_STATUS_PRIVILEGED || + status == nsIPrincipal::APP_STATUS_CERTIFIED); aOptions.behaviors().setDiscardSource(discardSource); } From 851cfc945accbae651926886074145f9389dece5 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 1 Feb 2016 20:56:43 +0000 Subject: [PATCH 34/91] Bug 1244782 - Removed non-used variables in Directory.cpp, r=smaug --- dom/filesystem/Directory.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index 93c882ffebc..999e3930200 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -250,15 +250,13 @@ Directory::GetPath(nsAString& aRetval) const already_AddRefed Directory::GetFilesAndDirectories() { - nsresult error = NS_OK; - nsString realPath; ErrorResult rv; RefPtr task = new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv); if (NS_WARN_IF(rv.Failed())) { return nullptr; } - task->SetError(error); + FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); } From 80805376be03f3165ef92553a3b6751a3646ba09 Mon Sep 17 00:00:00 2001 From: Mark Goodwin Date: Mon, 1 Feb 2016 11:18:50 +0000 Subject: [PATCH 35/91] Bug 1241455 Send TLS Error Reports for subresources r=past, Gijs, mcmanus This patch makes use of the security reporter component (which hasn't landed yet - see blockers) to allow automatic TLS error reports to be sent directly from nsHttpChannel.cpp rather than sending them from browser.js. This allows failed connections for subresources to be reported. Some of the report sending from browser.js was retained to allow reports to be sent at the time a user enables reporting. This has been modified to also make use of the component. Since the patient is on the table, I've also taken the opportunity to remove the retry and status bits from aboutCertError.xhtml and aboutNetError.xhtml - which removes a bunch of code and simplifies things a bit. The mochitests have been modified to cope with the fact that the UI does not update with report sending / report failures - instead, success and failure are determined by examining the response seen from the server from within the test. --- browser/base/content/aboutNetError.xhtml | 18 -- .../aboutcerterror/aboutCertError.xhtml | 13 -- browser/base/content/browser.js | 91 +------- browser/base/content/content.js | 202 +++++------------- .../test/general/browser_ssl_error_reports.js | 80 ++----- .../test/general/ssl_error_reports.sjs | 2 +- .../en-US/chrome/browser/aboutCertError.dtd | 3 - .../en-US/chrome/overrides/netError.dtd | 3 - netwerk/protocol/http/nsHttpChannel.cpp | 59 +++++ netwerk/protocol/http/nsHttpChannel.h | 5 + 10 files changed, 147 insertions(+), 329 deletions(-) diff --git a/browser/base/content/aboutNetError.xhtml b/browser/base/content/aboutNetError.xhtml index 6b88ff4c335..6b2e974ce81 100644 --- a/browser/base/content/aboutNetError.xhtml +++ b/browser/base/content/aboutNetError.xhtml @@ -144,12 +144,6 @@ } } - function sendErrorReport() { - var event = new CustomEvent("AboutNetErrorSendReport", {bubbles:true}); - - document.dispatchEvent(event); - } - function initPage() { var err = getErrorCode(); @@ -260,13 +254,7 @@ var event = new CustomEvent("AboutNetErrorSetAutomatic", {bubbles:true, detail:evt.target.checked}); document.dispatchEvent(event); - if (evt.target.checked) { - sendErrorReport(); - } }, false); - - var retryBtn = document.getElementById('reportCertificateErrorRetry'); - retryBtn.addEventListener('click', sendErrorReport, false); } } if (getErrorCode() == "weakCryptoUsed" || getErrorCode() == "sslv3Used") { @@ -527,12 +515,6 @@

- - - - &errorReporting.sending; - &errorReporting.sent; -

diff --git a/browser/base/content/aboutcerterror/aboutCertError.xhtml b/browser/base/content/aboutcerterror/aboutCertError.xhtml index 8afcd27ec68..1027e2eda77 100644 --- a/browser/base/content/aboutcerterror/aboutCertError.xhtml +++ b/browser/base/content/aboutcerterror/aboutCertError.xhtml @@ -96,13 +96,6 @@ })); }); - var retryBtn = document.getElementById("reportCertificateErrorRetry"); - retryBtn.addEventListener("click", function () { - document.dispatchEvent(new CustomEvent("AboutCertErrorSendReport", { - bubbles: true - })); - }); - addEventListener("AboutCertErrorOptions", function (event) { var options = JSON.parse(event.detail); if (options && options.enabled) { @@ -287,12 +280,6 @@

- - - - &errorReporting.sending; - &errorReporting.sent; -

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index bfa9993ee79..0fb32e358f6 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2711,100 +2711,23 @@ var BrowserOnClick = { }, onSSLErrorReport: function(browser, documentURI, location, securityInfo) { - function showReportStatus(reportStatus) { - gBrowser.selectedBrowser - .messageManager - .sendAsyncMessage("Browser:SSLErrorReportStatus", - { - reportStatus: reportStatus, - documentURI: documentURI - }); - } - if (!Services.prefs.getBoolPref("security.ssl.errorReporting.enabled")) { - showReportStatus("error"); Cu.reportError("User requested certificate error report sending, but certificate error reporting is disabled"); return; } - let bin = TLS_ERROR_REPORT_TELEMETRY_MANUAL_SEND; - if (Services.prefs.getBoolPref("security.ssl.errorReporting.automatic")) { - bin = TLS_ERROR_REPORT_TELEMETRY_AUTO_SEND; - } - Services.telemetry.getHistogramById("TLS_ERROR_REPORT_UI").add(bin); - let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] .getService(Ci.nsISerializationHelper); let transportSecurityInfo = serhelper.deserializeObject(securityInfo); transportSecurityInfo.QueryInterface(Ci.nsITransportSecurityInfo) - showReportStatus("activity"); - - /* - * Requested info for the report: - * - Domain of bad connection - * - Error type (e.g. Pinning, domain mismatch, etc) - * - Cert chain (at minimum, same data to distrust each cert in the - * chain) - * - Request data (e.g. User Agent, IP, Timestamp) - * - * The request data should be added to the report by the receiving server. - */ - - // Convert the nsIX509CertList into a format that can be parsed into - // JSON - let asciiCertChain = []; - - if (transportSecurityInfo.failedCertChain) { - let certs = transportSecurityInfo.failedCertChain.getEnumerator(); - while (certs.hasMoreElements()) { - let cert = certs.getNext(); - cert.QueryInterface(Ci.nsIX509Cert); - asciiCertChain.push(btoa(getDERString(cert))); - } - } - - let report = { - hostname: location.hostname, - port: location.port, - timestamp: Math.round(Date.now() / 1000), - errorCode: transportSecurityInfo.errorCode, - failedCertChain: asciiCertChain, - userAgent: window.navigator.userAgent, - version: 1, - build: gAppInfo.appBuildID, - product: gAppInfo.name, - channel: UpdateUtils.UpdateChannel - } - - let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url"); - - let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - try { - xhr.open("POST", reportURL); - } catch (e) { - Cu.reportError("xhr.open exception", e); - showReportStatus("error"); - } - - xhr.onerror = function (e) { - // error making request to reportURL - Cu.reportError("xhr onerror", e); - showReportStatus("error"); - }; - - xhr.onload = function (event) { - if (xhr.status !== 201 && xhr.status !== 0) { - // request returned non-success status - Cu.reportError("xhr returned failure code", xhr.status); - showReportStatus("error"); - } else { - showReportStatus("complete"); - } - }; - - xhr.send(JSON.stringify(report)); + let errorReporter = Cc["@mozilla.org/securityreporter;1"] + .getService(Ci.nsISecurityReporter); + // if location.port is the empty string, set to -1 (for consistency with + // port values from nsIURI) + let port = location.port === "" ? -1 : location.port; + errorReporter.reportTLSError(transportSecurityInfo, + location.hostname, port); }, onAboutCertError: function (browser, elementId, isTopFrame, location, securityInfoAsString) { diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 13c8a369ab5..58c4f6e0843 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -210,10 +210,8 @@ const TLS_ERROR_REPORT_TELEMETRY_FAILURE = 7; var AboutCertErrorListener = { init(chromeGlobal) { addMessageListener("AboutCertErrorDetails", this); - addMessageListener("Browser:SSLErrorReportStatus", this); chromeGlobal.addEventListener("AboutCertErrorLoad", this, false, true); chromeGlobal.addEventListener("AboutCertErrorSetAutomatic", this, false, true); - chromeGlobal.addEventListener("AboutCertErrorSendReport", this, false, true); }, get isAboutCertError() { @@ -232,9 +230,6 @@ var AboutCertErrorListener = { case "AboutCertErrorSetAutomatic": this.onSetAutomatic(event); break; - case "AboutCertErrorSendReport": - this.onSendReport(); - break; } }, @@ -247,9 +242,6 @@ var AboutCertErrorListener = { case "AboutCertErrorDetails": this.onDetails(msg); break; - case "Browser:SSLErrorReportStatus": - this.onReportStatus(msg); - break; } }, @@ -258,6 +250,11 @@ var AboutCertErrorListener = { let ownerDoc = originalTarget.ownerDocument; ClickEventHandler.onAboutCertError(originalTarget, ownerDoc); + // Set up the TLS Error Reporting UI - reports are sent automatically + // (from nsHttpChannel::OnStopRequest) if the user has previously enabled + // automatic sending of reports. The UI ensures that a report is sent + // for the certificate error currently displayed if the user enables it + // here. let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic"); content.dispatchEvent(new content.CustomEvent("AboutCertErrorOptions", { detail: JSON.stringify({ @@ -265,10 +262,6 @@ var AboutCertErrorListener = { automatic, }) })); - - if (automatic) { - this.onSendReport(); - } }, onDetails(msg) { @@ -277,68 +270,31 @@ var AboutCertErrorListener = { }, onSetAutomatic(event) { - if (event.detail) { - this.onSendReport(); - } - sendAsyncMessage("Browser:SetSSLErrorReportAuto", { automatic: event.detail }); - }, - onSendReport() { - let doc = content.document; - let location = doc.location.href; + // if we're enabling reports, send a report for this failure + if (event.detail) { + let doc = content.document; + let location = doc.location.href; - let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] - .getService(Ci.nsISerializationHelper); + let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] + .getService(Ci.nsISerializationHelper); - let serializable = docShell.failedChannel.securityInfo - .QueryInterface(Ci.nsITransportSecurityInfo) - .QueryInterface(Ci.nsISerializable); + let serializable = docShell.failedChannel.securityInfo + .QueryInterface(Ci.nsITransportSecurityInfo) + .QueryInterface(Ci.nsISerializable); - let serializedSecurityInfo = serhelper.serializeToString(serializable); + let serializedSecurityInfo = serhelper.serializeToString(serializable); - sendAsyncMessage("Browser:SendSSLErrorReport", { - documentURI: doc.documentURI, - location: {hostname: doc.location.hostname, port: doc.location.port}, - securityInfo: serializedSecurityInfo - }); - }, - - onReportStatus(msg) { - let doc = content.document; - if (doc.documentURI != msg.data.documentURI) { - return; + sendAsyncMessage("Browser:SendSSLErrorReport", { + documentURI: doc.documentURI, + location: {hostname: doc.location.hostname, port: doc.location.port}, + securityInfo: serializedSecurityInfo + }); } - - let reportSendingMsg = doc.getElementById("reportSendingMessage"); - let reportSentMsg = doc.getElementById("reportSentMessage"); - let retryBtn = doc.getElementById("reportCertificateErrorRetry"); - - switch (msg.data.reportStatus) { - case "activity": - // Hide the button that was just clicked - retryBtn.style.removeProperty("display"); - reportSentMsg.style.removeProperty("display"); - reportSendingMsg.style.display = "block"; - break; - case "error": - // show the retry button - retryBtn.style.display = "block"; - reportSendingMsg.style.removeProperty("display"); - sendAsyncMessage("Browser:SSLErrorReportTelemetry", - {reportStatus: TLS_ERROR_REPORT_TELEMETRY_FAILURE}); - break; - case "complete": - // Show a success indicator - reportSentMsg.style.display = "block"; - reportSendingMsg.style.removeProperty("display"); - sendAsyncMessage("Browser:SSLErrorReportTelemetry", - {reportStatus: TLS_ERROR_REPORT_TELEMETRY_SUCCESS}); - break; - } - } + }, }; AboutCertErrorListener.init(this); @@ -348,8 +304,6 @@ var AboutNetErrorListener = { init: function(chromeGlobal) { chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true); chromeGlobal.addEventListener('AboutNetErrorSetAutomatic', this, false, true); - chromeGlobal.addEventListener('AboutNetErrorSendReport', this, false, true); - chromeGlobal.addEventListener('AboutNetErrorUIExpanded', this, false, true); chromeGlobal.addEventListener('AboutNetErrorOverride', this, false, true); }, @@ -369,13 +323,6 @@ var AboutNetErrorListener = { case "AboutNetErrorSetAutomatic": this.onSetAutomatic(aEvent); break; - case "AboutNetErrorSendReport": - this.onSendReport(aEvent); - break; - case "AboutNetErrorUIExpanded": - sendAsyncMessage("Browser:SSLErrorReportTelemetry", - {reportStatus: TLS_ERROR_REPORT_TELEMETRY_EXPANDED}); - break; case "AboutNetErrorOverride": this.onOverride(aEvent); break; @@ -385,91 +332,46 @@ var AboutNetErrorListener = { onPageLoad: function(evt) { let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic"); content.dispatchEvent(new content.CustomEvent("AboutNetErrorOptions", { - detail: JSON.stringify({ - enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"), - automatic: automatic - }) - } - )); + detail: JSON.stringify({ + enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"), + automatic: automatic + }) + })); sendAsyncMessage("Browser:SSLErrorReportTelemetry", {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN}); - - if (automatic) { - this.onSendReport(evt); - } - // hide parts of the UI we don't need yet - let contentDoc = content.document; - - let reportSendingMsg = contentDoc.getElementById("reportSendingMessage"); - let reportSentMsg = contentDoc.getElementById("reportSentMessage"); - let retryBtn = contentDoc.getElementById("reportCertificateErrorRetry"); - reportSendingMsg.style.display = "none"; - reportSentMsg.style.display = "none"; - retryBtn.style.display = "none"; }, onSetAutomatic: function(evt) { sendAsyncMessage("Browser:SetSSLErrorReportAuto", { - automatic: evt.detail + automatic: evt.detail + }); + + // if we're enabling reports, send a report for this failure + if (evt.detail) { + let contentDoc = content.document; + + let location = contentDoc.location.href; + + let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] + .getService(Ci.nsISerializationHelper); + + let serializable = docShell.failedChannel.securityInfo + .QueryInterface(Ci.nsITransportSecurityInfo) + .QueryInterface(Ci.nsISerializable); + + let serializedSecurityInfo = serhelper.serializeToString(serializable); + + sendAsyncMessage("Browser:SendSSLErrorReport", { + documentURI: contentDoc.documentURI, + location: { + hostname: contentDoc.location.hostname, + port: contentDoc.location.port + }, + securityInfo: serializedSecurityInfo }); - }, - onSendReport: function(evt) { - let contentDoc = content.document; - - let reportSendingMsg = contentDoc.getElementById("reportSendingMessage"); - let reportSentMsg = contentDoc.getElementById("reportSentMessage"); - let retryBtn = contentDoc.getElementById("reportCertificateErrorRetry"); - - addMessageListener("Browser:SSLErrorReportStatus", function(message) { - // show and hide bits - but only if this is a message for the right - // document - we'll compare on document URI - if (contentDoc.documentURI === message.data.documentURI) { - switch(message.data.reportStatus) { - case "activity": - // Hide the button that was just clicked - retryBtn.style.display = "none"; - reportSentMsg.style.display = "none"; - reportSendingMsg.style.removeProperty("display"); - break; - case "error": - // show the retry button - retryBtn.style.removeProperty("display"); - reportSendingMsg.style.display = "none"; - sendAsyncMessage("Browser:SSLErrorReportTelemetry", - {reportStatus: TLS_ERROR_REPORT_TELEMETRY_FAILURE}); - break; - case "complete": - // Show a success indicator - reportSentMsg.style.removeProperty("display"); - reportSendingMsg.style.display = "none"; - sendAsyncMessage("Browser:SSLErrorReportTelemetry", - {reportStatus: TLS_ERROR_REPORT_TELEMETRY_SUCCESS}); - break; - } - } - }); - - let location = contentDoc.location.href; - - let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] - .getService(Ci.nsISerializationHelper); - - let serializable = docShell.failedChannel.securityInfo - .QueryInterface(Ci.nsITransportSecurityInfo) - .QueryInterface(Ci.nsISerializable); - - let serializedSecurityInfo = serhelper.serializeToString(serializable); - - sendAsyncMessage("Browser:SendSSLErrorReport", { - documentURI: contentDoc.documentURI, - location: { - hostname: contentDoc.location.hostname, - port: contentDoc.location.port - }, - securityInfo: serializedSecurityInfo - }); + } }, onOverride: function(evt) { diff --git a/browser/base/content/test/general/browser_ssl_error_reports.js b/browser/base/content/test/general/browser_ssl_error_reports.js index 266311088b2..d577c1b4256 100644 --- a/browser/base/content/test/general/browser_ssl_error_reports.js +++ b/browser/base/content/test/general/browser_ssl_error_reports.js @@ -28,13 +28,12 @@ registerCleanupFunction(() => { add_task(function* test_send_report_neterror() { yield testSendReportAutomatically(URL_BAD_CHAIN, "succeed", "neterror"); yield testSendReportAutomatically(URL_NO_CERT, "nocert", "neterror"); - yield testSendReportFailRetry(URL_NO_CERT, "nocert", "neterror"); yield testSetAutomatic(URL_NO_CERT, "nocert", "neterror"); }); + add_task(function* test_send_report_certerror() { yield testSendReportAutomatically(URL_BAD_CERT, "badcert", "certerror"); - yield testSendReportFailRetry(URL_BAD_CERT, "badcert", "certerror"); yield testSetAutomatic(URL_BAD_CERT, "badcert", "certerror"); }); @@ -65,10 +64,11 @@ function* testSendReportAutomatically(testURL, suffix, errorURISuffix) { let browser = tab.linkedBrowser; // Load the page and wait for the error report submission. - let promiseReport = createErrorReportPromise(browser); + let promiseStatus = createReportResponseStatusPromise(URL_REPORTS + suffix); browser.loadURI(testURL); - yield promiseReport; - ok(true, "SSL error report submitted successfully"); + + ok(!isErrorStatus(yield promiseStatus), + "SSL error report submitted successfully"); // Check that we loaded the right error page. yield checkErrorPage(browser, errorURISuffix); @@ -78,30 +78,6 @@ function* testSendReportAutomatically(testURL, suffix, errorURISuffix) { cleanup(); }; -function* testSendReportFailRetry(testURL, suffix, errorURISuffix) { - try { - yield testSendReportAutomatically(testURL, "error", errorURISuffix); - ok(false, "sending a report should have failed"); - } catch (err) { - ok(err, "saw a failure notification"); - } - - Services.prefs.setCharPref(PREF_REPORT_URL, URL_REPORTS + suffix); - - let browser = gBrowser.selectedBrowser; - let promiseReport = createErrorReportPromise(browser); - let promiseRetry = ContentTask.spawn(browser, null, function* () { - content.document.getElementById("reportCertificateErrorRetry").click(); - }); - - yield Promise.all([promiseReport, promiseRetry]); - ok(true, "SSL error report submitted successfully"); - - // Cleanup. - gBrowser.removeCurrentTab(); - cleanup(); -} - function* testSetAutomatic(testURL, suffix, errorURISuffix) { Services.prefs.setBoolPref(PREF_REPORT_ENABLED, true); Services.prefs.setBoolPref(PREF_REPORT_AUTOMATIC, false); @@ -118,15 +94,15 @@ function* testSetAutomatic(testURL, suffix, errorURISuffix) { // Check that we loaded the right error page. yield checkErrorPage(browser, errorURISuffix); + let statusPromise = createReportResponseStatusPromise(URL_REPORTS + suffix); + // Click the checkbox, enable automatic error reports. - let promiseReport = createErrorReportPromise(browser); yield ContentTask.spawn(browser, null, function* () { content.document.getElementById("automaticallyReportInFuture").click(); }); // Wait for the error report submission. - yield promiseReport; - ok(true, "SSL error report submitted successfully"); + yield statusPromise; let isAutomaticReportingEnabled = () => Services.prefs.getBoolPref(PREF_REPORT_AUTOMATIC); @@ -170,32 +146,22 @@ function* testSendReportDisabled(testURL, errorURISuffix) { gBrowser.removeTab(tab); } -function createErrorReportPromise(browser) { - return ContentTask.spawn(browser, null, function* () { - let type = "Browser:SSLErrorReportStatus"; - let active = false; +function isErrorStatus(status) { + return status < 200 || status >= 300; +} - yield new Promise((resolve, reject) => { - addMessageListener(type, function onReportStatus(message) { - switch (message.data.reportStatus) { - case "activity": - active = true; - break; - case "complete": - removeMessageListener(type, onReportStatus); - if (active) { - resolve(message.data.reportStatus); - } else { - reject("activity should be seen before success"); - } - break; - case "error": - removeMessageListener(type, onReportStatus); - reject("sending the report failed"); - break; - } - }); - }); +// use the observer service to see when a report is sent +function createReportResponseStatusPromise(expectedURI) { + return new Promise(resolve => { + let observer = (subject, topic, data) => { + subject.QueryInterface(Ci.nsIHttpChannel); + let requestURI = subject.URI.spec; + if (requestURI == expectedURI) { + Services.obs.removeObserver(observer, "http-on-examine-response"); + resolve(subject.responseStatus); + } + }; + Services.obs.addObserver(observer, "http-on-examine-response", false); }); } diff --git a/browser/base/content/test/general/ssl_error_reports.sjs b/browser/base/content/test/general/ssl_error_reports.sjs index 454d13b7f99..e2e5bafc040 100644 --- a/browser/base/content/test/general/ssl_error_reports.sjs +++ b/browser/base/content/test/general/ssl_error_reports.sjs @@ -85,7 +85,7 @@ function handleRequest(request, response) { break; default: response.setStatusLine("1.1", 500, "Server error"); - response.write("succeed, nocert or error expected"); + response.write("succeed, nocert or error expected (got " + request.queryString + ")"); break; } } diff --git a/browser/locales/en-US/chrome/browser/aboutCertError.dtd b/browser/locales/en-US/chrome/browser/aboutCertError.dtd index f021844a750..a23e45fd64d 100644 --- a/browser/locales/en-US/chrome/browser/aboutCertError.dtd +++ b/browser/locales/en-US/chrome/browser/aboutCertError.dtd @@ -36,6 +36,3 @@ you know there's a good reason why this site doesn't use trusted identification. - - - diff --git a/browser/locales/en-US/chrome/overrides/netError.dtd b/browser/locales/en-US/chrome/overrides/netError.dtd index 9a3d1d778cf..4ec74697006 100644 --- a/browser/locales/en-US/chrome/overrides/netError.dtd +++ b/browser/locales/en-US/chrome/overrides/netError.dtd @@ -206,9 +206,6 @@ functionality specific to firefox. --> - - -
  • Please contact the website owners to inform them of this problem.

"> diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 4f085cbfd8e..f311d95ce8b 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -19,6 +19,8 @@ #include "nsICaptivePortalService.h" #include "nsICryptoHash.h" #include "nsINetworkInterceptController.h" +#include "nsINSSErrorsService.h" +#include "nsISecurityReporter.h" #include "nsIStringBundle.h" #include "nsIStreamListenerTee.h" #include "nsISeekableStream.h" @@ -1323,6 +1325,56 @@ nsHttpChannel::ProcessSecurityHeaders() return NS_OK; } +/** + * Decide whether or not to send a security report and, if so, give the + * SecurityReporter the information required to send such a report. + */ +void +nsHttpChannel::ProcessSecurityReport(nsresult status) { + uint32_t errorClass; + nsCOMPtr errSvc = + do_GetService("@mozilla.org/nss_errors_service;1"); + // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is + // not in the set of errors covered by the NSS errors service. + nsresult rv = errSvc->GetErrorClass(status, &errorClass); + if (!NS_SUCCEEDED(rv)) { + return; + } + + // if the content was not loaded succesfully and we have security info, + // send a TLS error report - we must do this early as other parts of + // OnStopRequest can return early + bool reportingEnabled = + Preferences::GetBool("security.ssl.errorReporting.enabled"); + bool reportingAutomatic = + Preferences::GetBool("security.ssl.errorReporting.automatic"); + if (!mSecurityInfo || !reportingEnabled || !reportingAutomatic) { + return; + } + + nsCOMPtr secInfo = + do_QueryInterface(mSecurityInfo); + nsCOMPtr errorReporter = + do_GetService("@mozilla.org/securityreporter;1"); + + if (!secInfo || !mURI) { + return; + } + + nsAutoCString hostStr; + int32_t port; + rv = mURI->GetHost(hostStr); + if (!NS_SUCCEEDED(rv)) { + return; + } + + rv = mURI->GetPort(&port); + + if (NS_SUCCEEDED(rv)) { + errorReporter->ReportTLSError(secInfo, hostStr, port); + } +} + bool nsHttpChannel::IsHTTPS() { @@ -5765,6 +5817,13 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n", this, request, status)); + MOZ_ASSERT(NS_IsMainThread(), + "OnStopRequest should only be called from the main thread"); + + if (NS_FAILED(status)) { + ProcessSecurityReport(status); + } + if (mTimingEnabled && request == mCachePump) { mCacheReadEnd = TimeStamp::Now(); } diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index 3d0b5218835..d52e919cd65 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -365,6 +365,11 @@ private: */ nsresult ProcessSecurityHeaders(); + /** + * A function that will, if the feature is enabled, send security reports. + */ + void ProcessSecurityReport(nsresult status); + /** * A function to process a single security header (STS or PKP), assumes * some basic sanity checks have been applied to the channel. Called From f63b00f05af98e3e27289c2cf8475512730ccc65 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 1 Feb 2016 13:37:27 -0800 Subject: [PATCH 36/91] Bug 1216150 - Mini-bustage fix for something I think I unintentionally qref'd into the final patch. r=bustage in a CLOSED TREE --- dom/workers/WorkerScope.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 793d75621cc..ce4b8439df4 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -438,9 +438,10 @@ DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx, .extraWarningsOverride().set(extraWarnings); const bool inCertifiedApp = mWorkerPrivate->IsInCertifiedApp(); + const bool sharedMemoryEnabled = xpc::SharedMemoryEnabled(); JS::CompartmentCreationOptions& creationOptions = options.creationOptions(); - creationOptions.setSharedMemoryAndAtomicsEnabled(xpc::SharedMemoryEnabled()); + creationOptions.setSharedMemoryAndAtomicsEnabled(sharedMemoryEnabled) .setExperimentalDateTimeFormatFormatToPartsEnabled(inCertifiedApp); return DedicatedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, From 53bbeaa0f53516b90cd454588941d0aa5c9ee801 Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Mon, 1 Feb 2016 13:42:38 -0800 Subject: [PATCH 37/91] Bug 1219094 - releng - kill api-11 mozconfigs NPOTB DONTBUILD CLOSED TREE r=rail --- .../config/mozconfigs/android-api-11/debug | 18 ------------- .../mozconfigs/android-api-11/l10n-nightly | 24 ------------------ .../mozconfigs/android-api-11/l10n-release | 25 ------------------- .../config/mozconfigs/android-api-11/nightly | 20 --------------- .../config/mozconfigs/android-api-11/release | 16 ------------ 5 files changed, 103 deletions(-) delete mode 100644 mobile/android/config/mozconfigs/android-api-11/debug delete mode 100644 mobile/android/config/mozconfigs/android-api-11/l10n-nightly delete mode 100644 mobile/android/config/mozconfigs/android-api-11/l10n-release delete mode 100644 mobile/android/config/mozconfigs/android-api-11/nightly delete mode 100644 mobile/android/config/mozconfigs/android-api-11/release diff --git a/mobile/android/config/mozconfigs/android-api-11/debug b/mobile/android/config/mozconfigs/android-api-11/debug deleted file mode 100644 index 8ea71447535..00000000000 --- a/mobile/android/config/mozconfigs/android-api-11/debug +++ /dev/null @@ -1,18 +0,0 @@ -. "$topsrcdir/mobile/android/config/mozconfigs/common" - -# Global options -ac_add_options --enable-debug - -# Android -ac_add_options --with-android-min-sdk=15 -ac_add_options --target=arm-linux-androideabi - -export MOZILLA_OFFICIAL=1 -export MOZ_TELEMETRY_REPORTING=1 - -ac_add_options --with-branding=mobile/android/branding/nightly - -# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS). -ac_add_options --enable-warnings-as-errors - -. "$topsrcdir/mobile/android/config/mozconfigs/common.override" diff --git a/mobile/android/config/mozconfigs/android-api-11/l10n-nightly b/mobile/android/config/mozconfigs/android-api-11/l10n-nightly deleted file mode 100644 index db5a289789f..00000000000 --- a/mobile/android/config/mozconfigs/android-api-11/l10n-nightly +++ /dev/null @@ -1,24 +0,0 @@ -. "$topsrcdir/mobile/android/config/mozconfigs/common" - -# L10n -ac_add_options --with-l10n-base=../../l10n-central - -# Global options -ac_add_options --disable-tests - -# Android -ac_add_options --with-android-min-sdk=15 -ac_add_options --target=arm-linux-androideabi - -ac_add_options --with-system-zlib -ac_add_options --enable-updater -ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} - -export MOZILLA_OFFICIAL=1 -export MOZ_DISABLE_GECKOVIEW=1 - -ac_add_options --with-branding=mobile/android/branding/nightly - -ac_add_options --disable-stdcxx-compat - -. "$topsrcdir/mobile/android/config/mozconfigs/common.override" diff --git a/mobile/android/config/mozconfigs/android-api-11/l10n-release b/mobile/android/config/mozconfigs/android-api-11/l10n-release deleted file mode 100644 index f3c1a8a3c1d..00000000000 --- a/mobile/android/config/mozconfigs/android-api-11/l10n-release +++ /dev/null @@ -1,25 +0,0 @@ -. "$topsrcdir/mobile/android/config/mozconfigs/common" - -# L10n -ac_add_options --with-l10n-base=.. - -# Global options -ac_add_options --disable-tests - -# Android -ac_add_options --with-android-min-sdk=15 -ac_add_options --target=arm-linux-androideabi - -ac_add_options --with-system-zlib -ac_add_options --enable-updater -ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} - -export MOZILLA_OFFICIAL=1 -export MOZ_DISABLE_GECKOVIEW=1 - -ac_add_options --enable-official-branding -ac_add_options --with-branding=mobile/android/branding/beta - -ac_add_options --disable-stdcxx-compat - -. "$topsrcdir/mobile/android/config/mozconfigs/common.override" diff --git a/mobile/android/config/mozconfigs/android-api-11/nightly b/mobile/android/config/mozconfigs/android-api-11/nightly deleted file mode 100644 index 44814385555..00000000000 --- a/mobile/android/config/mozconfigs/android-api-11/nightly +++ /dev/null @@ -1,20 +0,0 @@ -. "$topsrcdir/mobile/android/config/mozconfigs/common" - -ac_add_options --enable-profiling - -# Android -ac_add_options --with-android-min-sdk=15 -ac_add_options --target=arm-linux-androideabi - -ac_add_options --with-branding=mobile/android/branding/nightly - -# This will overwrite the default of stripping everything and keep the symbol table. -# This is useful for profiling with eideticker. See bug 788680 -STRIP_FLAGS="--strip-debug" - -export MOZILLA_OFFICIAL=1 -export MOZ_TELEMETRY_REPORTING=1 - -MOZ_ANDROID_GECKOLIBS_AAR=1 - -. "$topsrcdir/mobile/android/config/mozconfigs/common.override" diff --git a/mobile/android/config/mozconfigs/android-api-11/release b/mobile/android/config/mozconfigs/android-api-11/release deleted file mode 100644 index 5d3fb3a8b13..00000000000 --- a/mobile/android/config/mozconfigs/android-api-11/release +++ /dev/null @@ -1,16 +0,0 @@ -. "$topsrcdir/mobile/android/config/mozconfigs/common" - -# Android -ac_add_options --with-android-min-sdk=15 -ac_add_options --target=arm-linux-androideabi - -ac_add_options --with-branding=mobile/android/branding/beta - -ac_add_options --enable-updater - -export MOZILLA_OFFICIAL=1 -export MOZ_TELEMETRY_REPORTING=1 - -ac_add_options --enable-official-branding - -. "$topsrcdir/mobile/android/config/mozconfigs/common.override" From 7a6d5c1bad0784d41fcb8509a1787343a2a6e776 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 1 Feb 2016 21:48:04 +0000 Subject: [PATCH 38/91] Bug 1243881 - patch 1 - unship performance.translateTime, r=bz --- dom/base/nsPerformance.cpp | 34 --------- dom/base/nsPerformance.h | 6 -- dom/base/test/empty_worker.js | 1 - dom/base/test/mochitest.ini | 2 - dom/base/test/test_performance_translate.html | 75 ------------------- dom/webidl/Performance.webidl | 3 - dom/workers/ServiceWorker.cpp | 7 -- dom/workers/ServiceWorker.h | 3 - dom/workers/ServiceWorkerPrivate.h | 6 -- 9 files changed, 137 deletions(-) delete mode 100644 dom/base/test/empty_worker.js delete mode 100644 dom/base/test/test_performance_translate.html diff --git a/dom/base/nsPerformance.cpp b/dom/base/nsPerformance.cpp index 27b7f2086e2..3f4160744af 100644 --- a/dom/base/nsPerformance.cpp +++ b/dom/base/nsPerformance.cpp @@ -11,7 +11,6 @@ #include "nsDOMNavigationTiming.h" #include "nsContentUtils.h" #include "nsIScriptSecurityManager.h" -#include "nsGlobalWindow.h" #include "nsIDOMWindow.h" #include "nsILoadInfo.h" #include "nsIURI.h" @@ -30,8 +29,6 @@ #include "mozilla/Preferences.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/TimeStamp.h" -#include "SharedWorker.h" -#include "ServiceWorker.h" #include "js/HeapAPI.h" #include "GeckoProfiler.h" #include "WorkerPrivate.h" @@ -918,37 +915,6 @@ PerformanceBase::ClearResourceTimings() mResourceEntries.Clear(); } -DOMHighResTimeStamp -PerformanceBase::TranslateTime(DOMHighResTimeStamp aTime, - const WindowOrWorkerOrSharedWorkerOrServiceWorker& aTimeSource, - ErrorResult& aRv) -{ - TimeStamp otherCreationTimeStamp; - - if (aTimeSource.IsWindow()) { - RefPtr performance = aTimeSource.GetAsWindow().GetPerformance(); - if (NS_WARN_IF(!performance)) { - aRv.Throw(NS_ERROR_FAILURE); - } - otherCreationTimeStamp = performance->CreationTimeStamp(); - } else if (aTimeSource.IsWorker()) { - otherCreationTimeStamp = aTimeSource.GetAsWorker().CreationTimeStamp(); - } else if (aTimeSource.IsSharedWorker()) { - SharedWorker& sharedWorker = aTimeSource.GetAsSharedWorker(); - WorkerPrivate* workerPrivate = sharedWorker.GetWorkerPrivate(); - otherCreationTimeStamp = workerPrivate->CreationTimeStamp(); - } else if (aTimeSource.IsServiceWorker()) { - ServiceWorker& serviceWorker = aTimeSource.GetAsServiceWorker(); - WorkerPrivate* workerPrivate = serviceWorker.GetWorkerPrivate(); - otherCreationTimeStamp = workerPrivate->CreationTimeStamp(); - } else { - MOZ_CRASH("This should not be possible."); - } - - return RoundTime( - aTime + (otherCreationTimeStamp - CreationTimeStamp()).ToMilliseconds()); -} - DOMHighResTimeStamp PerformanceBase::RoundTime(double aTime) const { diff --git a/dom/base/nsPerformance.h b/dom/base/nsPerformance.h index 8c94534bd21..161e3425d96 100644 --- a/dom/base/nsPerformance.h +++ b/dom/base/nsPerformance.h @@ -30,7 +30,6 @@ namespace dom { class PerformanceEntry; class PerformanceObserver; -class WindowOrWorkerOrSharedWorkerOrServiceWorker; } // namespace dom } // namespace mozilla @@ -318,11 +317,6 @@ public: virtual DOMHighResTimeStamp Now() const = 0; - DOMHighResTimeStamp - TranslateTime(DOMHighResTimeStamp aTime, - const mozilla::dom::WindowOrWorkerOrSharedWorkerOrServiceWorker& aTimeSource, - mozilla::ErrorResult& aRv); - void Mark(const nsAString& aName, mozilla::ErrorResult& aRv); void ClearMarks(const mozilla::dom::Optional& aName); void Measure(const nsAString& aName, diff --git a/dom/base/test/empty_worker.js b/dom/base/test/empty_worker.js deleted file mode 100644 index 3053583c761..00000000000 --- a/dom/base/test/empty_worker.js +++ /dev/null @@ -1 +0,0 @@ -/* nothing here */ diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index c593544fe0d..ab94ee2bee6 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -257,7 +257,6 @@ support-files = file_explicit_user_agent.sjs referrer_change_server.sjs file_change_policy_redirect.html - empty_worker.js file_bug1198095.js [test_anonymousContent_api.html] @@ -868,7 +867,6 @@ skip-if = e10s || os != 'linux' || buildapp != 'browser' [test_change_policy.html] skip-if = buildapp == 'b2g' #no ssl support [test_document.all_iteration.html] -[test_performance_translate.html] [test_bug1198095.html] [test_bug1187157.html] [test_bug769117.html] diff --git a/dom/base/test/test_performance_translate.html b/dom/base/test/test_performance_translate.html deleted file mode 100644 index a2e14495fcc..00000000000 --- a/dom/base/test/test_performance_translate.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - Test for performance.translate() - - - - - -
-      
-    
- - diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl index 6df0dd2ae70..4b06f7e18b6 100644 --- a/dom/webidl/Performance.webidl +++ b/dom/webidl/Performance.webidl @@ -17,9 +17,6 @@ typedef sequence PerformanceEntryList; interface Performance { [DependsOn=DeviceState, Affects=Nothing] DOMHighResTimeStamp now(); - - [Throws] - DOMHighResTimeStamp translateTime(DOMHighResTimeStamp time, (Window or Worker or SharedWorker or ServiceWorker) timeSource); }; [Exposed=Window] diff --git a/dom/workers/ServiceWorker.cpp b/dom/workers/ServiceWorker.cpp index abbb3a8eb9d..c4be2a17ea6 100644 --- a/dom/workers/ServiceWorker.cpp +++ b/dom/workers/ServiceWorker.cpp @@ -101,13 +101,6 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle aMessage, aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo)); } -WorkerPrivate* -ServiceWorker::GetWorkerPrivate() const -{ - ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate(); - return workerPrivate->GetWorkerPrivate(); -} - } // namespace workers } // namespace dom } // namespace mozilla diff --git a/dom/workers/ServiceWorker.h b/dom/workers/ServiceWorker.h index 55b4f82f04a..323c4c48758 100644 --- a/dom/workers/ServiceWorker.h +++ b/dom/workers/ServiceWorker.h @@ -67,9 +67,6 @@ public: const Optional>& aTransferable, ErrorResult& aRv); - WorkerPrivate* - GetWorkerPrivate() const; - private: // This class can only be created from the ServiceWorkerManager. ServiceWorker(nsPIDOMWindowInner* aWindow, ServiceWorkerInfo* aInfo); diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 43db837c846..fc9ae46f1ff 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -126,12 +126,6 @@ public: void NoteStoppedControllingDocuments(); - WorkerPrivate* - GetWorkerPrivate() const - { - return mWorkerPrivate; - } - void Activated(); From 95f7a6a844729d82f9d6c4dee35c9b1671cfb3d5 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 1 Feb 2016 21:48:04 +0000 Subject: [PATCH 39/91] Bug 1243881 - patch 2 - unship performance.translateTime, r=bz --- dom/events/Event.cpp | 2 +- dom/events/test/test_eventTimeStamp.html | 5 ++--- dom/workers/Performance.cpp | 8 ++++---- dom/workers/WorkerPrivate.cpp | 14 ++++++++++++++ dom/workers/WorkerPrivate.h | 12 ++++++++++++ 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 385ceb7cd9f..90e10a6673a 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -1107,7 +1107,7 @@ Event::TimeStamp() const MOZ_ASSERT(workerPrivate); TimeDuration duration = - mEvent->timeStamp - workerPrivate->CreationTimeStamp(); + mEvent->timeStamp - workerPrivate->NowBaseTimeStamp(); return duration.ToMilliseconds(); } diff --git a/dom/events/test/test_eventTimeStamp.html b/dom/events/test/test_eventTimeStamp.html index edc2ed05d6b..a3d096432a1 100644 --- a/dom/events/test/test_eventTimeStamp.html +++ b/dom/events/test/test_eventTimeStamp.html @@ -74,9 +74,8 @@ function testWorkerEvents() { var worker = new Worker(window.URL.createObjectURL(blob)); worker.onmessage = function(evt) { var timeAfterEvent = window.performance.now(); - var time = window.performance.translateTime(evt.data, worker); - ok(time >= timeBeforeEvent && - time <= timeAfterEvent, + ok(evt.data > timeBeforeEvent && + evt.data < timeAfterEvent, "Event timestamp in dedicated worker (" + evt.data + ") is in expected range: (" + timeBeforeEvent + ", " + timeAfterEvent + ")"); diff --git a/dom/workers/Performance.cpp b/dom/workers/Performance.cpp index 50add8d0a45..f49a2553bcd 100644 --- a/dom/workers/Performance.cpp +++ b/dom/workers/Performance.cpp @@ -32,7 +32,7 @@ DOMHighResTimeStamp Performance::Now() const { TimeDuration duration = - TimeStamp::Now() - mWorkerPrivate->CreationTimeStamp(); + TimeStamp::Now() - mWorkerPrivate->NowBaseTimeStamp(); return RoundTime(duration.ToMilliseconds()); } @@ -52,7 +52,7 @@ Performance::GetPerformanceTimingFromString(const nsAString& aProperty) } if (aProperty.EqualsLiteral("navigationStart")) { - return mWorkerPrivate->CreationTime(); + return mWorkerPrivate->NowBaseTime(); } MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync"); @@ -77,13 +77,13 @@ Performance::InsertUserEntry(PerformanceEntry* aEntry) TimeStamp Performance::CreationTimeStamp() const { - return mWorkerPrivate->CreationTimeStamp(); + return mWorkerPrivate->NowBaseTimeStamp(); } DOMHighResTimeStamp Performance::CreationTime() const { - return mWorkerPrivate->CreationTime(); + return mWorkerPrivate->NowBaseTime(); } void diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 8988a4039b3..f6793730270 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2223,11 +2223,25 @@ WorkerPrivateParent::WorkerPrivateParent( aParent->CopyJSSettings(mJSSettings); MOZ_ASSERT(IsDedicatedWorker()); + mNowBaseTimeStamp = aParent->NowBaseTimeStamp(); + mNowBaseTimeHighRes = aParent->NowBaseTime(); } else { AssertIsOnMainThread(); RuntimeService::GetDefaultJSSettings(mJSSettings); + + if (IsDedicatedWorker() && mLoadInfo.mWindow && + mLoadInfo.mWindow->GetPerformance()) { + mNowBaseTimeStamp = mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()-> + GetNavigationStartTimeStamp(); + mNowBaseTimeHighRes = + mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()-> + GetNavigationStartHighRes(); + } else { + mNowBaseTimeStamp = CreationTimeStamp(); + mNowBaseTimeHighRes = CreationTime(); + } } } diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 09363394950..1a584104d00 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -192,6 +192,8 @@ private: WorkerType mWorkerType; TimeStamp mCreationTimeStamp; DOMHighResTimeStamp mCreationTimeHighRes; + TimeStamp mNowBaseTimeStamp; + DOMHighResTimeStamp mNowBaseTimeHighRes; protected: // The worker is owned by its thread, which is represented here. This is set @@ -553,6 +555,16 @@ public: return mCreationTimeHighRes; } + TimeStamp NowBaseTimeStamp() const + { + return mNowBaseTimeStamp; + } + + DOMHighResTimeStamp NowBaseTime() const + { + return mNowBaseTimeHighRes; + } + nsIPrincipal* GetPrincipal() const { From 5daad2ebcdd670e6ecb138c5766e8b76348c43be Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Mon, 1 Feb 2016 16:11:12 -0500 Subject: [PATCH 40/91] Bug 1244720 - Modifications to tier 2 TaskCluster jobs. DONTBUILD. r=jmaher * add Jetpack definition * e10s mochitests were not using --e10s flag [1] * disable mochitest e10s since they're not green anymore * increase Marionette max runtime [1] Unfortunately, we had two payload entries defined for e10s mochitests. That would cause the first paylod (with --e10s) to be ignored. --- .../taskcluster/tasks/branches/base_jobs.yml | 4 ---- .../tasks/tests/fx_linux64_jetpack.yml | 23 +++++++++++++++++++ .../tasks/tests/fx_linux64_marionette.yml | 1 + .../tests/fx_linux64_mochitest_bc_e10s.yml | 9 ++++---- .../tests/fx_linux64_mochitest_dt_e10s.yml | 9 ++++---- .../tests/fx_linux64_mochitest_plain_e10s.yml | 11 ++++----- .../taskcluster/tasks/tests/fx_test_base.yml | 1 + 7 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 testing/taskcluster/tasks/tests/fx_linux64_jetpack.yml diff --git a/testing/taskcluster/tasks/branches/base_jobs.yml b/testing/taskcluster/tasks/branches/base_jobs.yml index 0264bbef35c..7d7f5dd8d38 100644 --- a/testing/taskcluster/tasks/branches/base_jobs.yml +++ b/testing/taskcluster/tasks/branches/base_jobs.yml @@ -238,10 +238,6 @@ tests: task: tasks/tests/b2g_emulator_mochitest.yml tasks/builds/dbg_linux64.yml: task: tasks/tests/fx_linux64_mochitest_plain.yml - mochitest-e10s: - allowed_build_tasks: - tasks/builds/dbg_linux64.yml: - task: tasks/tests/fx_linux64_mochitest_plain_e10s.yml mochitest-a11y: allowed_build_tasks: tasks/builds/dbg_linux64.yml: diff --git a/testing/taskcluster/tasks/tests/fx_linux64_jetpack.yml b/testing/taskcluster/tasks/tests/fx_linux64_jetpack.yml new file mode 100644 index 00000000000..bc91bc608d2 --- /dev/null +++ b/testing/taskcluster/tasks/tests/fx_linux64_jetpack.yml @@ -0,0 +1,23 @@ +--- +$inherits: + from: 'tasks/tests/fx_desktop_unittest.yml' +task: + scopes: + - 'docker-worker:capability:device:loopbackVideo' + - 'docker-worker:capability:device:loopbackAudio' + metadata: + name: '[TC] Linux64 jetpack' + description: Mochitest jetpack + payload: + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true + extra: + suite: + name: mochitest + flavor: jetpack-package + treeherder: + groupName: Desktop mochitests + groupSymbol: tc-M + symbol: JP diff --git a/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml b/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml index 8e54927513d..e836f42ff2c 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_marionette.yml @@ -13,6 +13,7 @@ task: MOZHARNESS_CONFIG: > mozharness/configs/marionette/prod_config.py mozharness/configs/remove_executables.py + maxRunTime: 5400 metadata: name: '[TC] Linux64 marionette' description: Marionette unittest run diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml index 012cc1a1e6f..29e39071a50 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_bc_e10s.yml @@ -5,17 +5,16 @@ task: payload: command: - --e10s + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true scopes: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: name: '[TC] Linux64 mochitest-browser-chrome e10s M(bc{{chunk}})' description: Mochitest browser-chrome e10s run {{chunk}} - payload: - capabilities: - devices: - loopbackVideo: true - loopbackAudio: true extra: chunks: total: 7 diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml index 3148635ea46..87eeddad266 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_dt_e10s.yml @@ -5,17 +5,16 @@ task: payload: command: - --e10s + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true scopes: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: name: '[TC] Linux64 mochitest-devtools-chrome e10s M(dt{{chunk}})' description: Mochitest devtools-chrome e10s run {{chunk}} - payload: - capabilities: - devices: - loopbackVideo: true - loopbackAudio: true extra: chunks: total: 10 diff --git a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml index 967bfb8802e..a5e35d98c06 100644 --- a/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml +++ b/testing/taskcluster/tasks/tests/fx_linux64_mochitest_plain_e10s.yml @@ -5,18 +5,17 @@ task: payload: command: - --e10s + maxRunTime: 5400 + capabilities: + devices: + loopbackVideo: true + loopbackAudio: true scopes: - 'docker-worker:capability:device:loopbackVideo' - 'docker-worker:capability:device:loopbackAudio' metadata: name: '[TC] Linux64 mochitest-plain e10s {{chunk}}' description: Mochitest plain e10s run {{chunk}} - payload: - maxRunTime: 5400 - capabilities: - devices: - loopbackVideo: true - loopbackAudio: true extra: chunks: total: 10 diff --git a/testing/taskcluster/tasks/tests/fx_test_base.yml b/testing/taskcluster/tasks/tests/fx_test_base.yml index 40268c8c82d..bbc547ecc9f 100644 --- a/testing/taskcluster/tasks/tests/fx_test_base.yml +++ b/testing/taskcluster/tasks/tests/fx_test_base.yml @@ -22,6 +22,7 @@ task: allowPtrace: true extra: treeherder: + groupName: Desktop tests tier: 2 treeherderEnv: - production From 63486e77adae5c85b66cf76851386e7a8411955f Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Wed, 27 Jan 2016 09:31:25 -0800 Subject: [PATCH 41/91] Bug 1243758 - Add --browser-chrome and --chunk-by-runtime flags to browser-chrome-coverage test. r=chmanchester --- testing/mozharness/configs/unittests/linux_unittest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/mozharness/configs/unittests/linux_unittest.py b/testing/mozharness/configs/unittests/linux_unittest.py index 780bc48fea3..ade9385acea 100644 --- a/testing/mozharness/configs/unittests/linux_unittest.py +++ b/testing/mozharness/configs/unittests/linux_unittest.py @@ -196,7 +196,7 @@ config = { "browser-chrome": ["--browser-chrome"], "browser-chrome-chunked": ["--browser-chrome", "--chunk-by-runtime"], "browser-chrome-addons": ["--browser-chrome", "--chunk-by-runtime", "--tag=addons"], - "browser-chrome-coverage": ["--timeout=1200"], + "browser-chrome-coverage": ["--browser-chrome", "--chunk-by-runtime", "--timeout=1200"], "browser-chrome-screenshots": ["--browser-chrome", "--subsuite=screenshots"], "mochitest-gl": ["--subsuite=webgl"], "mochitest-devtools-chrome": ["--browser-chrome", "--subsuite=devtools"], From 1b9fea8752b6a1516330fc3ac0641b22b7ad3687 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 1 Feb 2016 11:23:09 -0800 Subject: [PATCH 42/91] Bug 1244166: Don't ignore stroke/fill properties in high-contrast mode, since doing so can produce icons that are invisible or whose colors are unrelated to the user's chosen high-contrast colors. r=longsonr --- layout/style/nsCSSPropList.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 097efda7c07..49b7bea8d3b 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -3960,8 +3960,7 @@ CSS_PROP_SVG( fill, fill, Fill, - CSS_PROPERTY_PARSE_FUNCTION | - CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + CSS_PROPERTY_PARSE_FUNCTION, "", 0, kContextPatternKTable, @@ -4218,8 +4217,7 @@ CSS_PROP_SVG( stroke, stroke, Stroke, - CSS_PROPERTY_PARSE_FUNCTION | - CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + CSS_PROPERTY_PARSE_FUNCTION, "", 0, kContextPatternKTable, From edc5537da672263da436013469938f68e4eba5c3 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Mon, 1 Feb 2016 17:05:45 -0500 Subject: [PATCH 43/91] Bug 1220502 - ignore not visible text nodes for tree update, r=tbsaunde, roc --- accessible/base/NotificationController.h | 11 +++++++++-- layout/generic/nsTextFrame.cpp | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/accessible/base/NotificationController.h b/accessible/base/NotificationController.h index 946b7964f0c..ab106fc00c1 100644 --- a/accessible/base/NotificationController.h +++ b/accessible/base/NotificationController.h @@ -122,8 +122,15 @@ public: */ inline void ScheduleTextUpdate(nsIContent* aTextNode) { - if (mTextHash.PutEntry(aTextNode)) - ScheduleProcessing(); + // Make sure we are not called with a node that is not in the DOM tree or + // not visible. + MOZ_ASSERT(aTextNode->GetParentNode(), "A text node is not in DOM"); + MOZ_ASSERT(aTextNode->GetPrimaryFrame(), "A text node doesn't have a frame"); + MOZ_ASSERT(aTextNode->GetPrimaryFrame()->StyleVisibility()->IsVisible(), + "A text node is not visible"); + + mTextHash.PutEntry(aTextNode); + ScheduleProcessing(); } /** diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 8010ecac81c..8ebe07807c2 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -8498,7 +8498,9 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, #ifdef ACCESSIBILITY // Schedule the update of accessible tree since rendered text might be changed. - ReflowTextA11yNotifier(presContext, mContent); + if (StyleVisibility()->IsVisible()) { + ReflowTextA11yNotifier(presContext, mContent); + } #endif ///////////////////////////////////////////////////////////////////// From c2efbdb7f216bebce27ecf11bacebb718d0c217b Mon Sep 17 00:00:00 2001 From: Kyle Machulis Date: Sat, 16 Jan 2016 12:21:43 -0800 Subject: [PATCH 44/91] Bug 1164427 - Implement elementsFromPoint; r=roc r=khuey --- dom/base/nsDocument.cpp | 76 +++++++++++++++---- dom/base/nsDocument.h | 8 +- dom/base/nsIDocument.h | 13 ++++ dom/webidl/Document.webidl | 2 +- .../cssom-view/elementsFromPoint.html.ini | 29 ------- 5 files changed, 83 insertions(+), 45 deletions(-) delete mode 100644 testing/web-platform/meta/cssom-view/elementsFromPoint.html.ini diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 4dd42523a26..679fcf0fdbe 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3282,15 +3282,39 @@ nsIDocument::ElementFromPoint(float aX, float aY) return ElementFromPointHelper(aX, aY, false, true); } +void +nsIDocument::ElementsFromPoint(float aX, float aY, + nsTArray>& aElements) +{ + ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements); +} + Element* nsDocument::ElementFromPointHelper(float aX, float aY, bool aIgnoreRootScrollFrame, bool aFlushLayout) { - // As per the the spec, we return null if either coord is negative - if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) { + nsAutoTArray, 1> elementArray; + ElementsFromPointHelper(aX, aY, + ((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) | + (aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) | + nsIDocument::IS_ELEMENT_FROM_POINT), + elementArray); + if (elementArray.IsEmpty()) { return nullptr; } + return elementArray[0]; +} + +void +nsDocument::ElementsFromPointHelper(float aX, float aY, + uint32_t aFlags, + nsTArray>& aElements) +{ + // As per the the spec, we return null if either coord is negative + if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) { + return; + } nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); @@ -3298,32 +3322,58 @@ nsDocument::ElementFromPointHelper(float aX, float aY, // Make sure the layout information we get is up-to-date, and // ensure we get a root frame (for everything but XUL) - if (aFlushLayout) + if (aFlags & nsIDocument::FLUSH_LAYOUT) { FlushPendingNotifications(Flush_Layout); + } nsIPresShell *ps = GetShell(); if (!ps) { - return nullptr; + return; } nsIFrame *rootFrame = ps->GetRootFrame(); // XUL docs, unlike HTML, have no frame tree until everything's done loading if (!rootFrame) { - return nullptr; // return null to premature XUL callers as a reminder to wait + return; // return null to premature XUL callers as a reminder to wait } - nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, + nsTArray outFrames; + // Emulate what GetFrameAtPoint does, since we want all the frames under our + // point. + nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames, nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | - (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); - if (!ptFrame) { - return nullptr; + ((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); + + // Dunno when this would ever happen, as we should at least have a root frame under us? + if (outFrames.IsEmpty()) { + return; } - nsIContent* elem = GetContentInThisDocument(ptFrame); - if (elem && !elem->IsElement()) { - elem = elem->GetParent(); + // Used to filter out repeated elements in sequence. + nsIContent* lastAdded = nullptr; + + for (uint32_t i = 0; i < outFrames.Length(); i++) { + nsIContent* node = GetContentInThisDocument(outFrames[i]); + + if (!node || !node->IsElement()) { + // If this helper is called via ElementsFromPoint, we need to make sure + // our frame is an element. Otherwise return whatever the top frame is + // even if it isn't the top-painted element. + if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) { + continue; + } + node = node->GetParent(); + } + if (node && node != lastAdded) { + aElements.AppendElement(node->AsElement()); + lastAdded = node; + // If this helper is called via ElementFromPoint, just return the first + // element we find. + if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) { + return; + } + } } - return elem ? elem->AsElement() : nullptr; } nsresult diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index f6b95ceb515..24e7da920c2 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1031,8 +1031,12 @@ public: const nsAString& aAttrValue) const override; virtual Element* ElementFromPointHelper(float aX, float aY, - bool aIgnoreRootScrollFrame, - bool aFlushLayout) override; + bool aIgnoreRootScrollFrame, + bool aFlushLayout) override; + + virtual void ElementsFromPointHelper(float aX, float aY, + uint32_t aFlags, + nsTArray>& aElements) override; virtual nsresult NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index a8baaec25b2..66ac703417a 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -1669,6 +1669,16 @@ public: bool aIgnoreRootScrollFrame, bool aFlushLayout) = 0; + enum ElementsFromPointFlags { + IGNORE_ROOT_SCROLL_FRAME = 1, + FLUSH_LAYOUT = 2, + IS_ELEMENT_FROM_POINT = 4 + }; + + virtual void ElementsFromPointHelper(float aX, float aY, + uint32_t aFlags, + nsTArray>& aElements) = 0; + virtual nsresult NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, float aBottomSize, float aLeftSize, @@ -2531,6 +2541,9 @@ public: virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0; virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0; Element* ElementFromPoint(float aX, float aY); + void ElementsFromPoint(float aX, + float aY, + nsTArray>& aElements); /** * Retrieve the location of the caret position (DOM node and character diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index c46aa662721..e6b1f3a965c 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -278,7 +278,7 @@ partial interface Document { // http://dev.w3.org/csswg/cssom-view/#extensions-to-the-document-interface partial interface Document { Element? elementFromPoint (float x, float y); - + sequence elementsFromPoint (float x, float y); CaretPosition? caretPositionFromPoint (float x, float y); [Pref="dom.document.scrollingElement.enabled"] diff --git a/testing/web-platform/meta/cssom-view/elementsFromPoint.html.ini b/testing/web-platform/meta/cssom-view/elementsFromPoint.html.ini deleted file mode 100644 index 6c410842ca3..00000000000 --- a/testing/web-platform/meta/cssom-view/elementsFromPoint.html.ini +++ /dev/null @@ -1,29 +0,0 @@ -[elementsFromPoint.html] - type: testharness - [Negative co-ordinates] - expected: FAIL - - [co-ordinates larger than the viewport] - expected: FAIL - - [co-ordinates larger than the viewport from in iframe] - expected: FAIL - - [Return first element that is the target for hit testing] - expected: FAIL - - [First element to get mouse events with pointer-events css] - expected: FAIL - - [SVG element at x,y] - expected: FAIL - - [transformed element at x,y] - expected: FAIL - - [no hit target at x,y] - expected: FAIL - - [No viewport available] - expected: FAIL - From be48f11ff518760334d1c53796d157701f07116c Mon Sep 17 00:00:00 2001 From: Kyle Machulis Date: Thu, 28 Jan 2016 12:35:57 -0800 Subject: [PATCH 45/91] Bug 1164427 - Add w-p-t for elementFromPoint/elementsFromPoint on div with negative margins; r=roc --- testing/web-platform/meta/MANIFEST.json | 11 ++++++- .../tests/cssom-view/negativeMargins.html | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 testing/web-platform/tests/cssom-view/negativeMargins.html diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index da115865086..c7c06998045 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -33713,7 +33713,16 @@ }, "local_changes": { "deleted": [], - "items": {}, + "items": { + "testharness": { + "cssom-view/negativeMargins.html": [ + { + "path": "cssom-view/negativeMargins.html", + "url": "/cssom-view/negativeMargins.html" + } + ] + } + }, "reftest_nodes": {} }, "reftest_nodes": { diff --git a/testing/web-platform/tests/cssom-view/negativeMargins.html b/testing/web-platform/tests/cssom-view/negativeMargins.html new file mode 100644 index 00000000000..910ef457950 --- /dev/null +++ b/testing/web-platform/tests/cssom-view/negativeMargins.html @@ -0,0 +1,32 @@ + + +cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins + + + +
+
+ Hello +
+ + From 668e130fb851b00476d92d11fdbb38d292d4d622 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:13 -0500 Subject: [PATCH 46/91] Bug 1243049 - Introduce new PrefsHelper implementation; r=snorp Introduce a new implementation for PrefsHelper that does not use events or rely on browser.js for getting and/or setting prefs. Also add an addObserver method to better match the removeObserver method. --- .../java/org/mozilla/gecko/PrefsHelper.java | 344 +++++++++++------- 1 file changed, 204 insertions(+), 140 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java b/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java index f87fbdb0b3d..fef35f7f321 100644 --- a/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java @@ -7,16 +7,14 @@ package org.mozilla.gecko; import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.annotation.WrapForJNI; -import org.mozilla.gecko.util.GeckoEventListener; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import android.support.v4.util.SimpleArrayMap; import android.util.Log; import android.util.SparseArray; import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; /** * Helper class to get/set gecko prefs. @@ -24,187 +22,253 @@ import java.util.ArrayList; public final class PrefsHelper { private static final String LOGTAG = "GeckoPrefsHelper"; - private static boolean sRegistered; - private static int sUniqueRequestId = 1; - static final SparseArray sCallbacks = new SparseArray(); + // Map pref name to ArrayList for multiple observers or PrefHandler for single observer. + private static final SimpleArrayMap OBSERVERS = new SimpleArrayMap<>(); - @WrapForJNI @RobocopTarget - /* package */ static native void getPrefsById(int requestId, String[] prefNames, boolean observe); - @WrapForJNI @RobocopTarget - /* package */ static native void removePrefsObserver(int requestId); + @WrapForJNI + private static final int PREF_INVALID = -1; + @WrapForJNI + private static final int PREF_FINISH = 0; + @WrapForJNI + private static final int PREF_BOOL = 1; + @WrapForJNI + private static final int PREF_INT = 2; + @WrapForJNI + private static final int PREF_STRING = 3; - public static int getPref(String prefName, PrefHandler callback) { - return getPrefsInternal(new String[] { prefName }, callback); - } + @WrapForJNI(stubName = "GetPrefs") + private static native void nativeGetPrefs(String[] prefNames, PrefHandler handler); + @WrapForJNI(stubName = "SetPref") + private static native void nativeSetPref(String prefName, boolean flush, int type, + boolean boolVal, int intVal, String strVal); + @WrapForJNI(stubName = "AddObserver") + private static native void nativeAddObserver(String[] prefNames, PrefHandler handler, + String[] prefsToObserve); + @WrapForJNI(stubName = "RemoveObserver") + private static native void nativeRemoveObserver(String[] prefToUnobserve); - public static int getPrefs(String[] prefNames, PrefHandler callback) { - return getPrefsInternal(prefNames, callback); - } - - public static int getPrefs(ArrayList prefNames, PrefHandler callback) { - return getPrefsInternal(prefNames.toArray(new String[prefNames.size()]), callback); - } - - private static int getPrefsInternal(String[] prefNames, PrefHandler callback) { - int requestId; - synchronized (PrefsHelper.class) { - ensureRegistered(); - - requestId = sUniqueRequestId++; - sCallbacks.put(requestId, callback); - } - - // Because we use JS to handle pref events, we need to wait until the RUNNING state. - // If we ever convert that to native code, we can switch to using the JNI_READY state. - if (GeckoThread.isStateAtLeast(GeckoThread.State.RUNNING)) { - getPrefsById(requestId, prefNames, callback.isObserver()); + @RobocopTarget + public static void getPrefs(final String[] prefNames, final PrefHandler callback) { + if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { + nativeGetPrefs(prefNames, callback); } else { GeckoThread.queueNativeCallUntil( - GeckoThread.State.RUNNING, PrefsHelper.class, "getPrefsById", - requestId, prefNames, callback.isObserver()); + GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeGetPrefs", + String[].class, prefNames, PrefHandler.class, callback); } - return requestId; } - private static void ensureRegistered() { - if (sRegistered) { + public static void getPref(final String prefName, final PrefHandler callback) { + getPrefs(new String[] { prefName }, callback); + } + + public static void getPrefs(final ArrayList prefNames, final PrefHandler callback) { + getPrefs(prefNames.toArray(new String[prefNames.size()]), callback); + } + + @RobocopTarget + public static void setPref(final String pref, final Object value, final boolean flush) { + final int type; + boolean boolVal = false; + int intVal = 0; + String strVal = null; + + if (value instanceof Boolean) { + type = PREF_BOOL; + boolVal = (Boolean) value; + } else if (value instanceof Integer) { + type = PREF_INT; + intVal = (Integer) value; + } else { + type = PREF_STRING; + strVal = String.valueOf(value); + } + + if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { + nativeSetPref(pref, flush, type, boolVal, intVal, strVal); + } else { + GeckoThread.queueNativeCallUntil( + GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeSetPref", + String.class, pref, flush, type, boolVal, intVal, String.class, strVal); + } + } + + public static void setPref(final String pref, final Object value) { + setPref(pref, value, /* flush */ false); + } + + @RobocopTarget + public synchronized static void addObserver(final String[] prefNames, + final PrefHandler handler) { + List prefsToObserve = null; + + for (String pref : prefNames) { + final Object existing = OBSERVERS.get(pref); + + if (existing == null) { + // Not observing yet, so add observer. + if (prefsToObserve == null) { + prefsToObserve = new ArrayList<>(prefNames.length); + } + prefsToObserve.add(pref); + OBSERVERS.put(pref, handler); + + } else if (existing instanceof PrefHandler) { + // Already observing one, so turn it into an array. + final List handlerList = new ArrayList<>(2); + handlerList.add((PrefHandler) existing); + handlerList.add(handler); + OBSERVERS.put(pref, handlerList); + + } else { + // Already observing multiple, so add to existing array. + @SuppressWarnings("unchecked") + final List handlerList = (List) existing; + handlerList.add(handler); + } + } + + final String[] namesToObserve = prefsToObserve == null ? null : + prefsToObserve.toArray(new String[prefsToObserve.size()]); + + if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { + nativeAddObserver(prefNames, handler, namesToObserve); + } else { + GeckoThread.queueNativeCallUntil( + GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeAddObserver", + String[].class, prefNames, PrefHandler.class, handler, + String[].class, namesToObserve); + } + } + + @RobocopTarget + public synchronized static void removeObserver(final PrefHandler handler) { + List prefsToUnobserve = null; + + for (int i = OBSERVERS.size() - 1; i >= 0; i--) { + final Object existing = OBSERVERS.valueAt(i); + boolean removeObserver = false; + + if (existing == handler) { + removeObserver = true; + + } else if (!(existing instanceof PrefHandler)) { + // Removing existing handler from list. + @SuppressWarnings("unchecked") + final List handlerList = (List) existing; + if (handlerList.remove(handler) && handlerList.isEmpty()) { + removeObserver = true; + } + } + + if (removeObserver) { + // Removed last handler, so remove observer. + if (prefsToUnobserve == null) { + prefsToUnobserve = new ArrayList<>(); + } + prefsToUnobserve.add(OBSERVERS.keyAt(i)); + OBSERVERS.removeAt(i); + } + } + + if (prefsToUnobserve == null) { return; } - GeckoEventListener listener = new GeckoEventListener() { - @Override - public void handleMessage(String event, JSONObject message) { - try { - PrefHandler callback; - synchronized (PrefsHelper.class) { - try { - int requestId = message.getInt("requestId"); - callback = sCallbacks.get(requestId); - if (callback != null && !callback.isObserver()) { - sCallbacks.delete(requestId); - } - } catch (Exception e) { - callback = null; - } - } + final String[] namesToUnobserve = + prefsToUnobserve.toArray(new String[prefsToUnobserve.size()]); - if (callback == null) { - Log.d(LOGTAG, "Preferences:Data message had an unknown requestId; ignoring"); - return; - } - - JSONArray jsonPrefs = message.getJSONArray("preferences"); - for (int i = 0; i < jsonPrefs.length(); i++) { - JSONObject pref = jsonPrefs.getJSONObject(i); - String name = pref.getString("name"); - String type = pref.getString("type"); - try { - if ("bool".equals(type)) { - callback.prefValue(name, pref.getBoolean("value")); - } else if ("int".equals(type)) { - callback.prefValue(name, pref.getInt("value")); - } else if ("string".equals(type)) { - callback.prefValue(name, pref.getString("value")); - } else { - Log.e(LOGTAG, "Unknown pref value type [" + type + "] for pref [" + name + "]"); - } - } catch (Exception e) { - Log.e(LOGTAG, "Handler for preference [" + name + "] threw exception", e); - } - } - callback.finish(); - } catch (Exception e) { - Log.e(LOGTAG, "Error handling Preferences:Data message", e); - } - } - }; - EventDispatcher.getInstance().registerGeckoThreadListener(listener, "Preferences:Data"); - sRegistered = true; - } - - public static void setPref(String pref, Object value) { - setPref(pref, value, false); - } - - public static void setPref(String pref, Object value, boolean flush) { - if (pref == null || pref.length() == 0) { - throw new IllegalArgumentException("Pref name must be non-empty"); - } - - try { - JSONObject jsonPref = new JSONObject(); - jsonPref.put("name", pref); - jsonPref.put("flush", flush); - - if (value instanceof Boolean) { - jsonPref.put("type", "bool"); - jsonPref.put("value", ((Boolean)value).booleanValue()); - } else if (value instanceof Integer) { - jsonPref.put("type", "int"); - jsonPref.put("value", ((Integer)value).intValue()); - } else { - jsonPref.put("type", "string"); - jsonPref.put("value", String.valueOf(value)); - } - - GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:Set", jsonPref.toString()); - GeckoAppShell.sendEventToGecko(event); - } catch (JSONException e) { - Log.e(LOGTAG, "Error setting pref [" + pref + "]", e); + if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { + nativeRemoveObserver(namesToUnobserve); + } else { + GeckoThread.queueNativeCallUntil( + GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeRemoveObserver", + String[].class, namesToUnobserve); } } - public static void removeObserver(int requestId) { - if (requestId < 0) { - throw new IllegalArgumentException("Invalid request ID"); + @WrapForJNI + private static void callPrefHandler(final PrefHandler handler, int type, final String pref, + boolean boolVal, int intVal, String strVal) { + switch (type) { + case PREF_FINISH: + handler.finish(); + return; + case PREF_BOOL: + handler.prefValue(pref, boolVal); + return; + case PREF_INT: + handler.prefValue(pref, intVal); + return; + case PREF_STRING: + handler.prefValue(pref, strVal); + return; + } + throw new IllegalArgumentException(); + } + + @WrapForJNI + private synchronized static void onPrefChange(final String pref, final int type, + final boolean boolVal, final int intVal, + final String strVal) { + final Object existing = OBSERVERS.get(pref); + + if (existing == null) { + return; } - synchronized (PrefsHelper.class) { - PrefHandler callback = sCallbacks.get(requestId); - sCallbacks.delete(requestId); + final Iterator itor; + PrefHandler handler; - if (callback == null) { - Log.e(LOGTAG, "Unknown request ID " + requestId); + if (existing instanceof PrefHandler) { + itor = null; + handler = (PrefHandler) existing; + } else { + @SuppressWarnings("unchecked") + final List handlerList = (List) existing; + if (handlerList.isEmpty()) { return; } + itor = handlerList.iterator(); + handler = itor.next(); } - GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:RemoveObserver", - Integer.toString(requestId)); - GeckoAppShell.sendEventToGecko(event); + do { + callPrefHandler(handler, type, pref, boolVal, intVal, strVal); + handler.finish(); + + handler = itor != null && itor.hasNext() ? itor.next() : null; + } while (handler != null); } public interface PrefHandler { void prefValue(String pref, boolean value); void prefValue(String pref, int value); void prefValue(String pref, String value); - boolean isObserver(); void finish(); } public static abstract class PrefHandlerBase implements PrefHandler { @Override public void prefValue(String pref, boolean value) { - Log.w(LOGTAG, "Unhandled boolean value for pref [" + pref + "]"); + throw new UnsupportedOperationException( + "Unhandled boolean pref " + pref + "; wrong type?"); } @Override public void prefValue(String pref, int value) { - Log.w(LOGTAG, "Unhandled int value for pref [" + pref + "]"); + throw new UnsupportedOperationException( + "Unhandled int pref " + pref + "; wrong type?"); } @Override public void prefValue(String pref, String value) { - Log.w(LOGTAG, "Unhandled String value for pref [" + pref + "]"); + throw new UnsupportedOperationException( + "Unhandled String pref " + pref + "; wrong type?"); } @Override public void finish() { } - - @Override - public boolean isObserver() { - return false; - } } } From d0a68f73f09cf61de8bcb54f84abd8aa595ca3a9 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:13 -0500 Subject: [PATCH 47/91] Bug 1243049 - Update native bindings for PrefsHelper; r=me Update auto-generated native bindings for PrefsHelper. --- widget/android/GeneratedJNINatives.h | 20 ++-- widget/android/GeneratedJNIWrappers.cpp | 30 +++++- widget/android/GeneratedJNIWrappers.h | 116 ++++++++++++++++++++++-- 3 files changed, 147 insertions(+), 19 deletions(-) diff --git a/widget/android/GeneratedJNINatives.h b/widget/android/GeneratedJNINatives.h index da06b5a88bd..805b59e759e 100644 --- a/widget/android/GeneratedJNINatives.h +++ b/widget/android/GeneratedJNINatives.h @@ -220,13 +220,21 @@ class PrefsHelper::Natives : public mozilla::jni::NativeImpl public: static constexpr JNINativeMethod methods[] = { - mozilla::jni::MakeNativeMethod( - mozilla::jni::NativeStub - ::template Wrap<&Impl::GetPrefsById>), + mozilla::jni::MakeNativeMethod( + mozilla::jni::NativeStub + ::template Wrap<&Impl::AddObserver>), - mozilla::jni::MakeNativeMethod( - mozilla::jni::NativeStub - ::template Wrap<&Impl::RemovePrefsObserver>) + mozilla::jni::MakeNativeMethod( + mozilla::jni::NativeStub + ::template Wrap<&Impl::GetPrefs>), + + mozilla::jni::MakeNativeMethod( + mozilla::jni::NativeStub + ::template Wrap<&Impl::RemoveObserver>), + + mozilla::jni::MakeNativeMethod( + mozilla::jni::NativeStub + ::template Wrap<&Impl::SetPref>) }; }; diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index bebd1b442ff..fa06d65fc1d 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -1118,11 +1118,33 @@ auto GeckoView::Window::GlController() const -> mozilla::jni::Object::LocalRef constexpr char PrefsHelper::name[]; -constexpr char PrefsHelper::GetPrefsById_t::name[]; -constexpr char PrefsHelper::GetPrefsById_t::signature[]; +constexpr char PrefsHelper::CallPrefHandler_t::name[]; +constexpr char PrefsHelper::CallPrefHandler_t::signature[]; -constexpr char PrefsHelper::RemovePrefsObserver_t::name[]; -constexpr char PrefsHelper::RemovePrefsObserver_t::signature[]; +auto PrefsHelper::CallPrefHandler(mozilla::jni::Object::Param a0, int32_t a1, mozilla::jni::String::Param a2, bool a3, int32_t a4, mozilla::jni::String::Param a5) -> void +{ + return mozilla::jni::Method::Call(nullptr, nullptr, a0, a1, a2, a3, a4, a5); +} + +constexpr char PrefsHelper::AddObserver_t::name[]; +constexpr char PrefsHelper::AddObserver_t::signature[]; + +constexpr char PrefsHelper::GetPrefs_t::name[]; +constexpr char PrefsHelper::GetPrefs_t::signature[]; + +constexpr char PrefsHelper::RemoveObserver_t::name[]; +constexpr char PrefsHelper::RemoveObserver_t::signature[]; + +constexpr char PrefsHelper::SetPref_t::name[]; +constexpr char PrefsHelper::SetPref_t::signature[]; + +constexpr char PrefsHelper::OnPrefChange_t::name[]; +constexpr char PrefsHelper::OnPrefChange_t::signature[]; + +auto PrefsHelper::OnPrefChange(mozilla::jni::String::Param a0, int32_t a1, bool a2, int32_t a3, mozilla::jni::String::Param a4) -> void +{ + return mozilla::jni::Method::Call(nullptr, nullptr, a0, a1, a2, a3, a4); +} constexpr char Restrictions::name[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index f95e923e68c..ad4db214fe5 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -3028,17 +3028,40 @@ protected: PrefsHelper(jobject instance) : Class(instance) {} public: - struct GetPrefsById_t { + struct CallPrefHandler_t { typedef PrefsHelper Owner; typedef void ReturnType; typedef void SetterType; typedef mozilla::jni::Args< + mozilla::jni::Object::Param, int32_t, - mozilla::jni::ObjectArray::Param, - bool> Args; - static constexpr char name[] = "getPrefsById"; + mozilla::jni::String::Param, + bool, + int32_t, + mozilla::jni::String::Param> Args; + static constexpr char name[] = "callPrefHandler"; static constexpr char signature[] = - "(I[Ljava/lang/String;Z)V"; + "(Lorg/mozilla/gecko/PrefsHelper$PrefHandler;ILjava/lang/String;ZILjava/lang/String;)V"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + static auto CallPrefHandler(mozilla::jni::Object::Param, int32_t, mozilla::jni::String::Param, bool, int32_t, mozilla::jni::String::Param) -> void; + +public: + struct AddObserver_t { + typedef PrefsHelper Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + mozilla::jni::ObjectArray::Param, + mozilla::jni::Object::Param, + mozilla::jni::ObjectArray::Param> Args; + static constexpr char name[] = "nativeAddObserver"; + static constexpr char signature[] = + "([Ljava/lang/String;Lorg/mozilla/gecko/PrefsHelper$PrefHandler;[Ljava/lang/String;)V"; static const bool isStatic = true; static const bool isMultithreaded = false; static const mozilla::jni::ExceptionMode exceptionMode = @@ -3046,21 +3069,96 @@ public: }; public: - struct RemovePrefsObserver_t { + struct GetPrefs_t { typedef PrefsHelper Owner; typedef void ReturnType; typedef void SetterType; typedef mozilla::jni::Args< - int32_t> Args; - static constexpr char name[] = "removePrefsObserver"; + mozilla::jni::ObjectArray::Param, + mozilla::jni::Object::Param> Args; + static constexpr char name[] = "nativeGetPrefs"; static constexpr char signature[] = - "(I)V"; + "([Ljava/lang/String;Lorg/mozilla/gecko/PrefsHelper$PrefHandler;)V"; static const bool isStatic = true; static const bool isMultithreaded = false; static const mozilla::jni::ExceptionMode exceptionMode = mozilla::jni::ExceptionMode::ABORT; }; +public: + struct RemoveObserver_t { + typedef PrefsHelper Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + mozilla::jni::ObjectArray::Param> Args; + static constexpr char name[] = "nativeRemoveObserver"; + static constexpr char signature[] = + "([Ljava/lang/String;)V"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + +public: + struct SetPref_t { + typedef PrefsHelper Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + mozilla::jni::String::Param, + bool, + int32_t, + bool, + int32_t, + mozilla::jni::String::Param> Args; + static constexpr char name[] = "nativeSetPref"; + static constexpr char signature[] = + "(Ljava/lang/String;ZIZILjava/lang/String;)V"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + +public: + struct OnPrefChange_t { + typedef PrefsHelper Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + mozilla::jni::String::Param, + int32_t, + bool, + int32_t, + mozilla::jni::String::Param> Args; + static constexpr char name[] = "onPrefChange"; + static constexpr char signature[] = + "(Ljava/lang/String;IZILjava/lang/String;)V"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + static auto OnPrefChange(mozilla::jni::String::Param, int32_t, bool, int32_t, mozilla::jni::String::Param) -> void; + +public: + static const int32_t PREF_BOOL = 1; + +public: + static const int32_t PREF_FINISH = 0; + +public: + static const int32_t PREF_INT = 2; + +public: + static const int32_t PREF_INVALID = -1; + +public: + static const int32_t PREF_STRING = 3; + public: template class Natives; }; From b9d1a002a8d24a26b0814a49e0675ccd4e675348 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:13 -0500 Subject: [PATCH 48/91] Bug 1243049 - Implement new PrefsHelper native methods; r=snorp Implement the PrefsHelper native methods. The previous browser.js implementation supported "pseudo-prefs" that did not exist as actual prefs, but was accessible through PrefsHelper. In order to accommodate these pseudo-prefs, we send observer notifications in order to communicate with browser.js about prefs that we don't support. --- widget/android/PrefsHelper.h | 324 +++++++++++++++++++++++++++++----- widget/android/jni/Refs.h | 5 + widget/android/nsAppShell.cpp | 5 + 3 files changed, 291 insertions(+), 43 deletions(-) diff --git a/widget/android/PrefsHelper.h b/widget/android/PrefsHelper.h index fa7a31cffb0..afe47ffb695 100644 --- a/widget/android/PrefsHelper.h +++ b/widget/android/PrefsHelper.h @@ -9,70 +9,308 @@ #include "GeneratedJNINatives.h" #include "MainThreadUtils.h" #include "nsAppShell.h" +#include "nsCOMPtr.h" +#include "nsVariant.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" #include "mozilla/UniquePtr.h" namespace mozilla { -struct PrefsHelper - : widget::PrefsHelper::Natives - , UsesGeckoThreadProxy +class PrefsHelper + : public widget::PrefsHelper::Natives + , public UsesGeckoThreadProxy { PrefsHelper() = delete; - static void GetPrefsById(const jni::ClassObject::LocalRef& cls, - int32_t requestId, - jni::ObjectArray::Param prefNames, - bool observe) + static bool GetVariantPref(nsIObserverService* aObsServ, + nsIWritableVariant* aVariant, + jni::Object::Param aPrefHandler, + jni::String::LocalRef& aPrefName) { - MOZ_ASSERT(NS_IsMainThread()); - - nsAppShell* const appShell = nsAppShell::Get(); - MOZ_ASSERT(appShell); - - nsTArray namesRefArray(prefNames.GetElements()); - const size_t len = namesRefArray.Length(); - - // Java strings are not null-terminated, but XPCOM expects - // null-terminated strings, so we copy the strings to nsString to - // ensure null-termination. - nsTArray namesStrArray(len); - nsTArray namesPtrArray(len); - - for (size_t i = 0; i < len; i++) { - if (!namesRefArray[i]) { - namesPtrArray.AppendElement(nullptr); - continue; - } - namesPtrArray.AppendElement(namesStrArray.AppendElement(nsString( - jni::String::Ref::From(namesRefArray[i])))->Data()); + if (NS_FAILED(aObsServ->NotifyObservers(aVariant, "android-get-pref", + nsString(aPrefName).get()))) { + return false; } - nsIAndroidBrowserApp* browserApp = nullptr; - appShell->GetBrowserApp(&browserApp); - MOZ_ASSERT(browserApp); + uint16_t varType = nsIDataType::VTYPE_EMPTY; + if (NS_FAILED(aVariant->GetDataType(&varType))) { + return false; + } - if (observe) { - browserApp->ObservePreferences( - requestId, len ? &namesPtrArray[0] : nullptr, len); - } else { - browserApp->GetPreferences( - requestId, len ? &namesPtrArray[0] : nullptr, len); + int32_t type = widget::PrefsHelper::PREF_INVALID; + bool boolVal = false; + int32_t intVal = 0; + nsAutoString strVal; + + switch (varType) { + case nsIDataType::VTYPE_BOOL: + type = widget::PrefsHelper::PREF_BOOL; + if (NS_FAILED(aVariant->GetAsBool(&boolVal))) { + return false; + } + break; + case nsIDataType::VTYPE_INT32: + type = widget::PrefsHelper::PREF_INT; + if (NS_FAILED(aVariant->GetAsInt32(&intVal))) { + return false; + } + break; + case nsIDataType::VTYPE_ASTRING: + type = widget::PrefsHelper::PREF_STRING; + if (NS_FAILED(aVariant->GetAsAString(strVal))) { + return false; + } + break; + default: + return false; + } + + const auto& jstrVal = type == widget::PrefsHelper::PREF_STRING ? + jni::Param(strVal, aPrefName.Env()) : + jni::Param(nullptr); + + widget::PrefsHelper::CallPrefHandler( + aPrefHandler, type, aPrefName, boolVal, intVal, jstrVal); + return true; + } + + static bool SetVariantPref(nsIObserverService* aObsServ, + nsIWritableVariant* aVariant, + jni::String::Param aPrefName, + bool aFlush, + int32_t aType, + bool aBoolVal, + int32_t aIntVal, + jni::String::Param aStrVal) + { + nsresult rv = NS_ERROR_FAILURE; + + switch (aType) { + case widget::PrefsHelper::PREF_BOOL: + rv = aVariant->SetAsBool(aBoolVal); + break; + case widget::PrefsHelper::PREF_INT: + rv = aVariant->SetAsInt32(aIntVal); + break; + case widget::PrefsHelper::PREF_STRING: + rv = aVariant->SetAsAString(nsString(aStrVal)); + break; + } + + if (NS_SUCCEEDED(rv)) { + rv = aObsServ->NotifyObservers(aVariant, "android-set-pref", + nsString(aPrefName).get()); + } + + uint16_t varType = nsIDataType::VTYPE_EMPTY; + if (NS_SUCCEEDED(rv)) { + rv = aVariant->GetDataType(&varType); + } + + // We use set-to-empty to signal the pref was handled. + const bool handled = varType == nsIDataType::VTYPE_EMPTY; + + if (NS_SUCCEEDED(rv) && handled && aFlush) { + rv = Preferences::GetService()->SavePrefFile(nullptr); + } + + if (NS_SUCCEEDED(rv)) { + return handled; + } + + NS_WARNING(nsPrintfCString("Failed to set pref %s", + nsCString(aPrefName).get()).get()); + // Pretend we handled the pref. + return true; + } + +public: + static void GetPrefs(const jni::ClassObject::LocalRef& aCls, + jni::ObjectArray::Param aPrefNames, + jni::Object::Param aPrefHandler) + { + nsTArray nameRefArray(aPrefNames.GetElements()); + nsCOMPtr obsServ; + nsCOMPtr value; + nsAdoptingString strVal; + + for (jni::Object::LocalRef& nameRef : nameRefArray) { + jni::String::LocalRef nameStr(mozilla::Move(nameRef)); + const nsCString& name = nsCString(nameStr); + + int32_t type = widget::PrefsHelper::PREF_INVALID; + bool boolVal = false; + int32_t intVal = 0; + + switch (Preferences::GetType(name.get())) { + case nsIPrefBranch::PREF_BOOL: + type = widget::PrefsHelper::PREF_BOOL; + boolVal = Preferences::GetBool(name.get()); + break; + + case nsIPrefBranch::PREF_INT: + type = widget::PrefsHelper::PREF_INT; + intVal = Preferences::GetInt(name.get()); + break; + + case nsIPrefBranch::PREF_STRING: + type = widget::PrefsHelper::PREF_STRING; + strVal = Preferences::GetLocalizedString(name.get()); + if (!strVal) { + strVal = Preferences::GetString(name.get()); + } + break; + + default: + // Pref not found; try to find it. + if (!obsServ) { + obsServ = services::GetObserverService(); + if (!obsServ) { + continue; + } + } + if (value) { + value->SetAsEmpty(); + } else { + value = new nsVariant(); + } + if (!GetVariantPref(obsServ, value, + aPrefHandler, nameStr)) { + NS_WARNING(nsPrintfCString("Failed to get pref %s", + name.get()).get()); + } + continue; + } + + const auto& jstrVal = type == widget::PrefsHelper::PREF_STRING ? + jni::Param(strVal, aCls.Env()) : + jni::Param(nullptr); + + widget::PrefsHelper::CallPrefHandler( + aPrefHandler, type, nameStr, boolVal, intVal, jstrVal); + } + + widget::PrefsHelper::CallPrefHandler( + aPrefHandler, widget::PrefsHelper::PREF_FINISH, + nullptr, false, 0, nullptr); + } + + static void SetPref(jni::String::Param aPrefName, + bool aFlush, + int32_t aType, + bool aBoolVal, + int32_t aIntVal, + jni::String::Param aStrVal) + { + const nsCString& name = nsCString(aPrefName); + + if (Preferences::GetType(name.get()) == nsIPrefBranch::PREF_INVALID) { + // No pref; try asking first. + nsCOMPtr obsServ = + services::GetObserverService(); + nsCOMPtr value = new nsVariant(); + if (obsServ && SetVariantPref(obsServ, value, aPrefName, aFlush, + aType, aBoolVal, aIntVal, aStrVal)) { + return; + } + } + + switch (aType) { + case widget::PrefsHelper::PREF_BOOL: + Preferences::SetBool(name.get(), aBoolVal); + break; + case widget::PrefsHelper::PREF_INT: + Preferences::SetInt(name.get(), aIntVal); + break; + case widget::PrefsHelper::PREF_STRING: + Preferences::SetString(name.get(), nsString(aStrVal)); + break; + default: + MOZ_ASSERT(false, "Invalid pref type"); + } + + if (aFlush) { + Preferences::GetService()->SavePrefFile(nullptr); } } - static void RemovePrefsObserver(int32_t requestId) + static void AddObserver(const jni::ClassObject::LocalRef& aCls, + jni::ObjectArray::Param aPrefNames, + jni::Object::Param aPrefHandler, + jni::ObjectArray::Param aPrefsToObserve) { - MOZ_ASSERT(NS_IsMainThread()); + // Call observer immediately with existing pref values. + GetPrefs(aCls, aPrefNames, aPrefHandler); + if (!aPrefsToObserve) { + return; + } + + nsTArray nameRefArray( + aPrefsToObserve.GetElements()); nsAppShell* const appShell = nsAppShell::Get(); MOZ_ASSERT(appShell); - nsIAndroidBrowserApp* browserApp = nullptr; - appShell->GetBrowserApp(&browserApp); - MOZ_ASSERT(browserApp); + for (jni::Object::LocalRef& nameRef : nameRefArray) { + jni::String::LocalRef nameStr(mozilla::Move(nameRef)); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Preferences::AddStrongObserver( + appShell, nsCString(nameStr).get()))); + } + } - browserApp->RemovePreferenceObservers(requestId); + static void RemoveObserver(const jni::ClassObject::LocalRef& aCls, + jni::ObjectArray::Param aPrefsToUnobserve) + { + nsTArray nameRefArray( + aPrefsToUnobserve.GetElements()); + nsAppShell* const appShell = nsAppShell::Get(); + MOZ_ASSERT(appShell); + + for (jni::Object::LocalRef& nameRef : nameRefArray) { + jni::String::LocalRef nameStr(mozilla::Move(nameRef)); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Preferences::RemoveObserver( + appShell, nsCString(nameStr).get()))); + } + } + + static void OnPrefChange(const char16_t* aData) + { + const nsCString& name = NS_LossyConvertUTF16toASCII(aData); + + int32_t type = -1; + bool boolVal = false; + int32_t intVal = false; + nsAdoptingString strVal; + + switch (Preferences::GetType(name.get())) { + case nsIPrefBranch::PREF_BOOL: + type = widget::PrefsHelper::PREF_BOOL; + boolVal = Preferences::GetBool(name.get()); + break; + case nsIPrefBranch::PREF_INT: + type = widget::PrefsHelper::PREF_INT; + intVal = Preferences::GetInt(name.get()); + break; + case nsIPrefBranch::PREF_STRING: + type = widget::PrefsHelper::PREF_STRING; + strVal = Preferences::GetLocalizedString(name.get()); + if (!strVal) { + strVal = Preferences::GetString(name.get()); + } + break; + default: + NS_WARNING(nsPrintfCString("Invalid pref %s", + name.get()).get()); + return; + } + + const auto& jstrVal = type == widget::PrefsHelper::PREF_STRING ? + jni::Param(strVal) : + jni::Param(nullptr); + + widget::PrefsHelper::OnPrefChange(name, type, boolVal, intVal, jstrVal); } }; diff --git a/widget/android/jni/Refs.h b/widget/android/jni/Refs.h index 52469c4f8ce..a954016641b 100644 --- a/widget/android/jni/Refs.h +++ b/widget/android/jni/Refs.h @@ -776,6 +776,11 @@ private: } public: + MOZ_IMPLICIT Type(decltype(nullptr)) + : Ref(nullptr) + , mEnv(nullptr) + {} + MOZ_IMPLICIT Type(const String::Ref& ref) : Ref(ref.Get()) , mEnv(nullptr) diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 4e085a87d6e..609bfed714d 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -341,6 +341,11 @@ nsAppShell::Observe(nsISupports* aSubject, } } removeObserver = true; + + } else if (!strcmp(aTopic, "nsPref:changed")) { + if (jni::IsAvailable()) { + mozilla::PrefsHelper::OnPrefChange(aData); + } } if (removeObserver) { From 195a41f4e6d933c064e2744148bfb7d6fc625b34 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:14 -0500 Subject: [PATCH 49/91] Bug 1243049 - Convert browser.js prefs code to use observer; r=margaret Convert the old prefs code in browser.js to use observer notifications that are sent by the new PrefsHelper implementation, in order to handle pseudo-prefs. --- .../java/org/mozilla/gecko/PrefsHelper.java | 28 +- mobile/android/chrome/content/browser.js | 350 ++++++------------ widget/android/nsIAndroidBridge.idl | 9 +- 3 files changed, 135 insertions(+), 252 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java b/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java index fef35f7f321..9325546f859 100644 --- a/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/PrefsHelper.java @@ -13,6 +13,7 @@ import android.util.Log; import android.util.SparseArray; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -24,6 +25,15 @@ public final class PrefsHelper { // Map pref name to ArrayList for multiple observers or PrefHandler for single observer. private static final SimpleArrayMap OBSERVERS = new SimpleArrayMap<>(); + private static final HashSet INT_TO_STRING_PREFS = new HashSet<>(8); + + static { + INT_TO_STRING_PREFS.add("browser.chrome.titlebarMode"); + INT_TO_STRING_PREFS.add("network.cookie.cookieBehavior"); + INT_TO_STRING_PREFS.add("font.size.inflation.minTwips"); + INT_TO_STRING_PREFS.add("home.sync.updateMode"); + INT_TO_STRING_PREFS.add("browser.image_blocking"); + } @WrapForJNI private static final int PREF_INVALID = -1; @@ -73,7 +83,13 @@ public final class PrefsHelper { int intVal = 0; String strVal = null; - if (value instanceof Boolean) { + if (INT_TO_STRING_PREFS.contains(pref)) { + // When sending to Java, we normalized special preferences that use integers + // and strings to represent booleans. Here, we convert them back to their + // actual types so we can store them. + type = PREF_INT; + intVal = Integer.parseInt(String.valueOf(value)); + } else if (value instanceof Boolean) { type = PREF_BOOL; boolVal = (Boolean) value; } else if (value instanceof Integer) { @@ -190,6 +206,16 @@ public final class PrefsHelper { @WrapForJNI private static void callPrefHandler(final PrefHandler handler, int type, final String pref, boolean boolVal, int intVal, String strVal) { + + // Some Gecko preferences use integers or strings to reference state instead of + // directly representing the value. Since the Java UI uses the type to determine + // which ui elements to show and how to handle them, we need to normalize these + // preferences to the correct type. + if (INT_TO_STRING_PREFS.contains(pref)) { + type = PREF_STRING; + strVal = String.valueOf(intVal); + } + switch (type) { case PREF_FINISH: handler.finish(); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index b626fbeb625..8441fc9362d 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -397,7 +397,6 @@ function InitLater(fn, object, name) { var BrowserApp = { _tabs: [], _selectedTab: null, - _prefObservers: [], get isTablet() { let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); @@ -437,7 +436,6 @@ var BrowserApp = { Services.obs.addObserver(this, "Session:Stop", false); Services.obs.addObserver(this, "SaveAs:PDF", false); Services.obs.addObserver(this, "Browser:Quit", false); - Services.obs.addObserver(this, "Preferences:Set", false); Services.obs.addObserver(this, "ScrollTo:FocusedInput", false); Services.obs.addObserver(this, "Sanitize:ClearData", false); Services.obs.addObserver(this, "FullScreen:Exit", false); @@ -445,6 +443,8 @@ var BrowserApp = { Services.obs.addObserver(this, "Viewport:Flush", false); Services.obs.addObserver(this, "Passwords:Init", false); Services.obs.addObserver(this, "FormHistory:Init", false); + Services.obs.addObserver(this, "android-get-pref", false); + Services.obs.addObserver(this, "android-set-pref", false); Services.obs.addObserver(this, "gather-telemetry", false); Services.obs.addObserver(this, "keyword-search", false); Services.obs.addObserver(this, "webapps-runtime-install", false); @@ -1472,12 +1472,6 @@ var BrowserApp = { }); }, - notifyPrefObservers: function(aPref) { - this._prefObservers[aPref].forEach(function(aRequestId) { - this.getPreferences(aRequestId, [aPref], 1); - }, this); - }, - // These values come from pref_tracking_protection_entries in arrays.xml. PREF_TRACKING_PROTECTION_ENABLED: "2", PREF_TRACKING_PROTECTION_ENABLED_PB: "1", @@ -1497,205 +1491,6 @@ var BrowserApp = { return this.PREF_TRACKING_PROTECTION_DISABLED; }, - handlePreferencesRequest: function handlePreferencesRequest(aRequestId, - aPrefNames, - aListen) { - - let prefs = []; - - for (let prefName of aPrefNames) { - let pref = { - name: prefName, - type: "", - value: null - }; - - if (aListen) { - if (this._prefObservers[prefName]) - this._prefObservers[prefName].push(aRequestId); - else - this._prefObservers[prefName] = [ aRequestId ]; - Services.prefs.addObserver(prefName, this, false); - } - - // These pref names are not "real" pref names. - // They are used in the setting menu, - // and these are passed when initializing the setting menu. - switch (prefName) { - // The plugin pref is actually two separate prefs, so - // we need to handle it differently - case "plugin.enable": - pref.type = "string";// Use a string type for java's ListPreference - pref.value = PluginHelper.getPluginPreference(); - prefs.push(pref); - continue; - // Handle master password - case "privacy.masterpassword.enabled": - pref.type = "bool"; - pref.value = MasterPassword.enabled; - prefs.push(pref); - continue; - case "privacy.trackingprotection.state": { - pref.type = "string"; - pref.value = this.getTrackingProtectionState(); - prefs.push(pref); - continue; - } - // Crash reporter submit pref must be fetched from nsICrashReporter service. - case "datareporting.crashreporter.submitEnabled": - let crashReporterBuilt = "nsICrashReporter" in Ci && Services.appinfo instanceof Ci.nsICrashReporter; - if (crashReporterBuilt) { - pref.type = "bool"; - pref.value = Services.appinfo.submitReports; - prefs.push(pref); - } - continue; - } - - try { - switch (Services.prefs.getPrefType(prefName)) { - case Ci.nsIPrefBranch.PREF_BOOL: - pref.type = "bool"; - pref.value = Services.prefs.getBoolPref(prefName); - break; - case Ci.nsIPrefBranch.PREF_INT: - pref.type = "int"; - pref.value = Services.prefs.getIntPref(prefName); - break; - case Ci.nsIPrefBranch.PREF_STRING: - default: - pref.type = "string"; - try { - // Try in case it's a localized string (will throw an exception if not) - pref.value = Services.prefs.getComplexValue(prefName, Ci.nsIPrefLocalizedString).data; - } catch (e) { - pref.value = Services.prefs.getCharPref(prefName); - } - break; - } - } catch (e) { - dump("Error reading pref [" + prefName + "]: " + e); - // preference does not exist; do not send it - continue; - } - - // Some Gecko preferences use integers or strings to reference - // state instead of directly representing the value. - // Since the Java UI uses the type to determine which ui elements - // to show and how to handle them, we need to normalize these - // preferences to the correct type. - switch (prefName) { - // (string) index for determining which multiple choice value to display. - case "browser.chrome.titlebarMode": - case "network.cookie.cookieBehavior": - case "font.size.inflation.minTwips": - case "home.sync.updateMode": - case "browser.image_blocking": - pref.type = "string"; - pref.value = pref.value.toString(); - break; - } - - prefs.push(pref); - } - - Messaging.sendRequest({ - type: "Preferences:Data", - requestId: aRequestId, // opaque request identifier, can be any string/int/whatever - preferences: prefs - }); - }, - - setPreferences: function (aPref) { - let json = JSON.parse(aPref); - - switch (json.name) { - // The plugin pref is actually two separate prefs, so - // we need to handle it differently - case "plugin.enable": - PluginHelper.setPluginPreference(json.value); - return; - - // MasterPassword pref is not real, we just need take action and leave - case "privacy.masterpassword.enabled": - if (MasterPassword.enabled) - MasterPassword.removePassword(json.value); - else - MasterPassword.setPassword(json.value); - return; - - // "privacy.trackingprotection.state" is not a "real" pref name, but it's used in the setting menu. - // By default "privacy.trackingprotection.pbmode.enabled" is true, - // and "privacy.trackingprotection.enabled" is false. - case "privacy.trackingprotection.state": { - switch (json.value) { - // Tracking protection disabled. - case this.PREF_TRACKING_PROTECTION_DISABLED: - Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", false); - Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); - break; - // Tracking protection only in private browsing, - case this.PREF_TRACKING_PROTECTION_ENABLED_PB: - Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true); - Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); - break; - // Tracking protection everywhere. - case this.PREF_TRACKING_PROTECTION_ENABLED: - Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true); - Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true); - break; - } - return; - } - // Enabling or disabling suggestions will prevent future prompts - case SearchEngines.PREF_SUGGEST_ENABLED: - Services.prefs.setBoolPref(SearchEngines.PREF_SUGGEST_PROMPTED, true); - break; - - // Crash reporter preference is in a service; set and return. - case "datareporting.crashreporter.submitEnabled": - let crashReporterBuilt = "nsICrashReporter" in Ci && Services.appinfo instanceof Ci.nsICrashReporter; - if (crashReporterBuilt) { - Services.appinfo.submitReports = json.value; - } - return; - - // When sending to Java, we normalized special preferences that use - // integers and strings to represent booleans. Here, we convert them back - // to their actual types so we can store them. - case "browser.chrome.titlebarMode": - case "network.cookie.cookieBehavior": - case "font.size.inflation.minTwips": - case "home.sync.updateMode": - case "browser.image_blocking": - json.type = "int"; - json.value = parseInt(json.value); - break; - } - - switch (json.type) { - case "bool": - Services.prefs.setBoolPref(json.name, json.value); - break; - case "int": - Services.prefs.setIntPref(json.name, json.value); - break; - default: { - let pref = Cc["@mozilla.org/pref-localizedstring;1"].createInstance(Ci.nsIPrefLocalizedString); - pref.data = json.value; - Services.prefs.setComplexValue(json.name, Ci.nsISupportsString, pref); - break; - } - } - - // Finally, if we were asked to flush, flush prefs to disk right now. - // This allows us to be confident that prefs set in Settings are persisted, - // even if we crash very soon after. - if (json.flush) { - Services.prefs.savePrefFile(null); - } - }, - sanitize: function (aItems, callback) { let success = true; var promises = []; @@ -1998,10 +1793,6 @@ var BrowserApp = { this.saveAsPDF(browser); break; - case "Preferences:Set": - this.setPreferences(aData); - break; - case "ScrollTo:FocusedInput": // these messages come from a change in the viewable area and not user interaction // we allow scrolling to the selected input, but not zooming the page @@ -2040,6 +1831,111 @@ var BrowserApp = { break; } + case "android-get-pref": { + // These pref names are not "real" pref names. They are used in the + // setting menu, and these are passed when initializing the setting + // menu. aSubject is a nsIWritableVariant to hold the pref value. + aSubject.QueryInterface(Ci.nsIWritableVariant); + + switch (aData) { + // The plugin pref is actually two separate prefs, so + // we need to handle it differently + case "plugin.enable": + aSubject.setAsAString(PluginHelper.getPluginPreference()); + break; + + // Handle master password + case "privacy.masterpassword.enabled": + aSubject.setAsBool(MasterPassword.enabled); + break; + + case "privacy.trackingprotection.state": { + aSubject.setAsAString(this.getTrackingProtectionState()); + break; + } + + // Crash reporter submit pref must be fetched from nsICrashReporter + // service. + case "datareporting.crashreporter.submitEnabled": + let crashReporterBuilt = "nsICrashReporter" in Ci && + Services.appinfo instanceof Ci.nsICrashReporter; + if (crashReporterBuilt) { + aSubject.setAsBool(Services.appinfo.submitReports); + } + break; + } + break; + } + + case "android-set-pref": { + // Pseudo-prefs. aSubject is an nsIWritableVariant that holds the pref + // value. Set to empty to signal the pref was handled. + aSubject.QueryInterface(Ci.nsIWritableVariant); + let value = aSubject.QueryInterface(Ci.nsIVariant); + + switch (aData) { + // The plugin pref is actually two separate prefs, so we need to + // handle it differently. + case "plugin.enable": + PluginHelper.setPluginPreference(value); + aSubject.setAsEmpty(); + break; + + // MasterPassword pref is not real, we just need take action and leave + case "privacy.masterpassword.enabled": + if (MasterPassword.enabled) { + MasterPassword.removePassword(value); + } else { + MasterPassword.setPassword(value); + } + aSubject.setAsEmpty(); + break; + + // "privacy.trackingprotection.state" is not a "real" pref name, but + // it's used in the setting menu. By default + // "privacy.trackingprotection.pbmode.enabled" is true, and + // "privacy.trackingprotection.enabled" is false. + case "privacy.trackingprotection.state": { + switch (value) { + // Tracking protection disabled. + case this.PREF_TRACKING_PROTECTION_DISABLED: + Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", false); + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); + break; + // Tracking protection only in private browsing, + case this.PREF_TRACKING_PROTECTION_ENABLED_PB: + Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true); + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); + break; + // Tracking protection everywhere. + case this.PREF_TRACKING_PROTECTION_ENABLED: + Services.prefs.setBoolPref("privacy.trackingprotection.pbmode.enabled", true); + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", true); + break; + } + aSubject.setAsEmpty(); + break; + } + + // Enabling or disabling suggestions will prevent future prompts + case SearchEngines.PREF_SUGGEST_ENABLED: + Services.prefs.setBoolPref(SearchEngines.PREF_SUGGEST_PROMPTED, true); + aSubject.setAsEmpty(); + break; + + // Crash reporter preference is in a service; set and return. + case "datareporting.crashreporter.submitEnabled": + let crashReporterBuilt = "nsICrashReporter" in Ci && + Services.appinfo instanceof Ci.nsICrashReporter; + if (crashReporterBuilt) { + Services.appinfo.submitReports = value; + aSubject.setAsEmpty(); + } + break; + } + break; + } + case "sessionstore-state-purge-complete": Messaging.sendRequest({ type: "Session:StatePurged" }); break; @@ -2048,10 +1944,6 @@ var BrowserApp = { Messaging.sendRequest({ type: "Telemetry:Gather" }); break; - case "nsPref:changed": - this.notifyPrefObservers(aData); - break; - case "webapps-runtime-install": WebappManager.install(JSON.parse(aData), aSubject); break; @@ -2228,34 +2120,6 @@ var BrowserApp = { return UITelemetry; }, - getPreferences: function getPreferences(requestId, prefNames, count) { - this.handlePreferencesRequest(requestId, prefNames, false); - }, - - observePreferences: function observePreferences(requestId, prefNames, count) { - this.handlePreferencesRequest(requestId, prefNames, true); - }, - - removePreferenceObservers: function removePreferenceObservers(aRequestId) { - let newPrefObservers = []; - for (let prefName in this._prefObservers) { - let requestIds = this._prefObservers[prefName]; - // Remove the requestID from the preference handlers - let i = requestIds.indexOf(aRequestId); - if (i >= 0) { - requestIds.splice(i, 1); - } - - // If there are no more request IDs, remove the observer - if (requestIds.length == 0) { - Services.prefs.removeObserver(prefName, this); - } else { - newPrefObservers[prefName] = requestIds; - } - } - this._prefObservers = newPrefObservers; - }, - // This method will return a list of history items and toIndex based on the action provided from the fromIndex to toIndex, // optionally selecting selIndex (if fromIndex <= selIndex <= toIndex) getHistory: function(data) { diff --git a/widget/android/nsIAndroidBridge.idl b/widget/android/nsIAndroidBridge.idl index 4abc1de96c7..3ff55e1cbaf 100644 --- a/widget/android/nsIAndroidBridge.idl +++ b/widget/android/nsIAndroidBridge.idl @@ -25,17 +25,10 @@ interface nsIUITelemetryObserver : nsISupports { in wstring extras); }; -[scriptable, uuid(78ec5811-78ee-4239-a554-3303f823dbbc)] +[scriptable, uuid(0370450f-2e9c-4d16-b333-8ca6ce31a5ff)] interface nsIAndroidBrowserApp : nsISupports { readonly attribute nsIBrowserTab selectedTab; nsIBrowserTab getBrowserTab(in int32_t tabId); - void getPreferences(in int32_t requestId, - [array, size_is(count)] in wstring prefNames, - in unsigned long count); - void observePreferences(in int32_t requestId, - [array, size_is(count)] in wstring prefNames, - in unsigned long count); - void removePreferenceObservers(in int32_t requestId); nsIUITelemetryObserver getUITelemetryObserver(); }; From e2261271617829862e8031c37251401d8638f201 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:14 -0500 Subject: [PATCH 50/91] Bug 1243049 - Use PrefHelper.addObserver; r=snorp Use the new PrefHelper.addObserver method for adding pref observers. --- .../org/mozilla/gecko/DynamicToolbar.java | 14 +++------- .../java/org/mozilla/gecko/ZoomedView.java | 28 +++++++------------ .../gecko/gfx/DynamicToolbarAnimator.java | 17 ++++------- .../gecko/gfx/JavaPanZoomController.java | 14 ++++------ .../preferences/GeckoPreferenceFragment.java | 9 +++--- .../gecko/preferences/GeckoPreferences.java | 27 +++++++++--------- .../mozilla/gecko/toolbar/ToolbarPrefs.java | 18 ++---------- 7 files changed, 46 insertions(+), 81 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java b/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java index 5292d6786e6..caf7e16aa31 100644 --- a/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java +++ b/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java @@ -25,7 +25,7 @@ public class DynamicToolbar { // bugs in the Android code. See bug 1231554. private final boolean forceDisabled; - private final int prefObserverId; + private final PrefsHelper.PrefHandler prefObserver; private final EnumSet pinFlags = EnumSet.noneOf(PinReason.class); private LayerView layerView; private OnEnabledChangedListener enabledChangedListener; @@ -54,7 +54,8 @@ public class DynamicToolbar { public DynamicToolbar() { // Listen to the dynamic toolbar pref - prefObserverId = PrefsHelper.getPref(CHROME_PREF, new PrefHandler()); + prefObserver = new PrefHandler(); + PrefsHelper.addObserver(new String[] { CHROME_PREF }, prefObserver); forceDisabled = isForceDisabled(); if (forceDisabled) { Log.i(LOGTAG, "Force-disabling dynamic toolbar for " + Build.MODEL + " (" + Build.DEVICE + "/" + Build.PRODUCT + ")"); @@ -73,7 +74,7 @@ public class DynamicToolbar { } public void destroy() { - PrefsHelper.removeObserver(prefObserverId); + PrefsHelper.removeObserver(prefObserver); } public void setLayerView(LayerView layerView) { @@ -219,12 +220,5 @@ public class DynamicToolbar { } }); } - - @Override - public boolean isObserver() { - // We want to be notified of changes to be able to switch mode - // without restarting. - return true; - } } } diff --git a/mobile/android/base/java/org/mozilla/gecko/ZoomedView.java b/mobile/android/base/java/org/mozilla/gecko/ZoomedView.java index 7753609397f..ada71a07da2 100644 --- a/mobile/android/base/java/org/mozilla/gecko/ZoomedView.java +++ b/mobile/android/base/java/org/mozilla/gecko/ZoomedView.java @@ -66,8 +66,7 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL private int currentZoomFactorIndex; private boolean isSimplifiedUI; private int defaultZoomFactor; - private int prefDefaultZoomFactorObserverId; - private int prefSimplifiedUIObserverId; + private PrefsHelper.PrefHandler prefObserver; private ImageView zoomedImageView; private LayerView layerView; @@ -248,8 +247,10 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL } void destroy() { - PrefsHelper.removeObserver(prefDefaultZoomFactorObserverId); - PrefsHelper.removeObserver(prefSimplifiedUIObserverId); + if (prefObserver != null) { + PrefsHelper.removeObserver(prefObserver); + prefObserver = null; + } ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable); EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Gesture:clusteredLinksClicked", "Window:Resize", "Content:LocationChange", @@ -518,7 +519,7 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL } private void getPrefs() { - prefSimplifiedUIObserverId = PrefsHelper.getPref("ui.zoomedview.simplified", new PrefsHelper.PrefHandlerBase() { + prefObserver = new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, boolean simplified) { isSimplifiedUI = simplified; @@ -530,13 +531,6 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL updateUI(); } - @Override - public boolean isObserver() { - return true; - } - }); - - prefDefaultZoomFactorObserverId = PrefsHelper.getPref("ui.zoomedview.defaultZoomFactor", new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, int defaultZoomFactorFromSettings) { defaultZoomFactor = defaultZoomFactorFromSettings; @@ -547,12 +541,10 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL } updateUI(); } - - @Override - public boolean isObserver() { - return true; - } - }); + }; + PrefsHelper.addObserver(new String[] { "ui.zoomedview.simplified", + "ui.zoomedview.defaultZoomFactor" }, + prefObserver); } private void startZoomDisplay(LayerView aLayerView, final int leftFromGecko, final int topFromGecko) { diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java index 0d672a3c405..e8266a8ed16 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java @@ -60,7 +60,7 @@ public class DynamicToolbarAnimator { */ private float SCROLL_TOOLBAR_THRESHOLD = 0.20f; /* The ID of the prefs listener for the scroll-toolbar threshold */ - private Integer mPrefObserverId; + private final PrefsHelper.PrefHandler mPrefObserver; /* While we are resizing the viewport to account for the toolbar, the Java * code and painted layer metrics in the compositor have different notions @@ -92,24 +92,17 @@ public class DynamicToolbarAnimator { mInterpolator = new DecelerateInterpolator(); // Listen to the dynamic toolbar pref - mPrefObserverId = PrefsHelper.getPref(PREF_SCROLL_TOOLBAR_THRESHOLD, new PrefsHelper.PrefHandlerBase() { + mPrefObserver = new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, int value) { SCROLL_TOOLBAR_THRESHOLD = value / 100.0f; } - - @Override - public boolean isObserver() { - return true; - } - }); + }; + PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver); } public void destroy() { - if (mPrefObserverId != null) { - PrefsHelper.removeObserver(mPrefObserverId); - mPrefObserverId = null; - } + PrefsHelper.removeObserver(mPrefObserver); } public void addTranslationListener(LayerView.DynamicToolbarListener aListener) { diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java index 91152e03d0f..2869a579ce9 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java @@ -135,6 +135,8 @@ class JavaPanZoomController // Handler to be notified when overscroll occurs private Overscroll mOverscroll; + private final PrefsHelper.PrefHandler mPrefsObserver; + public JavaPanZoomController(PanZoomTarget target, View view, EventDispatcher eventDispatcher) { mTarget = target; mSubscroller = new SubdocumentScrollHelper(eventDispatcher); @@ -158,7 +160,7 @@ class JavaPanZoomController String[] prefs = { "ui.scrolling.axis_lock_mode", "ui.scrolling.negate_wheel_scroll", "ui.scrolling.gamepad_dead_zone" }; - PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() { + mPrefsObserver = new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, String value) { if (pref.equals("ui.scrolling.axis_lock_mode")) { if (value.equals("standard")) { @@ -182,13 +184,8 @@ class JavaPanZoomController mNegateWheelScroll = value; } } - - @Override - public boolean isObserver() { - return true; - } - - }); + }; + PrefsHelper.addObserver(prefs, mPrefsObserver); Axis.initPrefs(); @@ -202,6 +199,7 @@ class JavaPanZoomController @Override public void destroy() { + PrefsHelper.removeObserver(mPrefsObserver); mEventDispatcher.unregisterGeckoThreadListener(this, MESSAGE_ZOOM_RECT, MESSAGE_ZOOM_PAGE, diff --git a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferenceFragment.java b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferenceFragment.java index 7b3286c8613..48456cad420 100644 --- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferenceFragment.java +++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferenceFragment.java @@ -60,7 +60,7 @@ public class GeckoPreferenceFragment extends PreferenceFragment { } private static final String LOGTAG = "GeckoPreferenceFragment"; - private int mPrefsRequestId; + private PrefsHelper.PrefHandler mPrefsRequest; private Locale lastLocale = Locale.getDefault(); @Override @@ -87,7 +87,7 @@ public class GeckoPreferenceFragment extends PreferenceFragment { PreferenceScreen screen = getPreferenceScreen(); setPreferenceScreen(screen); - mPrefsRequestId = ((GeckoPreferences)getActivity()).setupPreferences(screen); + mPrefsRequest = ((GeckoPreferences)getActivity()).setupPreferences(screen); syncPreference = (SyncPreference) findPreference(GeckoPreferences.PREFS_SYNC); } @@ -248,8 +248,9 @@ public class GeckoPreferenceFragment extends PreferenceFragment { @Override public void onDestroy() { super.onDestroy(); - if (mPrefsRequestId > 0) { - PrefsHelper.removeObserver(mPrefsRequestId); + if (mPrefsRequest != null) { + PrefsHelper.removeObserver(mPrefsRequest); + mPrefsRequest = null; } final int res = getResource(); diff --git a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java index 58874275ae4..48c862c4539 100644 --- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java +++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java @@ -120,7 +120,7 @@ OnSharedPreferenceChangeListener private static boolean sIsCharEncodingEnabled; private boolean mInitialized; - private int mPrefsRequestId; + private PrefsHelper.PrefHandler mPrefsRequest; private List
mHeaders; // These match keys in resources/xml*/preferences*.xml @@ -513,7 +513,7 @@ OnSharedPreferenceChangeListener mInitialized = true; if (Versions.preHC) { PreferenceScreen screen = getPreferenceScreen(); - mPrefsRequestId = setupPreferences(screen); + mPrefsRequest = setupPreferences(screen); } } @@ -537,8 +537,9 @@ OnSharedPreferenceChangeListener EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this, "Snackbar:Show"); - if (mPrefsRequestId > 0) { - PrefsHelper.removeObserver(mPrefsRequestId); + if (mPrefsRequest != null) { + PrefsHelper.removeObserver(mPrefsRequest); + mPrefsRequest = null; } // The intent extras will be null if this is the top-level settings @@ -674,7 +675,7 @@ OnSharedPreferenceChangeListener * @return The integer id for the PrefsHelper.PrefHandlerBase listener added * to monitor changes to Gecko prefs. */ - public int setupPreferences(PreferenceGroup prefs) { + public PrefsHelper.PrefHandler setupPreferences(PreferenceGroup prefs) { ArrayList list = new ArrayList(); setupPreferences(prefs, list); return getGeckoPreferences(prefs, list); @@ -1465,8 +1466,10 @@ OnSharedPreferenceChangeListener } // Initialize preferences by requesting the preference values from Gecko - private int getGeckoPreferences(final PreferenceGroup screen, ArrayList prefs) { - return PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() { + private PrefsHelper.PrefHandler getGeckoPreferences(final PreferenceGroup screen, + ArrayList prefs) { + + final PrefsHelper.PrefHandler prefHandler = new PrefsHelper.PrefHandlerBase() { private Preference getField(String prefName) { return screen.findPreference(prefName); } @@ -1547,11 +1550,6 @@ OnSharedPreferenceChangeListener Log.w(LOGTAG, "Unhandled int value for pref [" + pref + "]"); } - @Override - public boolean isObserver() { - return true; - } - @Override public void finish() { // enable all preferences once we have them from gecko @@ -1562,7 +1560,10 @@ OnSharedPreferenceChangeListener } }); } - }); + }; + final String[] prefNames = prefs.toArray(new String[prefs.size()]); + PrefsHelper.addObserver(prefNames, prefHandler); + return prefHandler; } @Override diff --git a/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarPrefs.java b/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarPrefs.java index f78bd659f98..f881de154ad 100644 --- a/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarPrefs.java +++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarPrefs.java @@ -24,8 +24,6 @@ class ToolbarPrefs { private volatile boolean enableAutocomplete; private volatile boolean trimUrls; - private Integer prefObserverId; - ToolbarPrefs() { // Skip autocompletion while Gecko is loading. // We will get the correct pref value once Gecko is loaded. @@ -42,16 +40,11 @@ class ToolbarPrefs { } void open() { - if (prefObserverId == null) { - prefObserverId = PrefsHelper.getPrefs(PREFS, HANDLER); - } + PrefsHelper.addObserver(PREFS, HANDLER); } void close() { - if (prefObserverId != null) { - PrefsHelper.removeObserver(prefObserverId); - prefObserverId = null; - } + PrefsHelper.removeObserver(HANDLER); } private void triggerTitleChangeListener() { @@ -81,12 +74,5 @@ class ToolbarPrefs { } } } - - @Override - public boolean isObserver() { - // We want to be notified of changes to be able to switch mode - // without restarting. - return true; - } } } From 41e607e6aab86516fd598f1e939e789cb4837693 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:14 -0500 Subject: [PATCH 51/91] Bug 1243049 - Update robocop tests to use new prefs API; r=gbrown Change old robocop prefs API to the new API and add helper classes for getting prefs. Also switch all tests that use prefs to use the new API. --- .../src/org/mozilla/gecko/Actions.java | 51 ++++---- .../mozilla/gecko/FennecNativeActions.java | 109 +++++++++++++++++- .../src/org/mozilla/gecko/tests/BaseTest.java | 74 ++++-------- .../mozilla/gecko/tests/testAdobeFlash.java | 10 +- .../org/mozilla/gecko/tests/testCheck2.java | 10 +- .../org/mozilla/gecko/tests/testCheck3.java | 10 +- .../mozilla/gecko/tests/testDistribution.java | 91 ++++++++------- .../mozilla/gecko/tests/testDoorHanger.java | 71 +++--------- .../gecko/tests/testPasswordEncrypt.java | 10 +- .../gecko/tests/testPrefsObserver.java | 97 ++++++---------- 10 files changed, 253 insertions(+), 280 deletions(-) diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java index afb8d9ba5d7..05e6bfa52a3 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/Actions.java @@ -51,28 +51,39 @@ public interface Actions { */ void sendGeckoEvent(String geckoEvent, String data); - /** - * Sends a preferences get event to Gecko. - * - * @param requestId The id of this request. - * @param prefNames The preferences being requested. - */ - void sendPreferencesGetEvent(int requestId, String[] prefNames); + public interface PrefWaiter { + boolean isFinished(); + void waitForFinish(); + void waitForFinish(long timeoutMillis, boolean failOnTimeout); + } - /** - * Sends a preferences observe event to Gecko. - * - * @param requestId The id of this request. - * @param prefNames The preferences being requested. - */ - void sendPreferencesObserveEvent(int requestId, String[] prefNames); + public abstract static class PrefHandlerBase implements PrefsHelper.PrefHandler { + /* package */ Assert asserter; - /** - * Sends a preferences remove observers event to Gecko. - * - * @param requestId The id of this request. - */ - void sendPreferencesRemoveObserversEvent(int requestid); + @Override // PrefsHelper.PrefHandler + public void prefValue(String pref, boolean value) { + asserter.ok(false, "Unexpected pref callback", ""); + } + + @Override // PrefsHelper.PrefHandler + public void prefValue(String pref, int value) { + asserter.ok(false, "Unexpected pref callback", ""); + } + + @Override // PrefsHelper.PrefHandler + public void prefValue(String pref, String value) { + asserter.ok(false, "Unexpected pref callback", ""); + } + + @Override // PrefsHelper.PrefHandler + public void finish() { + } + } + + PrefWaiter getPrefs(String[] prefNames, PrefHandlerBase handler); + void setPref(String pref, Object value, boolean flush); + PrefWaiter addPrefsObserver(String[] prefNames, PrefHandlerBase handler); + void removePrefsObserver(PrefWaiter handler); /** * Listens for a gecko event to be sent from the Gecko instance. diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java index 84a44b37375..8af88ec002c 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/FennecNativeActions.java @@ -4,6 +4,7 @@ package org.mozilla.gecko; +import java.util.ArrayList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -190,16 +191,112 @@ public class FennecNativeActions implements Actions { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(geckoEvent, data)); } - public void sendPreferencesGetEvent(int requestId, String[] prefNames) { - PrefsHelper.getPrefsById(requestId, prefNames, /* observe */ false); + public static final class PrefProxy implements PrefsHelper.PrefHandler, PrefWaiter { + public static final int MAX_WAIT_MS = 180000; + + /* package */ final PrefHandlerBase target; + private final String[] expectedPrefs; + private final ArrayList seenPrefs = new ArrayList<>(); + private boolean finished = false; + + /* package */ PrefProxy(PrefHandlerBase target, String[] expectedPrefs, Assert asserter) { + this.target = target; + this.expectedPrefs = expectedPrefs; + target.asserter = asserter; + } + + @Override // PrefsHelper.PrefHandler + public void prefValue(String pref, boolean value) { + target.prefValue(pref, value); + seenPrefs.add(pref); + } + + @Override // PrefsHelper.PrefHandler + public void prefValue(String pref, int value) { + target.prefValue(pref, value); + seenPrefs.add(pref); + } + + @Override // PrefsHelper.PrefHandler + public void prefValue(String pref, String value) { + target.prefValue(pref, value); + seenPrefs.add(pref); + } + + @Override // PrefsHelper.PrefHandler + public synchronized void finish() { + target.finish(); + + for (String pref : expectedPrefs) { + target.asserter.ok(seenPrefs.remove(pref), "Checking pref was seen", pref); + } + target.asserter.ok(seenPrefs.isEmpty(), "Checking unexpected prefs", + TextUtils.join(", ", seenPrefs)); + + finished = true; + this.notifyAll(); + } + + @Override // PrefWaiter + public synchronized boolean isFinished() { + return finished; + } + + @Override // PrefWaiter + public void waitForFinish() { + waitForFinish(MAX_WAIT_MS, /* failOnTimeout */ true); + } + + @Override // PrefWaiter + public synchronized void waitForFinish(long timeoutMillis, boolean failOnTimeout) { + final long startTime = System.nanoTime(); + while (!finished) { + if (System.nanoTime() - startTime + >= timeoutMillis * 1e6 /* ns per ms */) { + final String prefsLog = "expected " + + TextUtils.join(", ", expectedPrefs) + "; got " + + TextUtils.join(", ", seenPrefs.toArray()) + "."; + if (failOnTimeout) { + FennecNativeDriver.logAllStackTraces(FennecNativeDriver.LogLevel.ERROR); + target.asserter.ok(false, "Timeout waiting for pref", prefsLog); + } else { + FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG, + "Pref timeout (" + prefsLog + ")"); + } + break; + } + try { + this.wait(1000); // Wait for 1 second at a time. + } catch (final InterruptedException e) { + // Attempt waiting again. + } + } + finished = false; + } } - public void sendPreferencesObserveEvent(int requestId, String[] prefNames) { - PrefsHelper.getPrefsById(requestId, prefNames, /* observe */ true); + @Override // Actions + public PrefWaiter getPrefs(String[] prefNames, PrefHandlerBase handler) { + final PrefProxy proxy = new PrefProxy(handler, prefNames, mAsserter); + PrefsHelper.getPrefs(prefNames, proxy); + return proxy; } - public void sendPreferencesRemoveObserversEvent(int requestId) { - PrefsHelper.removePrefsObserver(requestId); + @Override // Actions + public void setPref(String pref, Object value, boolean flush) { + PrefsHelper.setPref(pref, value, flush); + } + + @Override // Actions + public PrefWaiter addPrefsObserver(String[] prefNames, PrefHandlerBase handler) { + final PrefProxy proxy = new PrefProxy(handler, prefNames, mAsserter); + PrefsHelper.addObserver(prefNames, proxy); + return proxy; + } + + @Override // Actions + public void removePrefsObserver(PrefWaiter proxy) { + PrefsHelper.removeObserver((PrefProxy) proxy); } class PaintExpecter implements RepeatedEventExpecter { diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java index 12b25b26f6b..36b21f3512f 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java @@ -62,11 +62,9 @@ abstract class BaseTest extends BaseRobocopTest { public static final int MAX_WAIT_MS = 4500; public static final int LONG_PRESS_TIME = 6000; private static final int GECKO_READY_WAIT_MS = 180000; - public static final int MAX_WAIT_BLOCK_FOR_EVENT_DATA_MS = 90000; protected static final String URL_HTTP_PREFIX = "http://"; - private int mPreferenceRequestID = 0; public Device mDevice; protected DatabaseHelper mDatabaseHelper; protected int mScreenMidWidth; @@ -886,60 +884,34 @@ abstract class BaseTest extends BaseRobocopTest { /** * Set the preference and wait for it to change before proceeding with the test. */ - public void setPreferenceAndWaitForChange(final JSONObject jsonPref) { + public void setPreferenceAndWaitForChange(final String name, final Object value) { blockForGeckoReady(); - mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString()); - - // Get the preference name from the json and store it in an array. This array - // will be used later while fetching the preference data. - String[] prefNames = new String[1]; - try { - prefNames[0] = jsonPref.getString("name"); - } catch (JSONException e) { - mAsserter.ok(false, "Exception in setPreferenceAndWaitForChange", getStackTraceString(e)); - } + mActions.setPref(name, value, /* flush */ false); // Wait for confirmation of the pref change before proceeding with the test. - final int ourRequestID = mPreferenceRequestID--; - final Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); - mActions.sendPreferencesGetEvent(ourRequestID, prefNames); + mActions.getPrefs(new String[] { name }, new Actions.PrefHandlerBase() { - // Wait until we get the correct "Preferences:Data" event - waitForCondition(new Condition() { - final long endTime = SystemClock.elapsedRealtime() + MAX_WAIT_BLOCK_FOR_EVENT_DATA_MS; - - @Override - public boolean isSatisfied() { - try { - long timeout = endTime - SystemClock.elapsedRealtime(); - if (timeout < 0) { - timeout = 0; - } - - JSONObject data = new JSONObject(eventExpecter.blockForEventDataWithTimeout(timeout)); - int requestID = data.getInt("requestId"); - if (requestID != ourRequestID) { - return false; - } - - JSONArray preferences = data.getJSONArray("preferences"); - mAsserter.is(preferences.length(), 1, "Expecting preference array to have one element"); - JSONObject prefs = (JSONObject) preferences.get(0); - mAsserter.is(prefs.getString("name"), jsonPref.getString("name"), - "Expecting returned preference name to be the same as the set name"); - mAsserter.is(prefs.getString("type"), jsonPref.getString("type"), - "Expecting returned preference type to be the same as the set type"); - mAsserter.is(prefs.get("value"), jsonPref.get("value"), - "Expecting returned preference value to be the same as the set value"); - return true; - } catch(JSONException e) { - mAsserter.ok(false, "Exception in setPreferenceAndWaitForChange", getStackTraceString(e)); - // Please the java compiler - return false; - } + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, boolean changedValue) { + mAsserter.is(pref, name, "Expecting correct pref name"); + mAsserter.ok(value instanceof Boolean, "Expecting boolean pref", ""); + mAsserter.is(changedValue, value, "Expecting matching pref value"); } - }, MAX_WAIT_BLOCK_FOR_EVENT_DATA_MS); - eventExpecter.unregisterListener(); + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, int changedValue) { + mAsserter.is(pref, name, "Expecting correct pref name"); + mAsserter.ok(value instanceof Integer, "Expecting int pref", ""); + mAsserter.is(changedValue, value, "Expecting matching pref value"); + } + + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, String changedValue) { + mAsserter.is(pref, name, "Expecting correct pref name"); + mAsserter.ok(value instanceof CharSequence, "Expecting string pref", ""); + mAsserter.is(changedValue, value, "Expecting matching pref value"); + } + + }).waitForFinish(); } } diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAdobeFlash.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAdobeFlash.java index 3368f285d40..13f7f817a36 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAdobeFlash.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testAdobeFlash.java @@ -23,15 +23,7 @@ public class testAdobeFlash extends PixelTest { } // Enable plugins - JSONObject jsonPref = new JSONObject(); - try { - jsonPref.put("name", "plugin.enable"); - jsonPref.put("type", "string"); - jsonPref.put("value", "1"); - setPreferenceAndWaitForChange(jsonPref); - } catch (Exception ex) { - mAsserter.ok(false, "exception in testAdobeFlash", ex.toString()); - } + setPreferenceAndWaitForChange("plugin.enable", "1"); blockForGeckoReady(); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck2.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck2.java index 092deae1244..fc538b5bf68 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck2.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck2.java @@ -16,15 +16,7 @@ public class testCheck2 extends PixelTest { String url = getAbsoluteUrl("/startup_test/fennecmark/cnn/cnn.com/index.html"); // Enable double-tap zooming - JSONObject jsonPref = new JSONObject(); - try { - jsonPref.put("name", "browser.ui.zoom.force-user-scalable"); - jsonPref.put("type", "bool"); - jsonPref.put("value", true); - setPreferenceAndWaitForChange(jsonPref); - } catch (Exception ex) { - mAsserter.ok(false, "exception in testCheck2", ex.toString()); - } + setPreferenceAndWaitForChange("browser.ui.zoom.force-user-scalable", true); blockForGeckoReady(); loadAndPaint(url); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck3.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck3.java index 6ac93d7d012..28915bdbcb1 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck3.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testCheck3.java @@ -16,15 +16,7 @@ public class testCheck3 extends PixelTest { String url = getAbsoluteUrl("/facebook.com/www.facebook.com/barackobama.html"); // Enable double-tap zooming - JSONObject jsonPref = new JSONObject(); - try { - jsonPref.put("name", "browser.ui.zoom.force-user-scalable"); - jsonPref.put("type", "bool"); - jsonPref.put("value", true); - setPreferenceAndWaitForChange(jsonPref); - } catch (Exception ex) { - mAsserter.ok(false, "exception in testCheck3", ex.toString()); - } + setPreferenceAndWaitForChange("browser.ui.zoom.force-user-scalable", true); blockForGeckoReady(); loadAndPaint(url); diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java index 9a6154b9269..72e9b2b533f 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDistribution.java @@ -392,20 +392,36 @@ public class testDistribution extends ContentProviderTest { } private JSONArray getPrefs(String[] prefNames) throws JSONException { - Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); - mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); + final JSONArray result = new JSONArray(); - JSONObject data = null; - int requestId = -1; + mActions.getPrefs(prefNames, new Actions.PrefHandlerBase() { + private void addItem(String pref, Object value) { + try { + final JSONObject item = new JSONObject(); + item.put("name", pref).put("value", value); + result.put(item); + } catch (final JSONException e) { + mAsserter.ok(false, "exception getting prefs", e.toString()); + } + } - // Wait until we get the correct "Preferences:Data" event - while (requestId != PREF_REQUEST_ID) { - data = new JSONObject(eventExpecter.blockForEventData()); - requestId = data.getInt("requestId"); - } - eventExpecter.unregisterListener(); + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, boolean value) { + addItem(pref, value); + } - return data.getJSONArray("preferences"); + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, int value) { + addItem(pref, value); + } + + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, String value) { + addItem(pref, value); + } + }).waitForFinish(); + + return result; } // Sets the distribution locale preference for the test. @@ -414,56 +430,39 @@ public class testDistribution extends ContentProviderTest { } // Test localized distribution and preferences values stored in preferences.json - private void checkLocalizedPreferences(String aLocale) { - String prefAbout = "distribution.about"; - String prefLocalizeable = "distribution.test.localizeable"; - String prefLocalizeableOverride = "distribution.test.localizeable-override"; - - try { - final String[] prefNames = { prefAbout, prefLocalizeable, prefLocalizeableOverride }; - - Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); - mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames); - - JSONObject data = null; - int requestId = -1; - - // Wait until we get the correct "Preferences:Data" event - while (requestId != PREF_REQUEST_ID) { - data = new JSONObject(eventExpecter.blockForEventData()); - requestId = data.getInt("requestId"); - } - eventExpecter.unregisterListener(); - - JSONArray preferences = data.getJSONArray("preferences"); - for (int i = 0; i < preferences.length(); i++) { - JSONObject pref = (JSONObject) preferences.get(i); - String name = pref.getString("name"); + private void checkLocalizedPreferences(final String aLocale) { + final String prefAbout = "distribution.about"; + final String prefLocalizeable = "distribution.test.localizeable"; + final String prefLocalizeableOverride = "distribution.test.localizeable-override"; + final String[] prefNames = { prefAbout, prefLocalizeable, prefLocalizeableOverride }; + mActions.getPrefs(prefNames, new Actions.PrefHandlerBase() { + @Override // Actions.PrefHandlerBase + public void prefValue(String name, String value) { if (name.equals(prefAbout)) { if (aLocale.equals("en-US")) { - mAsserter.is(pref.getString("value"), "Test Partner", "check " + prefAbout); + mAsserter.is(value, "Test Partner", "check " + prefAbout); } else if (aLocale.equals("es-MX")) { - mAsserter.is(pref.getString("value"), "Afiliado de Prueba", "check " + prefAbout); + mAsserter.is(value, "Afiliado de Prueba", "check " + prefAbout); } } else if (name.equals(prefLocalizeable)) { if (aLocale.equals("en-US")) { - mAsserter.is(pref.getString("value"), "http://test.org/en-US/en-US/", "check " + prefLocalizeable); + mAsserter.is(value, "http://test.org/en-US/en-US/", "check " + prefLocalizeable); } else if (aLocale.equals("es-MX")) { - mAsserter.is(pref.getString("value"), "http://test.org/es-MX/es-MX/", "check " + prefLocalizeable); + mAsserter.is(value, "http://test.org/es-MX/es-MX/", "check " + prefLocalizeable); } } else if (name.equals(prefLocalizeableOverride)) { if (aLocale.equals("en-US")) { - mAsserter.is(pref.getString("value"), "http://cheese.com", "check " + prefLocalizeableOverride); + mAsserter.is(value, "http://cheese.com", "check " + prefLocalizeableOverride); } else if (aLocale.equals("es-MX")) { - mAsserter.is(pref.getString("value"), "http://test.org/es-MX/", "check " + prefLocalizeableOverride); + mAsserter.is(value, "http://test.org/es-MX/", "check " + prefLocalizeableOverride); } + } else { + // Raise exception. + super.prefValue(name, value); } } - - } catch (JSONException e) { - mAsserter.ok(false, "exception getting preferences", e.toString()); - } + }).waitForFinish(); } // Copies the mock package to the data directory and returns the file path to it. diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java index 4fc0e0ce770..a048aa3e17d 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testDoorHanger.java @@ -20,6 +20,8 @@ import org.mozilla.gecko.Actions; * Password Manager doorhangers - Remember and Not Now options dismiss the doorhanger */ public class testDoorHanger extends BaseTest { + private boolean offlineAllowedByDefault = true; + public void testDoorHanger() { String GEO_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_GEOLOCATION_URL); String BLANK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL); @@ -62,38 +64,17 @@ public class testDoorHanger extends BaseTest { mAsserter.is(mSolo.searchText(GEO_MESSAGE), false, "Geolocation doorhanger notification is hidden when opening a new tab"); */ - boolean offlineAllowedByDefault = true; // Save offline-allow-by-default preferences first - final String[] prefNames = { "offline-apps.allow_by_default" }; - final int ourRequestId = 0x7357; - final Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data"); - mActions.sendPreferencesGetEvent(ourRequestId, prefNames); - try { - JSONObject data = null; - int requestId = -1; - - // Wait until we get the correct "Preferences:Data" event - while (requestId != ourRequestId) { - data = new JSONObject(eventExpecter.blockForEventData()); - requestId = data.getInt("requestId"); + mActions.getPrefs(new String[] { "offline-apps.allow_by_default" }, + new Actions.PrefHandlerBase() { + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, boolean value) { + mAsserter.is(pref, "offline-apps.allow_by_default", "Expecting correct pref name"); + offlineAllowedByDefault = value; } - eventExpecter.unregisterListener(); + }).waitForFinish(); - JSONArray preferences = data.getJSONArray("preferences"); - if (preferences.length() > 0) { - JSONObject pref = (JSONObject) preferences.get(0); - offlineAllowedByDefault = pref.getBoolean("value"); - } - - // Turn off offline-allow-by-default - JSONObject jsonPref = new JSONObject(); - jsonPref.put("name", "offline-apps.allow_by_default"); - jsonPref.put("type", "bool"); - jsonPref.put("value", false); - setPreferenceAndWaitForChange(jsonPref); - } catch (JSONException e) { - mAsserter.ok(false, "exception getting preference", e.toString()); - } + setPreferenceAndWaitForChange("offline-apps.allow_by_default", false); // Load offline storage page loadUrlAndWait(OFFLINE_STORAGE_URL); @@ -117,16 +98,8 @@ public class testDoorHanger extends BaseTest { loadUrlAndWait(OFFLINE_STORAGE_URL); mAsserter.is(mSolo.searchText(mStringHelper.OFFLINE_MESSAGE), false, "Offline storage doorhanger is no longer triggered"); - try { - // Revert offline setting - JSONObject jsonPref = new JSONObject(); - jsonPref.put("name", "offline-apps.allow_by_default"); - jsonPref.put("type", "bool"); - jsonPref.put("value", offlineAllowedByDefault); - setPreferenceAndWaitForChange(jsonPref); - } catch (JSONException e) { - mAsserter.ok(false, "exception setting preference", e.toString()); - } + // Revert offline setting + setPreferenceAndWaitForChange("offline-apps.allow_by_default", offlineAllowedByDefault); // Load new login page loadUrlAndWait(getAbsoluteUrl(mStringHelper.ROBOCOP_LOGIN_01_URL)); @@ -152,15 +125,7 @@ public class testDoorHanger extends BaseTest { private void testPopupBlocking() { String POPUP_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_POPUP_URL); - try { - JSONObject jsonPref = new JSONObject(); - jsonPref.put("name", "dom.disable_open_during_load"); - jsonPref.put("type", "bool"); - jsonPref.put("value", true); - setPreferenceAndWaitForChange(jsonPref); - } catch (JSONException e) { - mAsserter.ok(false, "exception setting preference", e.toString()); - } + setPreferenceAndWaitForChange("dom.disable_open_during_load", true); // Load page with popup loadUrlAndWait(POPUP_URL); @@ -204,15 +169,7 @@ public class testDoorHanger extends BaseTest { // Check that we're on the same page to verify that the popup was not shown. verifyUrl(POPUP_URL); - try { - JSONObject jsonPref = new JSONObject(); - jsonPref.put("name", "dom.disable_open_during_load"); - jsonPref.put("type", "bool"); - jsonPref.put("value", false); - setPreferenceAndWaitForChange(jsonPref); - } catch (JSONException e) { - mAsserter.ok(false, "exception setting preference", e.toString()); - } + setPreferenceAndWaitForChange("dom.disable_open_during_load", false); } // wait for a CheckBox view that is clickable diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java index 378f70347ca..65a4eaba69b 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPasswordEncrypt.java @@ -106,15 +106,7 @@ public class testPasswordEncrypt extends BaseTest { } private void toggleMasterPassword(String passwd) { - JSONObject jsonPref = new JSONObject(); - try { - jsonPref.put("name", "privacy.masterpassword.enabled"); - jsonPref.put("type", "string"); - jsonPref.put("value", passwd); - setPreferenceAndWaitForChange(jsonPref); - } catch (Exception ex) { - mAsserter.ok(false, "exception in toggleMasterPassword", ex.toString()); - } + setPreferenceAndWaitForChange("privacy.masterpassword.enabled", passwd); } @Override diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrefsObserver.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrefsObserver.java index 72048f0061f..f63358d5759 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrefsObserver.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testPrefsObserver.java @@ -4,8 +4,6 @@ package org.mozilla.gecko.tests; -import org.json.JSONException; -import org.json.JSONObject; import org.mozilla.gecko.Actions; /** @@ -16,97 +14,68 @@ import org.mozilla.gecko.Actions; */ public class testPrefsObserver extends BaseTest { private static final String PREF_TEST_PREF = "robocop.tests.dummy"; - private static final int PREF_OBSERVE_REQUEST_ID = 0x7357; - private static final long PREF_TIMEOUT = 10000; - private Actions.RepeatedEventExpecter mExpecter; + private Actions.PrefWaiter prefWaiter; + private boolean prefValue; - public void setPref(boolean value) throws JSONException { + public void setPref(boolean value) { mAsserter.dumpLog("Setting pref"); - - JSONObject jsonPref = new JSONObject(); - jsonPref.put("name", PREF_TEST_PREF); - jsonPref.put("type", "bool"); - jsonPref.put("value", value); - mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString()); + mActions.setPref(PREF_TEST_PREF, value, /* flush */ false); } - public void waitAndCheckPref(boolean value) throws JSONException { + public void waitAndCheckPref(boolean value) { mAsserter.dumpLog("Waiting to check pref"); - JSONObject data = null; - int requestId = -1; + mAsserter.isnot(prefWaiter, null, "Check pref waiter is not null"); + prefWaiter.waitForFinish(); - while (requestId != PREF_OBSERVE_REQUEST_ID) { - data = new JSONObject(mExpecter.blockForEventData()); - if (!mExpecter.eventReceived()) { - mAsserter.ok(false, "Checking pref is correct value", "Didn't receive pref"); - return; - } - requestId = data.getInt("requestId"); - } - - JSONObject pref = data.getJSONArray("preferences").getJSONObject(0); - mAsserter.is(pref.getString("name"), PREF_TEST_PREF, "Pref name is correct"); - mAsserter.is(pref.getString("type"), "bool", "Pref type is correct"); - mAsserter.is(pref.getBoolean("value"), value, "Pref value is correct"); + mAsserter.is(prefValue, value, "Check correct pref value"); } - public void verifyDisconnect() throws JSONException { + public void verifyDisconnect() { mAsserter.dumpLog("Checking pref observer is removed"); - JSONObject pref = null; - int requestId = -1; - - while (requestId != PREF_OBSERVE_REQUEST_ID) { - String data = mExpecter.blockForEventDataWithTimeout(PREF_TIMEOUT); - if (data == null) { - mAsserter.ok(true, "Verifying pref is unobserved", "Didn't get unobserved pref"); - return; - } - pref = new JSONObject(data); - requestId = pref.getInt("requestId"); - } - - mAsserter.ok(false, "Received unobserved pref change", ""); + final boolean newValue = !prefValue; + setPreferenceAndWaitForChange(PREF_TEST_PREF, newValue); + mAsserter.isnot(prefValue, newValue, "Check pref value did not change"); } - public void observePref() throws JSONException { + public void observePref() { mAsserter.dumpLog("Setting up pref observer"); // Setup the pref observer - mExpecter = mActions.expectGeckoEvent("Preferences:Data"); - mActions.sendPreferencesObserveEvent(PREF_OBSERVE_REQUEST_ID, new String[] { PREF_TEST_PREF }); + mAsserter.is(prefWaiter, null, "Check pref waiter is null"); + prefWaiter = mActions.addPrefsObserver( + new String[] { PREF_TEST_PREF }, new Actions.PrefHandlerBase() { + @Override // Actions.PrefHandlerBase + public void prefValue(String pref, boolean value) { + mAsserter.is(pref, PREF_TEST_PREF, "Check correct pref name"); + prefValue = value; + } + }); } public void removePrefObserver() { mAsserter.dumpLog("Removing pref observer"); - mActions.sendPreferencesRemoveObserversEvent(PREF_OBSERVE_REQUEST_ID); + mActions.removePrefsObserver(prefWaiter); } public void testPrefsObserver() { blockForGeckoReady(); - try { - setPref(false); - observePref(); - waitAndCheckPref(false); + setPref(false); + observePref(); + waitAndCheckPref(false); - setPref(true); - waitAndCheckPref(true); + setPref(true); + waitAndCheckPref(true); - removePrefObserver(); - setPref(false); - verifyDisconnect(); - } catch (Exception ex) { - mAsserter.ok(false, "exception in testPrefsObserver", ex.toString()); - } finally { - // Make sure we remove the observer - if it's already removed, this - // will do nothing. - removePrefObserver(); - } - mExpecter.unregisterListener(); + removePrefObserver(); + verifyDisconnect(); + + // Removing again should be a no-op. + removePrefObserver(); } } From 04869506de8599352a01f02bfab7f685ce5abd23 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:14 -0500 Subject: [PATCH 52/91] Bug 1243069 - Control GeckoThread state entirely from C++ code; r=snorp Right now, we rely on browser.js sending Gecko:Ready, to set the GeckoThread state to RUNNING upon receiving Gecko:Ready. This patch gets rid of this dependency on browser.js and Gecko:Ready. --- .../java/org/mozilla/gecko/GeckoThread.java | 30 +++++++++---------- widget/android/GeneratedJNIWrappers.cpp | 8 +++++ widget/android/GeneratedJNIWrappers.h | 19 ++++++++++++ widget/android/nsAppShell.cpp | 13 ++++++++ 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoThread.java b/mobile/android/base/java/org/mozilla/gecko/GeckoThread.java index d263479f7c3..8804723f3b4 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoThread.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoThread.java @@ -8,7 +8,6 @@ package org.mozilla.gecko; import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.mozglue.GeckoLoader; -import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.ThreadUtils; import org.json.JSONException; @@ -27,12 +26,13 @@ import android.util.Log; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Locale; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -public class GeckoThread extends Thread implements GeckoEventListener { +public class GeckoThread extends Thread { private static final String LOGTAG = "GeckoThread"; @WrapForJNI @@ -49,7 +49,7 @@ public class GeckoThread extends Thread implements GeckoEventListener { JNI_READY, // After initializing profile and prefs. PROFILE_READY, - // After initializing frontend JS (corresponding to "Gecko:Ready" event) + // After initializing frontend JS RUNNING, // After leaving Gecko event loop EXITING, @@ -115,7 +115,6 @@ public class GeckoThread extends Thread implements GeckoEventListener { mDebugging = debugging; setName("Gecko"); - EventDispatcher.getInstance().registerGeckoThreadListener(this, "Gecko:Ready"); } public static boolean ensureInit(String args, String action) { @@ -191,7 +190,16 @@ public class GeckoThread extends Thread implements GeckoEventListener { method = cls.getDeclaredMethod( methodName, argTypes.toArray(new Class[argTypes.size()])); } catch (final NoSuchMethodException e) { - throw new UnsupportedOperationException("Cannot find method", e); + throw new IllegalArgumentException("Cannot find method", e); + } + + if (!Modifier.isNative(method.getModifiers())) { + // As a precaution, we disallow queuing non-native methods. Queuing non-native + // methods is dangerous because the method could end up being called on either + // the original thread or the Gecko thread depending on timing. Native methods + // usually handle this by posting an event to the Gecko thread automatically, + // but there is no automatic mechanism for non-native methods. + throw new UnsupportedOperationException("Not allowed to queue non-native methods"); } if (isStateAtLeast(state)) { @@ -451,7 +459,7 @@ public class GeckoThread extends Thread implements GeckoEventListener { public static void addPendingEvent(final GeckoEvent e) { synchronized (QUEUED_CALLS) { - if (QUEUED_CALLS.size() == 0 && isRunning()) { + if (isRunning()) { // We may just have switched to running state. GeckoAppShell.notifyGeckoOfEvent(e); e.recycle(); @@ -479,15 +487,6 @@ public class GeckoThread extends Thread implements GeckoEventListener { return true; } - @Override - public void handleMessage(String event, JSONObject message) { - if ("Gecko:Ready".equals(event)) { - EventDispatcher.getInstance().unregisterGeckoThreadListener(this, event); - setState(State.RUNNING); - Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - Gecko ready"); - } - } - /** * Check that the current Gecko thread state matches the given state. * @@ -541,6 +540,7 @@ public class GeckoThread extends Thread implements GeckoEventListener { } } + @WrapForJNI private static boolean checkAndSetState(final State currentState, final State newState) { synchronized (QUEUED_CALLS) { if (sState == currentState) { diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index fa06d65fc1d..5c27e82d860 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -914,6 +914,14 @@ constexpr char GeckoSmsManager::NotifyThreadCursorResult_t::signature[]; constexpr char GeckoThread::name[]; +constexpr char GeckoThread::CheckAndSetState_t::name[]; +constexpr char GeckoThread::CheckAndSetState_t::signature[]; + +auto GeckoThread::CheckAndSetState(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1) -> bool +{ + return mozilla::jni::Method::Call(nullptr, nullptr, a0, a1); +} + constexpr char GeckoThread::PumpMessageLoop_t::name[]; constexpr char GeckoThread::PumpMessageLoop_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index ad4db214fe5..1642e0dd96a 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -2488,6 +2488,25 @@ protected: public: class State; +public: + struct CheckAndSetState_t { + typedef GeckoThread Owner; + typedef bool ReturnType; + typedef bool SetterType; + typedef mozilla::jni::Args< + mozilla::jni::Object::Param, + mozilla::jni::Object::Param> Args; + static constexpr char name[] = "checkAndSetState"; + static constexpr char signature[] = + "(Lorg/mozilla/gecko/GeckoThread$State;Lorg/mozilla/gecko/GeckoThread$State;)Z"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + static auto CheckAndSetState(mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> bool; + public: struct PumpMessageLoop_t { typedef GeckoThread Owner; diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 609bfed714d..5873dfee924 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -265,6 +265,7 @@ nsAppShell::Init() if (obsServ) { obsServ->AddObserver(this, "browser-delayed-startup-finished", false); obsServ->AddObserver(this, "profile-after-change", false); + obsServ->AddObserver(this, "chrome-document-loaded", false); obsServ->AddObserver(this, "quit-application-granted", false); obsServ->AddObserver(this, "xpcom-shutdown", false); } @@ -329,8 +330,20 @@ nsAppShell::Observe(nsISupports* aSubject, } removeObserver = true; + } else if (!strcmp(aTopic, "chrome-document-loaded")) { + if (jni::IsAvailable()) { + // Our first window has loaded, assume any JS initialization has run. + widget::GeckoThread::CheckAndSetState( + widget::GeckoThread::State::PROFILE_READY(), + widget::GeckoThread::State::RUNNING()); + } + removeObserver = true; + } else if (!strcmp(aTopic, "quit-application-granted")) { if (jni::IsAvailable()) { + widget::GeckoThread::SetState( + widget::GeckoThread::State::EXITING()); + // We are told explicitly to quit, perhaps due to // nsIAppStartup::Quit being called. We should release our hold on // nsIAppStartup and let it continue to quit. From 728b69328691aacc6d8bec173f66bc773b9290da Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:14 -0500 Subject: [PATCH 53/91] Bug 1243070 - Use native method to notify window size change; r=snorp Convert the SIZE_CHANGED event to a native method in GLController, and carry over the SIZE_CHANGED implementation to the new implementation in GLController. Some other changes were made for correctness in handling size changes. --- .../org/mozilla/gecko/gfx/GLController.java | 4 ++ .../mozilla/gecko/gfx/GeckoLayerClient.java | 7 +- widget/android/GeneratedJNINatives.h | 4 ++ widget/android/GeneratedJNIWrappers.cpp | 3 + widget/android/GeneratedJNIWrappers.h | 19 ++++++ widget/android/nsWindow.cpp | 68 ++++++++++++++++++- 6 files changed, 100 insertions(+), 5 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java index cba2c2ee43c..76db6c130b6 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java @@ -74,6 +74,10 @@ public class GLController extends JNIObject { /* package */ native void attachToJava(GeckoLayerClient layerClient, NativePanZoomController npzc); + @WrapForJNI + /* package */ native void onSizeChanged(int windowWidth, int windowHeight, + int screenWidth, int screenHeight); + // Gecko thread creates compositor; blocks UI thread. @WrapForJNI private native void createCompositor(int width, int height); diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index d7531de9728..d9a239b86e3 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -277,9 +277,8 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget Log.d(LOGTAG, "Window-size changed to " + mWindowSize); } - GeckoEvent event = GeckoEvent.createSizeChangedEvent(mWindowSize.width, mWindowSize.height, - mScreenSize.width, mScreenSize.height); - GeckoAppShell.sendEventToGecko(event); + mView.getGLController().onSizeChanged(mWindowSize.width, mWindowSize.height, + mScreenSize.width, mScreenSize.height); String json = ""; try { @@ -297,7 +296,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget json = jsonObj.toString(); } } catch (Exception e) { - Log.e(LOGTAG, "Unable to convert point to JSON for " + event, e); + Log.e(LOGTAG, "Unable to convert point to JSON", e); } GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Window:Resize", json)); } diff --git a/widget/android/GeneratedJNINatives.h b/widget/android/GeneratedJNINatives.h index 805b59e759e..ae7a832e1ec 100644 --- a/widget/android/GeneratedJNINatives.h +++ b/widget/android/GeneratedJNINatives.h @@ -259,6 +259,10 @@ public: mozilla::jni::NativeStub ::template Wrap<&Impl::DisposeNative>), + mozilla::jni::MakeNativeMethod( + mozilla::jni::NativeStub + ::template Wrap<&Impl::OnSizeChanged>), + mozilla::jni::MakeNativeMethod( mozilla::jni::NativeStub ::template Wrap<&Impl::PauseCompositor>), diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index 5c27e82d860..420feb693c7 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -1307,6 +1307,9 @@ auto GLController::Destroy() const -> void constexpr char GLController::DisposeNative_t::name[]; constexpr char GLController::DisposeNative_t::signature[]; +constexpr char GLController::OnSizeChanged_t::name[]; +constexpr char GLController::OnSizeChanged_t::signature[]; + constexpr char GLController::PauseCompositor_t::name[]; constexpr char GLController::PauseCompositor_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 1642e0dd96a..1e0e99d12f5 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -3582,6 +3582,25 @@ public: mozilla::jni::ExceptionMode::ABORT; }; +public: + struct OnSizeChanged_t { + typedef GLController Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + int32_t, + int32_t, + int32_t, + int32_t> Args; + static constexpr char name[] = "onSizeChanged"; + static constexpr char signature[] = + "(IIII)V"; + static const bool isStatic = false; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + public: struct PauseCompositor_t { typedef GLController Owner; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 031ebeb4ee5..1642e65671a 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -867,6 +867,58 @@ public: #endif } + void OnSizeChanged(int32_t aWindowWidth, int32_t aWindowHeight, + int32_t aScreenWidth, int32_t aScreenHeight) + { + if (aWindowWidth != window.mBounds.width || + aWindowHeight != window.mBounds.height) { + + window.Resize(aWindowWidth, aWindowHeight, /* repaint */ false); + } + + if (aScreenWidth == gAndroidScreenBounds.width && + aScreenHeight == gAndroidScreenBounds.height) { + + return; + } + + gAndroidScreenBounds.width = aScreenWidth; + gAndroidScreenBounds.height = aScreenHeight; + + if (!XRE_IsParentProcess() && + !Preferences::GetBool("browser.tabs.remote.desktopbehavior", + false)) { + return; + } + + // Tell the content process the new screen size. + nsTArray cplist; + ContentParent::GetAll(cplist); + for (uint32_t i = 0; i < cplist.Length(); ++i) { + Unused << cplist[i]->SendScreenSizeChanged(gAndroidScreenBounds); + } + + if (gContentCreationNotifier) { + return; + } + + // If the content process is not created yet, wait until it's + // created and then tell it the screen size. + nsCOMPtr obs = services::GetObserverService(); + if (!obs) { + return; + } + + RefPtr notifier = new ContentCreationNotifier; + if (NS_FAILED(obs->AddObserver(notifier, "xpcom-shutdown", false)) || + NS_FAILED(obs->AddObserver( + notifier, "ipc:content-created", false))) { + return; + } + + gContentCreationNotifier = notifier; + } + void CreateCompositor(int32_t aWidth, int32_t aHeight) { window.CreateLayerManager(aWidth, aHeight); @@ -965,7 +1017,7 @@ nsWindow::GeckoViewSupport::Open(const jni::ClassObject::LocalRef& aCls, } nsCOMPtr domWindow; - ww->OpenWindow(nullptr, url, "_blank", "chrome,dialog=no,all", + ww->OpenWindow(nullptr, url, nullptr, "chrome,dialog=0,resizable", args, getter_AddRefs(domWindow)); MOZ_ASSERT(domWindow); @@ -985,6 +1037,16 @@ nsWindow::GeckoViewSupport::Open(const jni::ClassObject::LocalRef& aCls, aCls.Env(), GLController::Ref::From(aGLController))); gGeckoViewWindow = window; + + if (window->mWidgetListener) { + nsCOMPtr xulWindow( + window->mWidgetListener->GetXULWindow()); + if (xulWindow) { + // Out window is not intrinsically sized, so tell nsXULWindow to + // not set a size for us. + xulWindow->SetIntrinsicallySized(false); + } + } } void @@ -1751,6 +1813,10 @@ nsWindow::OnSizeChanged(const gfx::IntSize& aSize) if (mWidgetListener) { mWidgetListener->WindowResized(this, aSize.width, aSize.height); } + + if (mAttachedWidgetListener) { + mAttachedWidgetListener->WindowResized(this, aSize.width, aSize.height); + } } void From 5f448383ca9d56bcd971fc757f501347e67e08af Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Mon, 1 Feb 2016 17:38:14 -0500 Subject: [PATCH 54/91] Bug 1243070 - Remove obsolete size-change code; r=snorp Remove obsolete SIZE_CHANGED event and its handler in nsWindow. Also remove some other supporting code (such as gAndroidBounds and the FORCED_RESIZE event) that should be unnecessary by now. --- .../java/org/mozilla/gecko/GeckoEvent.java | 11 +- widget/android/AndroidJavaWrappers.cpp | 15 --- widget/android/AndroidJavaWrappers.h | 9 -- widget/android/nsAppShell.cpp | 16 --- widget/android/nsAppShell.h | 2 - widget/android/nsWindow.cpp | 116 +++--------------- 6 files changed, 15 insertions(+), 154 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoEvent.java b/mobile/android/base/java/org/mozilla/gecko/GeckoEvent.java index 77f5d13d142..9f847677164 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoEvent.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoEvent.java @@ -68,12 +68,11 @@ public class GeckoEvent { // Make sure to keep these values in sync with the enum in // AndroidGeckoEvent in widget/android/AndroidJavaWrappers.h @JNITarget - private enum NativeGeckoEvent { + public enum NativeGeckoEvent { NATIVE_POKE(0), MOTION_EVENT(2), SENSOR_EVENT(3), LOCATION_EVENT(5), - SIZE_CHANGED(8), APP_BACKGROUNDING(9), APP_FOREGROUNDING(10), LOAD_URI(12), @@ -470,14 +469,6 @@ public class GeckoEvent { return event; } - public static GeckoEvent createSizeChangedEvent(int w, int h, int screenw, int screenh) { - GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.SIZE_CHANGED); - event.mPoints = new Point[2]; - event.mPoints[0] = new Point(w, h); - event.mPoints[1] = new Point(screenw, screenh); - return event; - } - @RobocopTarget public static GeckoEvent createBroadcastEvent(String subject, String data) { GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.BROADCAST); diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index eea334fd02e..ddb850e809a 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -344,10 +344,6 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) mAckNeeded = jenv->GetBooleanField(jobj, jAckNeededField); switch (mType) { - case SIZE_CHANGED: - ReadPointArray(mPoints, jenv, jPoints, 2); - break; - case NATIVE_GESTURE_EVENT: mTime = jenv->GetLongField(jobj, jTimeField); mMetaState = jenv->GetIntField(jobj, jMetaStateField); @@ -520,17 +516,6 @@ AndroidGeckoEvent::Init(int aType) mAckNeeded = false; } -void -AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent) -{ - NS_ASSERTION(aResizeEvent->Type() == SIZE_CHANGED, "Init called on non-SIZE_CHANGED event"); - - mType = FORCED_RESIZE; - mAckNeeded = false; - mTime = aResizeEvent->mTime; - mPoints = aResizeEvent->mPoints; // x,y coordinates -} - bool AndroidGeckoEvent::CanCoalesceWith(AndroidGeckoEvent* ae) { diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index d738731f63f..7970606baf3 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -428,7 +428,6 @@ private: void Init(JNIEnv *jenv, jobject jobj); void Init(int aType); - void Init(AndroidGeckoEvent *aResizeEvent); public: static void InitGeckoEventClass(JNIEnv *jEnv); @@ -445,12 +444,6 @@ public: return event; } - static AndroidGeckoEvent* CopyResizeEvent(AndroidGeckoEvent *aResizeEvent) { - AndroidGeckoEvent *event = new AndroidGeckoEvent(); - event->Init(aResizeEvent); - return event; - } - static AndroidGeckoEvent* MakeBroadcastEvent(const nsCString& topic, const nsCString& data) { AndroidGeckoEvent* event = new AndroidGeckoEvent(); event->Init(BROADCAST); @@ -643,12 +636,10 @@ public: MOTION_EVENT = 2, SENSOR_EVENT = 3, LOCATION_EVENT = 5, - SIZE_CHANGED = 8, APP_BACKGROUNDING = 9, APP_FOREGROUNDING = 10, LOAD_URI = 12, NOOP = 15, - FORCED_RESIZE = 16, // used internally in nsAppShell/nsWindow APZ_INPUT_EVENT = 17, // used internally in AndroidJNI/nsAppShell/nsWindow BROADCAST = 19, VIEWPORT = 20, diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 5873dfee924..1b0037723bd 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -735,15 +735,6 @@ nsAppShell::LegacyGeckoEvent::Run() break; } - case AndroidGeckoEvent::SIZE_CHANGED: { - // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event - if (curEvent.get() != gLastSizeChange) { - gLastSizeChange = AndroidGeckoEvent::CopyResizeEvent(curEvent.get()); - } - nsWindow::OnGlobalAndroidEvent(curEvent.get()); - break; - } - case AndroidGeckoEvent::VISITED: { #ifdef MOZ_ANDROID_HISTORY nsCOMPtr history = services::GetHistoryService(); @@ -953,13 +944,6 @@ nsAppShell::LegacyGeckoEvent::PostTo(mozilla::LinkedList& queue) } } -void -nsAppShell::ResendLastResizeEvent(nsWindow* aDest) { - if (gLastSizeChange) { - nsWindow::OnGlobalAndroidEvent(gLastSizeChange); - } -} - nsresult nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver) { diff --git a/widget/android/nsAppShell.h b/widget/android/nsAppShell.h index 66b8bbb2b3f..91b0e362231 100644 --- a/widget/android/nsAppShell.h +++ b/widget/android/nsAppShell.h @@ -139,8 +139,6 @@ public: mozilla::UniquePtr(*eventFactory)( mozilla::UniquePtr&&) = nullptr); - void ResendLastResizeEvent(nsWindow* aDest); - void SetBrowserApp(nsIAndroidBrowserApp* aBrowserApp) { mBrowserApp = aBrowserApp; } diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 1642e65671a..a2a67d6c84d 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -89,8 +89,6 @@ using namespace mozilla::layers; NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget) -// The dimensions of the current android view -static gfx::IntSize gAndroidBounds = gfx::IntSize(0, 0); static gfx::IntSize gAndroidScreenBounds; #include "mozilla/layers/CompositorChild.h" @@ -141,7 +139,7 @@ NS_IMPL_ISUPPORTS(ContentCreationNotifier, nsIObserver) // All the toplevel windows that have been created; these are in -// stacking order, so the window at gAndroidBounds[0] is the topmost +// stacking order, so the window at gTopLevelWindows[0] is the topmost // one. static nsTArray gTopLevelWindows; @@ -1155,11 +1153,15 @@ nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, nsWidgetInitData* aInitData) { - ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent, aRect.x, aRect.y, aRect.width, aRect.height); + ALOG("nsWindow[%p]::Create %p [%d %d %d %d]", (void*)this, (void*)aParent, + aRect.x, aRect.y, aRect.width, aRect.height); + nsWindow *parent = (nsWindow*) aParent; if (aNativeParent) { if (parent) { - ALOG("Ignoring native parent on Android window [%p], since parent was specified (%p %p)", (void*)this, (void*)aNativeParent, (void*)aParent); + ALOG("Ignoring native parent on Android window [%p], " + "since parent was specified (%p %p)", (void*)this, + (void*)aNativeParent, (void*)aParent); } else { parent = (nsWindow*) aNativeParent; } @@ -1167,23 +1169,15 @@ nsWindow::Create(nsIWidget* aParent, mBounds = aRect; - // for toplevel windows, bounds are fixed to full screen size - if (!parent) { - mBounds.x = 0; - mBounds.y = 0; - mBounds.width = gAndroidBounds.width; - mBounds.height = gAndroidBounds.height; - } - BaseCreate(nullptr, aInitData); - NS_ASSERTION(IsTopLevel() || parent, "non top level windowdoesn't have a parent!"); + NS_ASSERTION(IsTopLevel() || parent, + "non-top-level window doesn't have a parent!"); if (IsTopLevel()) { gTopLevelWindows.AppendElement(this); - } - if (parent) { + } else if (parent) { parent->mChildren.AppendElement(this); mParent = parent; } @@ -1337,10 +1331,9 @@ nsWindow::Show(bool aState) // if Show() is the right place to do this, though. if (aState) { - // It just became visible, so send a resize update if necessary - // and bring it to the front. - Resize(0, 0, gAndroidBounds.width, gAndroidBounds.height, false); + // It just became visible, so bring it to the front. BringToFront(); + } else if (nsWindow::TopWindow() == this) { // find the next visible window to show unsigned int i; @@ -1546,9 +1539,9 @@ nsWindow::BringToFront() RefPtr kungFuDeathGrip(this); nsWindow *oldTop = nullptr; - nsWindow *newTop = this; - if (!gTopLevelWindows.IsEmpty()) + if (!gTopLevelWindows.IsEmpty()) { oldTop = gTopLevelWindows[0]; + } gTopLevelWindows.RemoveElement(this); gTopLevelWindows.InsertElementAt(0, this); @@ -1560,21 +1553,10 @@ nsWindow::BringToFront() } } - if (Destroyed()) { - // somehow the deactivate event handler destroyed this window. - // try to recover by grabbing the next window in line and activating - // that instead - if (gTopLevelWindows.IsEmpty()) - return; - newTop = gTopLevelWindows[0]; - } - if (mWidgetListener) { mWidgetListener->WindowActivated(); } - // force a window resize - nsAppShell::Get()->ResendLastResizeEvent(newTop); RedrawAll(); } @@ -1691,76 +1673,6 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) return; switch (ae->Type()) { - case AndroidGeckoEvent::FORCED_RESIZE: - win->mBounds.width = 0; - win->mBounds.height = 0; - // also resize the children - for (uint32_t i = 0; i < win->mChildren.Length(); i++) { - win->mChildren[i]->mBounds.width = 0; - win->mChildren[i]->mBounds.height = 0; - } - case AndroidGeckoEvent::SIZE_CHANGED: { - const nsTArray& points = ae->Points(); - NS_ASSERTION(points.Length() == 2, "Size changed does not have enough coordinates"); - - int nw = points[0].x; - int nh = points[0].y; - - if (ae->Type() == AndroidGeckoEvent::FORCED_RESIZE || nw != gAndroidBounds.width || - nh != gAndroidBounds.height) { - gAndroidBounds.width = nw; - gAndroidBounds.height = nh; - - // tell all the windows about the new size - for (size_t i = 0; i < gTopLevelWindows.Length(); ++i) { - if (gTopLevelWindows[i]->mIsVisible) - gTopLevelWindows[i]->Resize(gAndroidBounds.width, - gAndroidBounds.height, - false); - } - } - - int newScreenWidth = points[1].x; - int newScreenHeight = points[1].y; - - if (newScreenWidth == gAndroidScreenBounds.width && - newScreenHeight == gAndroidScreenBounds.height) - break; - - gAndroidScreenBounds.width = newScreenWidth; - gAndroidScreenBounds.height = newScreenHeight; - - if (!XRE_IsParentProcess() && - !Preferences::GetBool("browser.tabs.remote.desktopbehavior", false)) { - break; - } - - // Tell the content process the new screen size. - nsTArray cplist; - ContentParent::GetAll(cplist); - for (uint32_t i = 0; i < cplist.Length(); ++i) - Unused << cplist[i]->SendScreenSizeChanged(gAndroidScreenBounds); - - if (gContentCreationNotifier) - break; - - // If the content process is not created yet, wait until it's - // created and then tell it the screen size. - nsCOMPtr obs = - mozilla::services::GetObserverService(); - if (!obs) - break; - - RefPtr notifier = new ContentCreationNotifier; - if (NS_SUCCEEDED(obs->AddObserver(notifier, "ipc:content-created", false))) { - if (NS_SUCCEEDED(obs->AddObserver(notifier, "xpcom-shutdown", false))) - gContentCreationNotifier = notifier; - else - obs->RemoveObserver(notifier, "ipc:content-created"); - } - break; - } - case AndroidGeckoEvent::APZ_INPUT_EVENT: { win->UserActivity(); From 2e6b117ca324cf255c70153aaa1e9a3e7890b4d2 Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Mon, 1 Feb 2016 14:46:21 -0800 Subject: [PATCH 55/91] Bug 1240825 - Add an asserts statement for a crashtest on e10s, a=test-only --- dom/plugins/test/crashtests/crashtests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/plugins/test/crashtests/crashtests.list b/dom/plugins/test/crashtests/crashtests.list index c11f9d8b307..279d644b414 100644 --- a/dom/plugins/test/crashtests/crashtests.list +++ b/dom/plugins/test/crashtests/crashtests.list @@ -2,7 +2,7 @@ load 41276-1.html load 48856-1.html load 110650-1.html skip-if(!haveTestPlugin) script 539897-1.html -skip-if(!haveTestPlugin) script 540114-1.html +asserts(0-1) skip-if(!haveTestPlugin) script 540114-1.html load 570884.html # This test relies on the reading of screenX/Y forcing a round trip to # the X server, which is a bad assumption for . From 695fcf875c029e531d781a7c54af3b577ce8e068 Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Mon, 1 Feb 2016 15:52:34 -0700 Subject: [PATCH 56/91] Bug 1241907 - Grant runtime permissions before running browser tests; r=jmaher --- layout/tools/reftest/mach_commands.py | 3 +++ mobile/android/build.mk | 7 ------- testing/mochitest/mach_commands.py | 5 +++++ .../mozrunner/devices/android_device.py | 20 +++++++++++++++++++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/layout/tools/reftest/mach_commands.py b/layout/tools/reftest/mach_commands.py index 3346902d9ad..eecff54b396 100644 --- a/layout/tools/reftest/mach_commands.py +++ b/layout/tools/reftest/mach_commands.py @@ -246,6 +246,9 @@ class ReftestRunner(MozbuildObject): kwargs["ignoreWindowSize"] = True kwargs["printDeviceInfo"] = False + from mozrunner.devices.android_device import grant_runtime_permissions + grant_runtime_permissions(self, kwargs['app']) + # A symlink and some path manipulations are required so that test # manifests can be found both locally and remotely (via a url) # using the same relative path. diff --git a/mobile/android/build.mk b/mobile/android/build.mk index a94db465fe2..d983063c395 100644 --- a/mobile/android/build.mk +++ b/mobile/android/build.mk @@ -11,8 +11,6 @@ package: @$(MAKE) -C mobile/android/installer ifeq ($(OS_TARGET),Android) -sdk_level=$(shell $(ADB) shell getprop ro.build.version.sdk) -permissions-required=$(shell if [ $(sdk_level) -gt 22 ] ; then echo yes ; else echo no ; fi) ifneq ($(MOZ_ANDROID_INSTALL_TARGET),) ANDROID_SERIAL = $(MOZ_ANDROID_INSTALL_TARGET) endif @@ -36,12 +34,7 @@ endif endif install:: -ifeq ($(permissions-required),yes) - - $(ADB) install -r -g $(DIST)/$(PKG_PATH)$(PKG_BASENAME).apk -else $(ADB) install -r $(DIST)/$(PKG_PATH)$(PKG_BASENAME).apk -endif else @echo 'Mobile can't be installed directly.' @exit 1 diff --git a/testing/mochitest/mach_commands.py b/testing/mochitest/mach_commands.py index fedc0a42f94..fa4e90a4dc9 100644 --- a/testing/mochitest/mach_commands.py +++ b/testing/mochitest/mach_commands.py @@ -537,6 +537,8 @@ class MachCommands(MachCommandBase): if buildapp in ('b2g',): run_mochitest = mochitest.run_b2g_test elif buildapp == 'android': + from mozrunner.devices.android_device import grant_runtime_permissions + grant_runtime_permissions(self, kwargs['app']) run_mochitest = mochitest.run_android_test else: run_mochitest = mochitest.run_desktop_test @@ -608,6 +610,9 @@ class RobocopCommands(MachCommandBase): sorted(list(test_paths))))) return 1 + from mozrunner.devices.android_device import grant_runtime_permissions + grant_runtime_permissions(self, kwargs['app']) + mochitest = self._spawn(MochitestRunner) return mochitest.run_robocop_test(self._mach_context, tests, 'robocop', **kwargs) diff --git a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py index 9ad9246714a..6e08d93a0d8 100644 --- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py +++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py @@ -260,6 +260,26 @@ def run_firefox_for_android(build_obj, params): return 1 return 0 +def grant_runtime_permissions(build_obj, app): + """ + Grant required runtime permissions to the specified app (typically org.mozilla.fennec_$USER). + """ + adb_path = _find_sdk_exe(build_obj.substs, 'adb', False) + if not adb_path: + adb_path = 'adb' + dm = DeviceManagerADB(autoconnect=False, adbPath=adb_path, retryLimit=1) + dm.default_timeout = 10 + try: + sdk_level = dm.shellCheckOutput(['getprop', 'ro.build.version.sdk']) + if sdk_level and int(sdk_level) >= 23: + _log_info("Granting important runtime permissions to %s" % app) + dm.shellCheckOutput(['pm', 'grant', app, 'android.permission.WRITE_EXTERNAL_STORAGE']) + dm.shellCheckOutput(['pm', 'grant', app, 'android.permission.ACCESS_FINE_LOCATION']) + dm.shellCheckOutput(['pm', 'grant', app, 'android.permission.CAMERA']) + dm.shellCheckOutput(['pm', 'grant', app, 'android.permission.WRITE_CONTACTS']) + except DMError: + _log_warning("Unable to grant runtime permissions to %s" % app) + class AndroidEmulator(object): """ From 733f2ee438cf7cca5170d68cf15521d829d72ab5 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:06 -0800 Subject: [PATCH 57/91] Bug 1240796 - Detemplatize getOrCreateSimdTypeDescr(). r=bbouvier This saves some code size in a cold function, and it makes it possible to pass in the SIMD type as a dynamic argument. Also detemplatize the static CreateSimdType() to save some code size. Replace all of the Get*TypeRepr() self-hosting functions with a single GetSimdTypeDescr() which takes one of the JS_SIMDTYPEREPR_* constants as an argument instead. Total code shrink ~ 32 KB. --- js/src/builtin/SIMD.cpp | 41 ++++++++--- js/src/builtin/TypedObject.cpp | 120 +++------------------------------ js/src/builtin/TypedObject.h | 96 ++------------------------ js/src/builtin/TypedObject.js | 28 ++++---- js/src/jit/BaselineIC.cpp | 6 +- js/src/vm/GlobalObject.h | 26 +++---- js/src/vm/SelfHosting.cpp | 13 +--- 7 files changed, 70 insertions(+), 260 deletions(-) diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 279c777e164..44112214d2d 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -140,7 +140,7 @@ static SimdTypeDescr* GetTypeDescr(JSContext* cx) { RootedGlobalObject global(cx, cx->global()); - return GlobalObject::getOrCreateSimdTypeDescr(cx, global); + return GlobalObject::getOrCreateSimdTypeDescr(cx, global, T::type); } template @@ -441,9 +441,9 @@ GlobalObject::initSimdObject(JSContext* cx, Handle global) return true; } -template static bool -CreateSimdType(JSContext* cx, Handle global, HandlePropertyName stringRepr) +CreateSimdType(JSContext* cx, Handle global, HandlePropertyName stringRepr, + SimdType simdType, const JSFunctionSpec* methods) { RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); if (!funcProto) @@ -455,7 +455,6 @@ CreateSimdType(JSContext* cx, Handle global, HandlePropertyName s if (!typeDescr) return false; - const SimdType simdType = T::type; typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd)); typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(simdType))); @@ -491,7 +490,7 @@ CreateSimdType(JSContext* cx, Handle global, HandlePropertyName s MOZ_ASSERT(globalSimdObject); RootedValue typeValue(cx, ObjectValue(*typeDescr)); - if (!JS_DefineFunctions(cx, typeDescr, T::Methods) || + if (!JS_DefineFunctions(cx, typeDescr, methods) || !DefineProperty(cx, globalSimdObject, stringRepr, typeValue, nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING)) { @@ -505,12 +504,13 @@ CreateSimdType(JSContext* cx, Handle global, HandlePropertyName s } bool -GlobalObject::initSimdType(JSContext* cx, Handle global, uint32_t simdTypeDescrType) +GlobalObject::initSimdType(JSContext* cx, Handle global, SimdType simdType) { #define CREATE_(Type) \ - case SimdType::Type: return CreateSimdType(cx, global, cx->names().Type); + case SimdType::Type: \ + return CreateSimdType(cx, global, cx->names().Type, simdType, Type##Defn::Methods); - switch (SimdType(simdTypeDescrType)) { + switch (simdType) { FOR_EACH_SIMD(CREATE_) case SimdType::Count: break; } @@ -519,6 +519,28 @@ GlobalObject::initSimdType(JSContext* cx, Handle global, uint32_t #undef CREATE_ } +SimdTypeDescr* +GlobalObject::getOrCreateSimdTypeDescr(JSContext* cx, Handle global, + SimdType simdType) +{ + MOZ_ASSERT(unsigned(simdType) < unsigned(SimdType::Count), "Invalid SIMD type"); + + RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); + if (!globalSimdObject) + return nullptr; + + uint32_t typeSlotIndex = uint32_t(simdType); + if (globalSimdObject->as().getReservedSlot(typeSlotIndex).isUndefined() && + !GlobalObject::initSimdType(cx, global, simdType)) + { + return nullptr; + } + + const Value& slot = globalSimdObject->as().getReservedSlot(typeSlotIndex); + MOZ_ASSERT(slot.isObject()); + return &slot.toObject().as(); +} + bool SimdObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved) { @@ -529,7 +551,8 @@ SimdObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* Rooted global(cx, cx->global()); #define TRY_RESOLVE_(Type) \ if (str == cx->names().Type) { \ - *resolved = CreateSimdType(cx, global, cx->names().Type); \ + *resolved = CreateSimdType(cx, global, cx->names().Type, \ + SimdType::Type, Type##Defn::Methods); \ return *resolved; \ } FOR_EACH_SIMD(TRY_RESOLVE_) diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index b4b222631d7..a513464fac3 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -2599,122 +2599,18 @@ js::GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp) } bool -js::GetFloat32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) +js::GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isInt32()); + // One of the JS_SIMDTYPEREPR_* constants / a SimdType enum value. + // getOrCreateSimdTypeDescr() will do the range check. + int32_t simdTypeRepr = args[0].toInt32(); Rooted global(cx, cx->global()); MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetFloat64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetInt8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetInt16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetInt32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetUint8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetUint16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetUint32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetBool8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetBool16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetBool32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); - return true; -} - -bool -js::GetBool64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - Rooted global(cx, cx->global()); - MOZ_ASSERT(global); - args.rval().setObject(*GlobalObject::getOrCreateSimdTypeDescr(cx, global)); + auto* obj = GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType(simdTypeRepr)); + args.rval().setObject(*obj); return true; } diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 0cca66673fd..66d2ddaa0a4 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -817,100 +817,14 @@ bool ClampToUint8(JSContext* cx, unsigned argc, Value* vp); bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp); /* - * Usage: GetFloat32x4TypeDescr() + * Usage: GetSimdTypeDescr(simdTypeRepr) * - * Returns the Float32x4 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetFloat32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetFloat64x2TypeDescr() + * Returns one of the SIMD type objects, identified by `simdTypeRepr` which must + * be one of the JS_SIMDTYPEREPR_* constants. * - * Returns the Float64x2 type object. SIMD pseudo-module must have - * been initialized for this to be safe. + * The SIMD pseudo-module must have been initialized for this to be safe. */ -bool GetFloat64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetBool8x16TypeDescr() - * - * Returns the Bool8x16 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetBool8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetBool16x8TypeDescr() - * - * Returns the Bool16x8 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetBool16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetBool32x4TypeDescr() - * - * Returns the Bool32x4 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetBool32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetBool64x2TypeDescr() - * - * Returns the Bool64x2 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetBool64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetInt8x16TypeDescr() - * - * Returns the Int8x16 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetInt8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetInt16x8TypeDescr() - * - * Returns the Int16x8 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetInt16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetInt32x4TypeDescr() - * - * Returns the Int32x4 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetInt32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetUint8x16TypeDescr() - * - * Returns the Uint8x16 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetUint8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetUint16x8TypeDescr() - * - * Returns the Uint16x8 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetUint16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp); - -/* - * Usage: GetUint32x4TypeDescr() - * - * Returns the Uint32x4 type object. SIMD pseudo-module must have - * been initialized for this to be safe. - */ -bool GetUint32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp); +bool GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp); /* * Usage: Store_int8(targetDatum, targetOffset, value) diff --git a/js/src/builtin/TypedObject.js b/js/src/builtin/TypedObject.js index e26d8bb531c..21b5bf8d712 100644 --- a/js/src/builtin/TypedObject.js +++ b/js/src/builtin/TypedObject.js @@ -144,18 +144,19 @@ function TypedObjectGetReference(descr, typedObj, offset) { function TypedObjectGetSimd(descr, typedObj, offset) { var type = DESCR_TYPE(descr); + var simdTypeDescr = GetSimdTypeDescr(type); switch (type) { case JS_SIMDTYPEREPR_FLOAT32X4: var x = Load_float32(typedObj, offset + 0); var y = Load_float32(typedObj, offset + 4); var z = Load_float32(typedObj, offset + 8); var w = Load_float32(typedObj, offset + 12); - return GetFloat32x4TypeDescr()(x, y, z, w); + return simdTypeDescr(x, y, z, w); case JS_SIMDTYPEREPR_FLOAT64X2: var x = Load_float64(typedObj, offset + 0); var y = Load_float64(typedObj, offset + 8); - return GetFloat64x2TypeDescr()(x, y); + return simdTypeDescr(x, y); case JS_SIMDTYPEREPR_INT8X16: var s0 = Load_int8(typedObj, offset + 0); @@ -174,8 +175,7 @@ function TypedObjectGetSimd(descr, typedObj, offset) { var s13 = Load_int8(typedObj, offset + 13); var s14 = Load_int8(typedObj, offset + 14); var s15 = Load_int8(typedObj, offset + 15); - return GetInt8x16TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7, - s8, s9, s10, s11, s12, s13, s14, s15); + return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); case JS_SIMDTYPEREPR_INT16X8: var s0 = Load_int16(typedObj, offset + 0); @@ -186,14 +186,14 @@ function TypedObjectGetSimd(descr, typedObj, offset) { var s5 = Load_int16(typedObj, offset + 10); var s6 = Load_int16(typedObj, offset + 12); var s7 = Load_int16(typedObj, offset + 14); - return GetInt16x8TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7); + return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7); case JS_SIMDTYPEREPR_INT32X4: var x = Load_int32(typedObj, offset + 0); var y = Load_int32(typedObj, offset + 4); var z = Load_int32(typedObj, offset + 8); var w = Load_int32(typedObj, offset + 12); - return GetInt32x4TypeDescr()(x, y, z, w); + return simdTypeDescr(x, y, z, w); case JS_SIMDTYPEREPR_UINT8X16: var s0 = Load_uint8(typedObj, offset + 0); @@ -212,8 +212,7 @@ function TypedObjectGetSimd(descr, typedObj, offset) { var s13 = Load_uint8(typedObj, offset + 13); var s14 = Load_uint8(typedObj, offset + 14); var s15 = Load_uint8(typedObj, offset + 15); - return GetUint8x16TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7, - s8, s9, s10, s11, s12, s13, s14, s15); + return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); case JS_SIMDTYPEREPR_UINT16X8: var s0 = Load_uint16(typedObj, offset + 0); @@ -224,14 +223,14 @@ function TypedObjectGetSimd(descr, typedObj, offset) { var s5 = Load_uint16(typedObj, offset + 10); var s6 = Load_uint16(typedObj, offset + 12); var s7 = Load_uint16(typedObj, offset + 14); - return GetUint16x8TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7); + return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7); case JS_SIMDTYPEREPR_UINT32X4: var x = Load_uint32(typedObj, offset + 0); var y = Load_uint32(typedObj, offset + 4); var z = Load_uint32(typedObj, offset + 8); var w = Load_uint32(typedObj, offset + 12); - return GetUint32x4TypeDescr()(x, y, z, w); + return simdTypeDescr(x, y, z, w); case JS_SIMDTYPEREPR_BOOL8X16: var s0 = Load_int8(typedObj, offset + 0); @@ -250,8 +249,7 @@ function TypedObjectGetSimd(descr, typedObj, offset) { var s13 = Load_int8(typedObj, offset + 13); var s14 = Load_int8(typedObj, offset + 14); var s15 = Load_int8(typedObj, offset + 15); - return GetBool8x16TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7, - s8, s9, s10, s11, s12, s13, s14, s15); + return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); case JS_SIMDTYPEREPR_BOOL16X8: var s0 = Load_int16(typedObj, offset + 0); @@ -262,19 +260,19 @@ function TypedObjectGetSimd(descr, typedObj, offset) { var s5 = Load_int16(typedObj, offset + 10); var s6 = Load_int16(typedObj, offset + 12); var s7 = Load_int16(typedObj, offset + 14); - return GetBool16x8TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7); + return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7); case JS_SIMDTYPEREPR_BOOL32X4: var x = Load_int32(typedObj, offset + 0); var y = Load_int32(typedObj, offset + 4); var z = Load_int32(typedObj, offset + 8); var w = Load_int32(typedObj, offset + 12); - return GetBool32x4TypeDescr()(x, y, z, w); + return simdTypeDescr(x, y, z, w); case JS_SIMDTYPEREPR_BOOL64X2: var x = Load_int32(typedObj, offset + 0); var y = Load_int32(typedObj, offset + 8); - return GetBool64x2TypeDescr()(x, y); + return simdTypeDescr(x, y); } diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 305d295ca3f..c2a9f406ebc 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5640,7 +5640,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, ADD_INT32X4_SIMD_OP_NAME_(fromFloat32x4) ADD_INT32X4_SIMD_OP_NAME_(fromFloat32x4Bits)) { - Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global)); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType::Int32x4)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; } @@ -5651,7 +5651,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, FOREACH_COMP_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) FOREACH_COMP_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) { - Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global)); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType::Bool32x4)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; } @@ -5663,7 +5663,7 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, ADD_FLOAT32X4_SIMD_OP_NAME_(fromInt32x4Bits) ION_COMMONX4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) { - Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global)); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType::Float32x4)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index fa2e0673e43..cca06fa777d 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -31,6 +31,7 @@ class StaticBlockScope; class ClonedBlockObject; class SimdTypeDescr; +enum class SimdType : uint8_t; /* * Global object slots are reserved as follows: @@ -444,22 +445,11 @@ class GlobalObject : public NativeObject return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_SIMD, initSimdObject); } - template - static SimdTypeDescr* - getOrCreateSimdTypeDescr(JSContext* cx, Handle global) { - RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx)); - if (!globalSimdObject) - return nullptr; - uint32_t typeSlotIndex = uint32_t(T::type); - if (globalSimdObject->as().getReservedSlot(typeSlotIndex).isUndefined() && - !GlobalObject::initSimdType(cx, global, typeSlotIndex)) - { - return nullptr; - } - const Value& slot = globalSimdObject->as().getReservedSlot(typeSlotIndex); - MOZ_ASSERT(slot.isObject()); - return &slot.toObject().as(); - } + // Get the type descriptor for one of the SIMD types. + // simdType is one of the JS_SIMDTYPEREPR_* constants. + // Implemented in builtin/SIMD.cpp. + static SimdTypeDescr* getOrCreateSimdTypeDescr(JSContext* cx, Handle global, + SimdType simdType); TypedObjectModuleObject& getTypedObjectModule() const; @@ -732,9 +722,9 @@ class GlobalObject : public NativeObject // Implemented in builtin/TypedObject.cpp static bool initTypedObjectModule(JSContext* cx, Handle global); - // Implemented in builtim/SIMD.cpp + // Implemented in builtin/SIMD.cpp static bool initSimdObject(JSContext* cx, Handle global); - static bool initSimdType(JSContext* cx, Handle global, uint32_t simdTypeDescrType); + static bool initSimdType(JSContext* cx, Handle global, SimdType simdType); static bool initStandardClasses(JSContext* cx, Handle global); static bool initSelfHostingBuiltins(JSContext* cx, Handle global, diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 86c8059475c..9ac33628b24 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1845,18 +1845,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("TypedObjectTypeDescr", js::TypedObjectTypeDescr, 1, 0), JS_FN("ClampToUint8", js::ClampToUint8, 1, 0), JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0), - JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0), - JS_FN("GetFloat64x2TypeDescr", js::GetFloat64x2TypeDescr, 0, 0), - JS_FN("GetInt8x16TypeDescr", js::GetInt8x16TypeDescr, 0, 0), - JS_FN("GetInt16x8TypeDescr", js::GetInt16x8TypeDescr, 0, 0), - JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0), - JS_FN("GetUint8x16TypeDescr", js::GetUint8x16TypeDescr, 0, 0), - JS_FN("GetUint16x8TypeDescr", js::GetUint16x8TypeDescr, 0, 0), - JS_FN("GetUint32x4TypeDescr", js::GetUint32x4TypeDescr, 0, 0), - JS_FN("GetBool8x16TypeDescr", js::GetBool8x16TypeDescr, 0, 0), - JS_FN("GetBool16x8TypeDescr", js::GetBool16x8TypeDescr, 0, 0), - JS_FN("GetBool32x4TypeDescr", js::GetBool32x4TypeDescr, 0, 0), - JS_FN("GetBool64x2TypeDescr", js::GetBool64x2TypeDescr, 0, 0), + JS_FN("GetSimdTypeDescr", js::GetSimdTypeDescr, 1, 0), JS_INLINABLE_FN("ObjectIsTypeDescr" , js::ObjectIsTypeDescr, 1, 0, IntrinsicObjectIsTypeDescr), From 27011c51839e4b73d1e48c0b5513f7638f7c2724 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:06 -0800 Subject: [PATCH 58/91] Bug 1240796 - Inline SIMD operations that return scalars. r=bbouvier The extractLane(), anyTrue(), and allTrue() SIMD functions produce scalar values, and so they don't need a template object. The canInlineSimd() function was rejecting these functions because of the missing template object. At the same time, explicitly avoid inlining any SIMD operations if the JIT does not support SIMD. This was previously controlled by the absense of the template object. --- js/src/jit/MCallOptimize.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index c01f5d00d0c..e426e67cac0 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3060,6 +3060,11 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr) IonBuilder::InliningStatus IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) { + if (!JitSupportsSimd()) { + trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport); + return InliningStatus_NotInlined; + } + JSNative native = target->native(); const JSJitInfo* jitInfo = target->jitInfo(); MOZ_ASSERT(jitInfo && jitInfo->type() == JSJitInfo::InlinableNative); @@ -3262,6 +3267,11 @@ bool SimdTypeToMIRType(SimdType type, MIRType* mirType) IonBuilder::InliningStatus IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) { + if (!JitSupportsSimd()) { + trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport); + return InliningStatus_NotInlined; + } + // Generic constructor of SIMD valuesX4. MIRType simdType; if (!SimdTypeToMIRType(descr->type(), &simdType)) @@ -3432,9 +3442,12 @@ IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, MIRType mirType IonBuilder::InliningStatus IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, MIRType vecType) { - InlineTypedObject* templateObj = nullptr; - if (!canInlineSimd(callInfo, native, 2, &templateObj)) + // extractLane() returns a scalar, so don't use canInlineSimd() which looks + // for a template object. + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MDefinition* arg = callInfo.getArg(1); if (!arg->isConstantValue() || arg->type() != MIRType_Int32) @@ -3533,9 +3546,12 @@ IonBuilder::inlineSimdShuffle(CallInfo& callInfo, JSNative native, MIRType mirTy IonBuilder::InliningStatus IonBuilder::inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native) { - InlineTypedObject* templateObj = nullptr; - if (!canInlineSimd(callInfo, native, 1, &templateObj)) + // anyTrue() / allTrue() return a scalar, so don't use canInlineSimd() which looks + // for a template object. + if (callInfo.argc() != 1 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); return InliningStatus_NotInlined; + } MUnaryInstruction* ins; if (IsAllTrue) From d3a2d31c0c4e51c35cf17b70e3851bbb242a3028 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:07 -0800 Subject: [PATCH 59/91] Bug 1240796 - Extract baseline code to GetTemplateObjectForSimd(). r=bbouvier Extract the code that generates template objects for SIMD operations, and rewrite it to use the JSJitInfo nativeOp encoding. This avoids the native function pointer comparisons, and it makes it simpler to add new SIMD types and operations. --- js/src/builtin/SIMD.h | 34 ++++++++++++ js/src/jit/BaselineIC.cpp | 109 ++++++++++++++++++++++---------------- 2 files changed, 96 insertions(+), 47 deletions(-) diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 4d45c30c162..87a5c9597e6 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -799,6 +799,40 @@ enum class SimdType : uint8_t { Count }; +// Get the boolean SIMD type with the same shape as t. +// +// This is the result type of a comparison operation, and it can also be used to +// identify the geometry of a SIMD type. +inline SimdType +GetBooleanSimdType(SimdType t) +{ + switch(t) { + case SimdType::Int8x16: + case SimdType::Uint8x16: + case SimdType::Bool8x16: + return SimdType::Bool8x16; + + case SimdType::Int16x8: + case SimdType::Uint16x8: + case SimdType::Bool16x8: + return SimdType::Bool16x8; + + case SimdType::Int32x4: + case SimdType::Uint32x4: + case SimdType::Float32x4: + case SimdType::Bool32x4: + return SimdType::Bool32x4; + + case SimdType::Float64x2: + case SimdType::Bool64x2: + return SimdType::Bool64x2; + + case SimdType::Count: + break; + } + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type"); +} + // Complete set of SIMD operations. // // No SIMD types implement all of these operations. diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index c2a9f406ebc..036b204bd7f 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -18,6 +18,7 @@ #include "gc/Policy.h" #include "jit/BaselineDebugModeOSR.h" #include "jit/BaselineJIT.h" +#include "jit/InlinableNatives.h" #include "jit/JitSpewer.h" #include "jit/Linker.h" #include "jit/Lowering.h" @@ -5549,10 +5550,67 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, return true; } +// Check if target is a native SIMD operation which returns a SIMD type. +// If so, set res to a template object matching the SIMD type produced and return true. static bool -GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, +GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject res) +{ + const JSJitInfo* jitInfo = target->jitInfo(); + if (!jitInfo || jitInfo->type() != JSJitInfo::InlinableNative) + return false; + + // Check if this is a native inlinable SIMD operation. + SimdType ctrlType; + switch (jitInfo->inlinableNative) { + case InlinableNative::SimdInt32x4: ctrlType = SimdType::Int32x4; break; + case InlinableNative::SimdFloat32x4: ctrlType = SimdType::Float32x4; break; + case InlinableNative::SimdBool32x4: ctrlType = SimdType::Bool32x4; break; + // This is not an inlinable SIMD operation. + default: return false; + } + + // The controlling type is not necessarily the return type. + // Check the actual operation. + SimdOperation simdOp = SimdOperation(jitInfo->nativeOp); + SimdType retType; + + switch(simdOp) { + case SimdOperation::Fn_allTrue: + case SimdOperation::Fn_anyTrue: + case SimdOperation::Fn_extractLane: + // These operations return a scalar. No template object needed. + return false; + + case SimdOperation::Fn_lessThan: + case SimdOperation::Fn_lessThanOrEqual: + case SimdOperation::Fn_equal: + case SimdOperation::Fn_notEqual: + case SimdOperation::Fn_greaterThan: + case SimdOperation::Fn_greaterThanOrEqual: + // These operations return a boolean vector with the same shape as the + // controlling type. + retType = GetBooleanSimdType(ctrlType); + break; + + default: + // All other operations return the controlling type. + retType = ctrlType; + break; + } + + // Create a template object based on retType. + RootedGlobalObject global(cx, cx->global()); + Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, retType)); + res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); + return true; +} + +static bool +GetTemplateObjectForNative(JSContext* cx, JSFunction* target, const CallArgs& args, MutableHandleObject res, bool* skipAttach) { + Native native = target->native(); + // Check for natives to which template objects can be attached. This is // done to provide templates to Ion for inlining these natives later on. @@ -5626,51 +5684,8 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, return !!res; } - if (JitSupportsSimd()) { - RootedGlobalObject global(cx, cx->global()); -#define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP -#define ADD_BOOL32X4_SIMD_OP_NAME_(OP) || native == js::simd_bool32x4_##OP -#define ADD_FLOAT32X4_SIMD_OP_NAME_(OP) || native == js::simd_float32x4_##OP - // Operations producing an int32x4. - if (false - ION_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) - FOREACH_BITWISE_SIMD_UNOP(ADD_INT32X4_SIMD_OP_NAME_) - FOREACH_BITWISE_SIMD_BINOP(ADD_INT32X4_SIMD_OP_NAME_) - FOREACH_SHIFT_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) - ADD_INT32X4_SIMD_OP_NAME_(fromFloat32x4) - ADD_INT32X4_SIMD_OP_NAME_(fromFloat32x4Bits)) - { - Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType::Int32x4)); - res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); - return !!res; - } - // Operations producing a bool32x4. - if (false - FOREACH_BITWISE_SIMD_UNOP(ADD_BOOL32X4_SIMD_OP_NAME_) - FOREACH_BITWISE_SIMD_BINOP(ADD_BOOL32X4_SIMD_OP_NAME_) - FOREACH_COMP_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) - FOREACH_COMP_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) - { - Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType::Bool32x4)); - res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); - return !!res; - } - // Operations producing a float32x4. - if (false - FOREACH_FLOAT_SIMD_UNOP(ADD_FLOAT32X4_SIMD_OP_NAME_) - FOREACH_FLOAT_SIMD_BINOP(ADD_FLOAT32X4_SIMD_OP_NAME_) - ADD_FLOAT32X4_SIMD_OP_NAME_(fromInt32x4) - ADD_FLOAT32X4_SIMD_OP_NAME_(fromInt32x4Bits) - ION_COMMONX4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) - { - Rooted descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType::Float32x4)); - res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); - return !!res; - } -#undef ADD_BOOL32X4_SIMD_OP_NAME_ -#undef ADD_INT32X4_SIMD_OP_NAME_ -#undef ADD_FLOAT32X4_SIMD_OP_NAME_ - } + if (JitSupportsSimd() && GetTemplateObjectForSimd(cx, target, res)) + return !!res; return true; } @@ -5934,7 +5949,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb if (MOZ_LIKELY(!isSpread && !isSuper)) { bool skipAttach = false; CallArgs args = CallArgsFromVp(argc, vp); - if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject, &skipAttach)) + if (!GetTemplateObjectForNative(cx, fun, args, &templateObject, &skipAttach)) return false; if (skipAttach) { *handled = true; From bcb07d6a3e0a9df5a32153ff81df52f2e9624d0b Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:07 -0800 Subject: [PATCH 60/91] Bug 1240796 - Add Uint32x4 support to jit-test/lib/simd.js. r=bbouvier --- js/src/jit-test/lib/simd.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/js/src/jit-test/lib/simd.js b/js/src/jit-test/lib/simd.js index 019f46e5c14..8736fa50a21 100644 --- a/js/src/jit-test/lib/simd.js +++ b/js/src/jit-test/lib/simd.js @@ -34,6 +34,7 @@ function GetType(v) { var pt = Object.getPrototypeOf(v); switch (pt) { case SIMD.Int32x4.prototype: return SIMD.Int32x4; + case SIMD.Uint32x4.prototype: return SIMD.Uint32x4; case SIMD.Float32x4.prototype: return SIMD.Float32x4; case SIMD.Bool32x4.prototype: return SIMD.Bool32x4; } @@ -60,6 +61,7 @@ function assertEqX4(vec, arr, ...opts) { } var Type = GetType(vec); + if (Type === SIMD.Int32x4) { assertFunc(SIMD.Int32x4.extractLane(vec, 0), arr[0]); assertFunc(SIMD.Int32x4.extractLane(vec, 1), arr[1]); @@ -68,6 +70,14 @@ function assertEqX4(vec, arr, ...opts) { return; } + if (Type === SIMD.Uint32x4) { + assertFunc(SIMD.Uint32x4.extractLane(vec, 0), arr[0]); + assertFunc(SIMD.Uint32x4.extractLane(vec, 1), arr[1]); + assertFunc(SIMD.Uint32x4.extractLane(vec, 2), arr[2]); + assertFunc(SIMD.Uint32x4.extractLane(vec, 3), arr[3]); + return; + } + if (Type === SIMD.Float32x4) { assertFunc(SIMD.Float32x4.extractLane(vec, 0), arr[0]); assertFunc(SIMD.Float32x4.extractLane(vec, 1), arr[1]); @@ -99,6 +109,15 @@ function simdToArray(vec) { ]; } + if (Type === SIMD.Uint32x4) { + return [ + SIMD.Uint32x4.extractLane(vec, 0), + SIMD.Uint32x4.extractLane(vec, 1), + SIMD.Uint32x4.extractLane(vec, 2), + SIMD.Uint32x4.extractLane(vec, 3), + ]; + } + if (Type === SIMD.Float32x4) { return [ SIMD.Float32x4.extractLane(vec, 0), From 128fbdfe8b6088827d332a314a2d776ef74f6042 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:07 -0800 Subject: [PATCH 61/91] Bug 1240796 - Connect SIMD.Uint32x4 operations to the Ion inliner. r=bbouvier Add a new InlinableNative::SimdUint32x4 enumerator, and emit the corresponding JSJitInfo objects in SIMD.cpp. Start producing template objects for Uint32x4 operations in BaselineIC.cpp. Add a new SimdSign enum class to SIMD.h which will be used to distinguish between signed and unsigned integers in the few places where it matters. Map the SIMD.Uint32x4 type to the existing MIRType_Int32x4 + SimdSign::Unsigned. Map SIMD.Int32x4 to MITType_Int32x4 + SimdSign::Signed. Add a 'SimdSign sign' argument to those inlineSimd...() functions that care. Some MIR instructions will get similar fields in the following commits. For now, abort inlining if unsigned vectors are actually encountered. These cases will be fixed in the following commits. --- js/src/builtin/SIMD.cpp | 21 +++-------- js/src/builtin/SIMD.h | 41 +++++++++++++++++++-- js/src/jit/BaselineIC.cpp | 1 + js/src/jit/InlinableNatives.h | 1 + js/src/jit/IonBuilder.h | 12 ++++--- js/src/jit/IonTypes.h | 1 + js/src/jit/MCallOptimize.cpp | 68 +++++++++++++++++++++++++---------- js/src/jit/MIR.h | 5 +++ 8 files changed, 109 insertions(+), 41 deletions(-) diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 44112214d2d..9ba70fd5865 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -39,21 +39,6 @@ using mozilla::NumberIsInt32; static_assert(unsigned(SimdType::Count) == 12, "sync with TypedObjectConstants.h"); -bool -js::IsSignedIntSimdType(SimdType type) -{ - switch (type) { - case SimdType::Int32x4: - return true; - case SimdType::Float32x4: - case SimdType::Bool32x4: - return false; - default: - break; - } - MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type"); -} - PropertyName* js::SimdTypeToName(JSContext* cx, SimdType type) { @@ -252,6 +237,10 @@ FLOAT32X4_FUNCTION_LIST(TDEFN) INT32X4_FUNCTION_LIST(TDEFN) #undef TDEFN +#define TDEFN(Name, Func, Operands) DEFN(Uint32x4, Name) +UINT32X4_FUNCTION_LIST(TDEFN) +#undef TDEFN + #define TDEFN(Name, Func, Operands) DEFN(Bool32x4, Name) BOOL32X4_FUNCTION_LIST(TDEFN) #undef TDEFN @@ -317,7 +306,7 @@ const JSFunctionSpec Uint16x8Defn::Methods[] = { const JSFunctionSpec Uint32x4Defn::Methods[] = { #define SIMD_UINT32X4_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_uint32x4_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_uint32x4_##Name, Operands, 0, SimdUint32x4_##Name), UINT32X4_FUNCTION_LIST(SIMD_UINT32X4_FUNCTION_ITEM) #undef SIMD_UINT32X4_FUNCTION_ITEM JS_FS_END diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 87a5c9597e6..6ea4d065215 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -799,6 +799,45 @@ enum class SimdType : uint8_t { Count }; +// The integer SIMD types have a lot of operations that do the exact same thing +// for signed and unsigned integer types. Sometimes it is simpler to treat +// signed and unsigned integer SIMD types as the same type, using a SimdSign to +// distinguish the few cases where there is a difference. +enum class SimdSign { + // Signedness is not applicable to this type. (i.e., Float or Bool). + NotApplicable, + // Treat as an unsigned integer with a range 0 .. 2^N-1. + Unsigned, + // Treat as a signed integer in two's complement encoding. + Signed, +}; + +// Get the signedness of a SIMD type. +inline SimdSign +GetSimdSign(SimdType t) +{ + switch(t) { + case SimdType::Int8x16: + case SimdType::Int16x8: + case SimdType::Int32x4: + return SimdSign::Signed; + + case SimdType::Uint8x16: + case SimdType::Uint16x8: + case SimdType::Uint32x4: + return SimdSign::Unsigned; + + default: + return SimdSign::NotApplicable; + } +} + +inline bool +IsSignedIntSimdType(SimdType type) +{ + return GetSimdSign(type) == SimdSign::Signed; +} + // Get the boolean SIMD type with the same shape as t. // // This is the result type of a comparison operation, and it can also be used to @@ -1041,8 +1080,6 @@ struct Bool64x2 { } }; -bool IsSignedIntSimdType(SimdType type); - PropertyName* SimdTypeToName(JSContext* cx, SimdType type); template diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 036b204bd7f..a4ccd4ea8fb 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5563,6 +5563,7 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject SimdType ctrlType; switch (jitInfo->inlinableNative) { case InlinableNative::SimdInt32x4: ctrlType = SimdType::Int32x4; break; + case InlinableNative::SimdUint32x4: ctrlType = SimdType::Uint32x4; break; case InlinableNative::SimdFloat32x4: ctrlType = SimdType::Float32x4; break; case InlinableNative::SimdBool32x4: ctrlType = SimdType::Bool32x4; break; // This is not an inlinable SIMD operation. diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 76b8e51d4d1..2d7c46df2b8 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -80,6 +80,7 @@ _(ObjectCreate) \ \ _(SimdInt32x4) \ + _(SimdUint32x4) \ _(SimdFloat32x4) \ _(SimdBool32x4) \ \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 85ec62f74de..c7f7011b3d6 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -860,23 +860,27 @@ class IonBuilder InlineTypedObject* templateObj); MDefinition* convertToBooleanSimdLane(MDefinition* scalar); - InliningStatus inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType); + InliningStatus inlineSimd(CallInfo& callInfo, JSFunction* target, + MIRType simdType, SimdSign sign = SimdSign::NotApplicable); template InliningStatus inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Operation op, MIRType mirType); InliningStatus inlineSimdComp(CallInfo& callInfo, JSNative native, - MSimdBinaryComp::Operation op, MIRType compType); + MSimdBinaryComp::Operation op, + MIRType compType, SimdSign sign); InliningStatus inlineSimdUnary(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op, MIRType mirType); - InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native, MIRType vecType); + InliningStatus inlineSimdExtractLane(CallInfo& callInfo, JSNative native, + MIRType vecType, SimdSign sign); InliningStatus inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, MIRType mirType); InliningStatus inlineSimdSplat(CallInfo& callInfo, JSNative native, MIRType mirType); InliningStatus inlineSimdShuffle(CallInfo& callInfo, JSNative native, MIRType type, unsigned numVectors, unsigned numLanes); InliningStatus inlineSimdCheck(CallInfo& callInfo, JSNative native, MIRType type); InliningStatus inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast, - MIRType from, MIRType to); + MIRType from, MIRType to, + SimdSign sign = SimdSign::NotApplicable); InliningStatus inlineSimdSelect(CallInfo& callInfo, JSNative native, MIRType type); bool prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, MInstruction** elements, diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 53ed36d0fb3..ddf388479c1 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -420,6 +420,7 @@ enum MIRType MIRType_ObjectGroup, // An ObjectGroup pointer. MIRType_Last = MIRType_ObjectGroup, MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT), + // Representing both SIMD.Int32x4 and SIMD.Uint32x4. MIRType_Int32x4 = MIRType_Int32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Bool32x4 = MIRType_Boolean | (2 << VECTOR_SCALE_SHIFT), MIRType_Doublex2 = MIRType_Double | (1 << VECTOR_SCALE_SHIFT) diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index e426e67cac0..f6db17360c9 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -203,7 +203,9 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) // SIMD natives. case InlinableNative::SimdInt32x4: - return inlineSimd(callInfo, target, MIRType_Int32x4); + return inlineSimd(callInfo, target, MIRType_Int32x4, SimdSign::Signed); + case InlinableNative::SimdUint32x4: + return inlineSimd(callInfo, target, MIRType_Int32x4, SimdSign::Unsigned); case InlinableNative::SimdFloat32x4: return inlineSimd(callInfo, target, MIRType_Float32x4); case InlinableNative::SimdBool32x4: @@ -3057,8 +3059,10 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr) } // Main entry point for SIMD inlining. +// When the controlling simdType is an integer type, sign indicates whether the lanes should +// be treated as signed or unsigned integers. IonBuilder::InliningStatus -IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) +IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType, SimdSign sign) { if (!JitSupportsSimd()) { trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport); @@ -3069,7 +3073,10 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) const JSJitInfo* jitInfo = target->jitInfo(); MOZ_ASSERT(jitInfo && jitInfo->type() == JSJitInfo::InlinableNative); SimdOperation simdOp = SimdOperation(jitInfo->nativeOp); + MOZ_ASSERT(IsSimdType(simdType)); + MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(simdType), + "Signedness must be specified for ints, and only for ints"); switch(simdOp) { case SimdOperation::Constructor: @@ -3082,7 +3089,7 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) case SimdOperation::Fn_splat: return inlineSimdSplat(callInfo, native, simdType); case SimdOperation::Fn_extractLane: - return inlineSimdExtractLane(callInfo, native, simdType); + return inlineSimdExtractLane(callInfo, native, simdType, sign); case SimdOperation::Fn_replaceLane: return inlineSimdReplaceLane(callInfo, native, simdType); case SimdOperation::Fn_select: @@ -3149,9 +3156,8 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) case SimdOperation::Fn_shiftLeftByScalar: return inlineSimdBinary(callInfo, native, MSimdShift::lsh, simdType); case SimdOperation::Fn_shiftRightByScalar: - // TODO: select opcode from simdType signedness. - MOZ_ASSERT(simdType == MIRType_Int32x4); - return inlineSimdBinary(callInfo, native, MSimdShift::rsh, simdType); + return inlineSimdBinary(callInfo, native, MSimdShift::rshForSign(sign), + simdType); case SimdOperation::Fn_shiftRightArithmeticByScalar: return inlineSimdBinary(callInfo, native, MSimdShift::rsh, simdType); case SimdOperation::Fn_shiftRightLogicalByScalar: @@ -3165,25 +3171,33 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) // Comparisons. case SimdOperation::Fn_lessThan: - return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThan, simdType); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThan, + simdType, sign); case SimdOperation::Fn_lessThanOrEqual: - return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThanOrEqual, simdType); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThanOrEqual, + simdType, sign); case SimdOperation::Fn_equal: - return inlineSimdComp(callInfo, native, MSimdBinaryComp::equal, simdType); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::equal, + simdType, sign); case SimdOperation::Fn_notEqual: - return inlineSimdComp(callInfo, native, MSimdBinaryComp::notEqual, simdType); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::notEqual, + simdType, sign); case SimdOperation::Fn_greaterThan: - return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThan, simdType); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThan, + simdType, sign); case SimdOperation::Fn_greaterThanOrEqual: - return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThanOrEqual, simdType); + return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThanOrEqual, + simdType, sign); // Int <-> Float conversions. case SimdOperation::Fn_fromInt32x4: - return inlineSimdConvert(callInfo, native, false, MIRType_Int32x4, simdType); + return inlineSimdConvert(callInfo, native, false, MIRType_Int32x4, + simdType, SimdSign::Signed); case SimdOperation::Fn_fromUint32x4: - return InliningStatus_NotInlined; + return inlineSimdConvert(callInfo, native, false, MIRType_Int32x4, + simdType, SimdSign::Unsigned); case SimdOperation::Fn_fromFloat32x4: - return inlineSimdConvert(callInfo, native, false, MIRType_Float32x4, simdType); + return inlineSimdConvert(callInfo, native, false, MIRType_Float32x4, simdType, sign); // Load/store. case SimdOperation::Fn_load: @@ -3208,10 +3222,10 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, MIRType simdType) case SimdOperation::Fn_fromInt16x8Bits: return InliningStatus_NotInlined; case SimdOperation::Fn_fromInt32x4Bits: + case SimdOperation::Fn_fromUint32x4Bits: return inlineSimdConvert(callInfo, native, true, MIRType_Int32x4, simdType); case SimdOperation::Fn_fromUint8x16Bits: case SimdOperation::Fn_fromUint16x8Bits: - case SimdOperation::Fn_fromUint32x4Bits: return InliningStatus_NotInlined; case SimdOperation::Fn_fromFloat32x4Bits: return inlineSimdConvert(callInfo, native, true, MIRType_Float32x4, simdType); @@ -3391,12 +3405,16 @@ IonBuilder::inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Op IonBuilder::InliningStatus IonBuilder::inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op, - MIRType mirType) + MIRType mirType, SimdSign sign) { InlineTypedObject* templateObj = nullptr; if (!canInlineSimd(callInfo, native, 2, &templateObj)) return InliningStatus_NotInlined; + // TODO JSO: Implement unsigned integer comparisons. + if (sign == SimdSign::Unsigned) + return InliningStatus_NotInlined; + // If the type of any of the arguments is neither a SIMD type, an Object // type, or a Value, then the applyTypes phase will add a fallible box & // unbox sequence. This does not matter much as all binary SIMD @@ -3440,7 +3458,8 @@ IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, MIRType mirType } IonBuilder::InliningStatus -IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, MIRType vecType) +IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, + MIRType vecType, SimdSign sign) { // extractLane() returns a scalar, so don't use canInlineSimd() which looks // for a template object. @@ -3449,6 +3468,10 @@ IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, MIRType v return InliningStatus_NotInlined; } + // TODO JSO: Implement unsigned integer lane values. + if (sign == SimdSign::Unsigned) + return InliningStatus_NotInlined; + MDefinition* arg = callInfo.getArg(1); if (!arg->isConstantValue() || arg->type() != MIRType_Int32) return InliningStatus_NotInlined; @@ -3491,14 +3514,21 @@ IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, MIRType m return boxSimd(callInfo, ins, templateObj); } +// Inline a SIMD conversion or bitcast. When isCast==false, one of the types +// must be floating point and the other integer. In this case, sign indicates if +// the integer lanes should be treated as signed or unsigned integers. IonBuilder::InliningStatus IonBuilder::inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast, - MIRType fromType, MIRType toType) + MIRType fromType, MIRType toType, SimdSign sign) { InlineTypedObject* templateObj = nullptr; if (!canInlineSimd(callInfo, native, 1, &templateObj)) return InliningStatus_NotInlined; + // TODO JSO: Implement unsigned integer conversions. + if (sign == SimdSign::Unsigned) + return InliningStatus_NotInlined; + // See comment in inlineSimdBinary MInstruction* ins; if (isCast) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 91ee069e139..b7890c4ec30 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2367,6 +2367,11 @@ class MSimdShift return new(alloc) MSimdShift(left, right, op); } + // Get the relevant right shift operation given the signedness of a type. + static Operation rshForSign(SimdSign sign) { + return sign == SimdSign::Unsigned ? ursh : rsh; + } + AliasSet getAliasSet() const override { return AliasSet::None(); } From 5392d148c6ef7f3ab41cff5c36e97ce7c3698854 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:07 -0800 Subject: [PATCH 62/91] Bug 1240796 - Implement Uint32x4 <==> Float32x4 conversions. r=sunfish The conversion from Uint32x4 to Float32x4 is not available as an SSE instruction, so we need to expand into a larger instruction sequence lifted from LLVM. Make this expansion early when generating MIR so that it can be exposed to LICM and GVN optimizations. The conversion from Float32x4 to Uint32x4 can throw a RangeError. It is handled similarly to LFloat32x4ToInt32x4. This expansion depends on the details of the cvttps2dq instruction that can't be expressed in MIR, so it can't be expanded early. --- js/src/jit-test/tests/SIMD/uconvert.js | 81 +++++++++++++++ js/src/jit/Lowering.cpp | 24 ++++- js/src/jit/MCallOptimize.cpp | 14 +-- js/src/jit/MIR.cpp | 99 +++++++++++++++++++ js/src/jit/MIR.h | 35 +++++-- js/src/jit/arm/Assembler-arm.h | 3 + js/src/jit/arm64/Assembler-arm64.h | 3 + js/src/jit/mips32/Assembler-mips32.h | 3 + js/src/jit/mips64/Assembler-mips64.h | 3 + js/src/jit/none/Architecture-none.h | 3 + js/src/jit/shared/LIR-shared.h | 23 +++++ js/src/jit/shared/LOpcodes-shared.h | 1 + .../jit/x86-shared/Architecture-x86-shared.h | 3 + .../x86-shared/CodeGenerator-x86-shared.cpp | 85 ++++++++++++++++ .../jit/x86-shared/CodeGenerator-x86-shared.h | 1 + 15 files changed, 365 insertions(+), 16 deletions(-) create mode 100644 js/src/jit-test/tests/SIMD/uconvert.js diff --git a/js/src/jit-test/tests/SIMD/uconvert.js b/js/src/jit-test/tests/SIMD/uconvert.js new file mode 100644 index 00000000000..a1f5e002d8a --- /dev/null +++ b/js/src/jit-test/tests/SIMD/uconvert.js @@ -0,0 +1,81 @@ +load(libdir + 'simd.js'); + +setJitCompilerOption("ion.warmup.trigger", 30); + +// Testing Uint32 <-> Float32 conversions. +// These conversions deserve special attention because SSE doesn't provide +// simple conversion instructions. + +// Convert an Uint32Array to a Float32Array using scalar conversions. +function cvt_utof_scalar(u32s, f32s) { + assertEq(u32s.length, f32s.length); + for (var i = 0; i < u32s.length; i++) { + f32s[i] = u32s[i]; + } +} + +// Convert an Uint32Array to a Float32Array using simd conversions. +function cvt_utof_simd(u32s, f32s) { + assertEq(u32s.length, f32s.length); + for (var i = 0; i < u32s.length; i += 4) { + SIMD.Float32x4.store(f32s, i, SIMD.Float32x4.fromUint32x4(SIMD.Uint32x4.load(u32s, i))); + } +} + +// Convert a Float32Array to an Uint32Array using scalar conversions. +function cvt_ftou_scalar(f32s, u32s) { + assertEq(f32s.length, u32s.length); + for (var i = 0; i < f32s.length; i++) { + u32s[i] = f32s[i]; + } +} + +// Convert a Float32Array to an Uint32Array using simd conversions. +function cvt_ftou_simd(f32s, u32s) { + assertEq(f32s.length, u32s.length); + for (var i = 0; i < f32s.length; i += 4) { + SIMD.Uint32x4.store(u32s, i, SIMD.Uint32x4.fromFloat32x4(SIMD.Float32x4.load(f32s, i))); + } +} + +function check(a, b) { + assertEq(a.length, b.length); + for (var i = 0; i < a.length; i++) { + assertEq(a[i], b[i]); + } +} + +// Uint32x4 --> Float32x4 tests. +var src = new Uint32Array(8000); +var dst1 = new Float32Array(8000); +var dst2 = new Float32Array(8000); + +for (var i = 0; i < 2000; i++) { + src[i] = i; + src[i + 2000] = 0x7fffffff - i; + src[i + 4000] = 0x80000000 + i; + src[i + 6000] = 0xffffffff - i; +} + +for (var n = 0; n < 10; n++) { + cvt_utof_scalar(src, dst1); + cvt_utof_simd(src, dst2); + check(dst1, dst2); +} + +// Float32x4 --> Uint32x4 tests. +var fsrc = dst1; +var fdst1 = new Uint32Array(8000); +var fdst2 = new Uint32Array(8000); + +// The 0xffffffff entries in fsrc round to 0x1.0p32f which throws. +// Go as high as 0x0.ffffffp32f. +for (var i = 0; i < 2000; i++) { + fsrc[i + 6000] = 0xffffff7f - i; +} + +for (var n = 0; n < 10; n++) { + cvt_ftou_scalar(fsrc, fdst1); + cvt_ftou_simd(fsrc, fdst2); + check(fdst1, fdst2); +} diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index fbae3458861..d36cb23e1df 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4101,12 +4101,28 @@ LIRGenerator::visitSimdConvert(MSimdConvert* ins) LUse use = useRegister(input); if (ins->type() == MIRType_Int32x4) { MOZ_ASSERT(input->type() == MIRType_Float32x4); - LFloat32x4ToInt32x4* lir = new(alloc()) LFloat32x4ToInt32x4(use, temp()); - if (!gen->compilingAsmJS()) - assignSnapshot(lir, Bailout_BoundsCheck); - define(lir, ins); + switch (ins->signedness()) { + case SimdSign::Signed: { + LFloat32x4ToInt32x4* lir = new(alloc()) LFloat32x4ToInt32x4(use, temp()); + if (!gen->compilingAsmJS()) + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); + break; + } + case SimdSign::Unsigned: { + LFloat32x4ToUint32x4* lir = + new (alloc()) LFloat32x4ToUint32x4(use, temp(), temp(LDefinition::INT32X4)); + if (!gen->compilingAsmJS()) + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); + break; + } + default: + MOZ_CRASH("Unexpected SimdConvert sign"); + } } else if (ins->type() == MIRType_Float32x4) { MOZ_ASSERT(input->type() == MIRType_Int32x4); + MOZ_ASSERT(ins->signedness() == SimdSign::Signed, "Unexpected SimdConvert sign"); define(new(alloc()) LInt32x4ToFloat32x4(use), ins); } else { MOZ_CRASH("Unknown SIMD kind when generating constant"); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index f6db17360c9..36e54932064 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3377,7 +3377,10 @@ IonBuilder::boxSimd(CallInfo& callInfo, MInstruction* ins, InlineTypedObject* te { MSimdBox* obj = MSimdBox::New(alloc(), constraints(), ins, templateObj, templateObj->group()->initialHeap(constraints())); - current->add(ins); + + // In some cases, ins has already been added to current. + if (!ins->block()) + current->add(ins); current->add(obj); current->push(obj); @@ -3525,16 +3528,15 @@ IonBuilder::inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast, if (!canInlineSimd(callInfo, native, 1, &templateObj)) return InliningStatus_NotInlined; - // TODO JSO: Implement unsigned integer conversions. - if (sign == SimdSign::Unsigned) - return InliningStatus_NotInlined; - // See comment in inlineSimdBinary MInstruction* ins; if (isCast) + // Signed/Unsigned doesn't matter for bitcasts. ins = MSimdReinterpretCast::New(alloc(), callInfo.getArg(0), fromType, toType); else - ins = MSimdConvert::New(alloc(), callInfo.getArg(0), fromType, toType); + // Possibly expand into multiple instructions. + ins = MSimdConvert::AddLegalized(alloc(), current, callInfo.getArg(0), + fromType, toType, sign); return boxSimd(callInfo, ins, templateObj); } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 81873829eba..3e18282f6c9 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1037,6 +1037,105 @@ MSimdGeneralShuffle::foldsTo(TempAllocator& alloc) return MSimdShuffle::New(alloc, vector(0), vector(1), type(), lanes[0], lanes[1], lanes[2], lanes[3]); } +MInstruction* +MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj, + MIRType fromType, MIRType toType, SimdSign sign) +{ + if (SupportsUint32x4FloatConversions || sign != SimdSign::Unsigned) { + MInstruction* ins = New(alloc, obj, fromType, toType, sign); + addTo->add(ins); + return ins; + } + + // This architecture can't do Uint32x4 <-> Float32x4 conversions (Hi SSE!) + MOZ_ASSERT(sign == SimdSign::Unsigned); + if (fromType == MIRType_Int32x4 && toType == MIRType_Float32x4) { + // Converting Uint32x4 -> Float32x4. This algorithm is from LLVM. + // + // Split the input number into high and low parts: + // + // uint32_t hi = x >> 16; + // uint32_t lo = x & 0xffff; + // + // Insert these parts as the low mantissa bits in a float32 number with + // the corresponding exponent: + // + // float fhi = (bits-as-float)(hi | 0x53000000); // 0x1.0p39f + hi*2^16 + // float flo = (bits-as-float)(lo | 0x4b000000); // 0x1.0p23f + lo + // + // Subtract the bias from the hi part: + // + // fhi -= (0x1.0p39 + 0x1.0p23) // hi*2^16 - 0x1.0p23 + // + // And finally combine: + // + // result = flo + fhi // lo + hi*2^16. + + // Compute hi = obj >> 16 (lane-wise unsigned shift). + MInstruction* c16 = MConstant::New(alloc, Int32Value(16)); + addTo->add(c16); + MInstruction* hi = MSimdShift::New(alloc, obj, c16, MSimdShift::ursh, MIRType_Int32x4); + addTo->add(hi); + + // Compute lo = obj & 0xffff (lane-wise). + MInstruction* m16 = + MSimdConstant::New(alloc, SimdConstant::SplatX4(0xffff), MIRType_Int32x4); + addTo->add(m16); + MInstruction* lo = + MSimdBinaryBitwise::New(alloc, obj, m16, MSimdBinaryBitwise::and_, MIRType_Int32x4); + addTo->add(lo); + + // Mix in the exponents. + MInstruction* exphi = + MSimdConstant::New(alloc, SimdConstant::SplatX4(0x53000000), MIRType_Int32x4); + addTo->add(exphi); + MInstruction* mhi = + MSimdBinaryBitwise::New(alloc, hi, exphi, MSimdBinaryBitwise::or_, MIRType_Int32x4); + addTo->add(mhi); + MInstruction* explo = + MSimdConstant::New(alloc, SimdConstant::SplatX4(0x4b000000), MIRType_Int32x4); + addTo->add(explo); + MInstruction* mlo = + MSimdBinaryBitwise::New(alloc, lo, explo, MSimdBinaryBitwise::or_, MIRType_Int32x4); + addTo->add(mlo); + + // Bit-cast both to Float32x4. + MInstruction* fhi = + MSimdReinterpretCast::New(alloc, mhi, MIRType_Int32x4, MIRType_Float32x4); + addTo->add(fhi); + MInstruction* flo = + MSimdReinterpretCast::New(alloc, mlo, MIRType_Int32x4, MIRType_Float32x4); + addTo->add(flo); + + // Subtract out the bias: 0x1.0p39f + 0x1.0p23f. + // MSVC doesn't support the hexadecimal float syntax. + const float BiasValue = 549755813888.f + 8388608.f; + MInstruction* bias = + MSimdConstant::New(alloc, SimdConstant::SplatX4(BiasValue), MIRType_Float32x4); + addTo->add(bias); + MInstruction* fhi_debiased = + MSimdBinaryArith::New(alloc, fhi, bias, MSimdBinaryArith::Op_sub, MIRType_Float32x4); + addTo->add(fhi_debiased); + + // Compute the final result. + MInstruction* result = MSimdBinaryArith::New(alloc, fhi_debiased, flo, + MSimdBinaryArith::Op_add, MIRType_Float32x4); + addTo->add(result); + + return result; + } + + if (fromType == MIRType_Float32x4 && toType == MIRType_Int32x4) { + // The Float32x4 -> Uint32x4 conversion can throw if the input is out of + // range. This is handled by the LFloat32x4ToUint32x4 expansion. + MInstruction* ins = New(alloc, obj, fromType, toType, sign); + addTo->add(ins); + return ins; + } + + MOZ_CRASH("Unhandled SIMD type conversion"); +} + template static void PrintOpcodeOperation(T* mir, GenericPrinter& out) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index b7890c4ec30..91100753233 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1542,10 +1542,18 @@ class MSimdConvert : public MUnaryInstruction, public SimdPolicy<0>::Data { - MSimdConvert(MDefinition* obj, MIRType fromType, MIRType toType) - : MUnaryInstruction(obj) + // When either fromType or toType is an integer vector, should it be treated + // as signed or unsigned. Note that we don't support int-int conversions - + // use MSimdReinterpretCast for that. + SimdSign sign_; + + MSimdConvert(MDefinition* obj, MIRType fromType, MIRType toType, SimdSign sign) + : MUnaryInstruction(obj), sign_(sign) { MOZ_ASSERT(IsSimdType(toType)); + // All conversions are int <-> float, so signedness is required. + MOZ_ASSERT(sign != SimdSign::NotApplicable); + setResultType(toType); specialization_ = fromType; // expects fromType as input @@ -1562,20 +1570,35 @@ class MSimdConvert MIRType toType) { MOZ_ASSERT(IsSimdType(obj->type()) && fromType == obj->type()); - return new(alloc) MSimdConvert(obj, fromType, toType); + // AsmJS only has signed integer vectors for now. + return new(alloc) MSimdConvert(obj, fromType, toType, SimdSign::Signed); } static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType fromType, - MIRType toType) + MIRType toType, SimdSign sign) { - return new(alloc) MSimdConvert(obj, fromType, toType); + return new(alloc) MSimdConvert(obj, fromType, toType, sign); + } + + // Create a MSimdConvert instruction and add it to the basic block. + // Possibly create and add an equivalent sequence of instructions instead if + // the current target doesn't support the requested conversion directly. + // Return the inserted MInstruction that computes the converted value. + static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj, + MIRType fromType, MIRType toType, SimdSign sign); + + SimdSign signedness() const { + return sign_; } AliasSet getAliasSet() const override { return AliasSet::None(); } bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); + if (!congruentIfOperandsEqual(ins)) + return false; + const MSimdConvert* other = ins->toSimdConvert(); + return sign_ == other->sign_; } ALLOW_CLONE(MSimdConvert) }; diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index c941bc60a01..a67406c101b 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -220,6 +220,9 @@ static_assert(JitStackAlignment % SimdMemoryAlignment == 0, static const uint32_t AsmJSStackAlignment = SimdMemoryAlignment; +// Does this architecture support SIMD conversions between Uint32x4 and Float32x4? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; + static const Scale ScalePointer = TimesFour; class Instruction; diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index b7cf5e1b4a2..a836e7728db 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -176,6 +176,9 @@ static_assert(CodeAlignment % SimdMemoryAlignment == 0, static const uint32_t AsmJSStackAlignment = SimdMemoryAlignment; static const int32_t AsmJSGlobalRegBias = 1024; +// Does this architecture support SIMD conversions between Uint32x4 and Float32x4? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; + class Assembler : public vixl::Assembler { public: diff --git a/js/src/jit/mips32/Assembler-mips32.h b/js/src/jit/mips32/Assembler-mips32.h index 9ca575907ea..727fcab7a5a 100644 --- a/js/src/jit/mips32/Assembler-mips32.h +++ b/js/src/jit/mips32/Assembler-mips32.h @@ -98,6 +98,9 @@ static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment > static MOZ_CONSTEXPR_VAR uint32_t SimdMemoryAlignment = 8; static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; +// Does this architecture support SIMD conversions between Uint32x4 and Float32x4? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; + static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesFour; class Assembler : public AssemblerMIPSShared diff --git a/js/src/jit/mips64/Assembler-mips64.h b/js/src/jit/mips64/Assembler-mips64.h index b8bf551fd83..68ac086bb05 100644 --- a/js/src/jit/mips64/Assembler-mips64.h +++ b/js/src/jit/mips64/Assembler-mips64.h @@ -109,6 +109,9 @@ static MOZ_CONSTEXPR_VAR uint32_t SimdMemoryAlignment = 16; static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; +// Does this architecture support SIMD conversions between Uint32x4 and Float32x4? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; + static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesEight; class Assembler : public AssemblerMIPSShared diff --git a/js/src/jit/none/Architecture-none.h b/js/src/jit/none/Architecture-none.h index c1ae48e3597..bece31f4727 100644 --- a/js/src/jit/none/Architecture-none.h +++ b/js/src/jit/none/Architecture-none.h @@ -18,6 +18,9 @@ static const bool SupportsSimd = false; static const uint32_t SimdMemoryAlignment = 4; // Make it 4 to avoid a bunch of div-by-zero warnings static const uint32_t AsmJSStackAlignment = 8; +// Does this architecture support SIMD conversions between Uint32x4 and Float32x4? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; + class Registers { public: diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 24554d76d16..e7ae13d2579 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -3806,6 +3806,29 @@ class LFloat32x4ToInt32x4 : public LInstructionHelper<1, 1, 1> } }; +// Float32x4 to Uint32x4 needs one GPR temp and one FloatReg temp. +class LFloat32x4ToUint32x4 : public LInstructionHelper<1, 1, 2> +{ + public: + LIR_HEADER(Float32x4ToUint32x4); + explicit LFloat32x4ToUint32x4(const LAllocation& input, const LDefinition& tempR, + const LDefinition& tempF) + { + setOperand(0, input); + setTemp(0, tempR); + setTemp(1, tempF); + } + const LDefinition* tempR() { + return getTemp(0); + } + const LDefinition* tempF() { + return getTemp(1); + } + const MSimdConvert* mir() const { + return mir_->toSimdConvert(); + } +}; + // Double raised to a half power. class LPowHalfD : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 7719f115291..9a9830375e8 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -185,6 +185,7 @@ _(ValueToObjectOrNull) \ _(Int32x4ToFloat32x4) \ _(Float32x4ToInt32x4) \ + _(Float32x4ToUint32x4) \ _(Start) \ _(OsrEntry) \ _(OsrValue) \ diff --git a/js/src/jit/x86-shared/Architecture-x86-shared.h b/js/src/jit/x86-shared/Architecture-x86-shared.h index 1e5454541c5..53126115b7c 100644 --- a/js/src/jit/x86-shared/Architecture-x86-shared.h +++ b/js/src/jit/x86-shared/Architecture-x86-shared.h @@ -20,6 +20,9 @@ namespace js { namespace jit { +// Does this architecture support SIMD conversions between Uint32x4 and Float32x4? +static const bool SupportsUint32x4FloatConversions = false; + #if defined(JS_CODEGEN_X86) // In bytes: slots needed for potential memory->memory move spills. // +8 for cycles diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index a0e251349ae..359c56f2a0c 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2306,6 +2306,91 @@ CodeGeneratorX86Shared::visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIn } } +// Convert Float32x4 to Uint32x4. +// +// If any input lane value is out of range or NaN, bail out. +void +CodeGeneratorX86Shared::visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4* ins) +{ + FloatRegister in = ToFloatRegister(ins->input()); + FloatRegister out = ToFloatRegister(ins->output()); + Register temp = ToRegister(ins->tempR()); + FloatRegister tempF = ToFloatRegister(ins->tempF()); + + // Classify lane values into 4 disjoint classes: + // + // N-lanes: in < -0.0 + // A-lanes: -0.0 <= in <= 0x0.ffffffp31 + // B-lanes: 0x1.0p31 <= in <= 0x0.ffffffp32 + // V-lanes: 0x1.0p32 <= in, or isnan(in) + // + // We need to bail out to throw a RangeError if we see any N-lanes or + // V-lanes. + // + // For A-lanes and B-lanes, we make two float -> int32 conversions: + // + // A = cvttps2dq(in) + // B = cvttps2dq(in - 0x1.0p31f) + // + // Note that the subtraction for the B computation is exact for B-lanes. + // There is no rounding, so B is the low 31 bits of the correctly converted + // result. + // + // The cvttps2dq instruction produces 0x80000000 when the input is NaN or + // out of range for a signed int32_t. This conveniently provides the missing + // high bit for B, so the desired result is A for A-lanes and A|B for + // B-lanes. + + ScratchSimd128Scope scratch(masm); + + // First we need to filter out N-lanes. We need to use a floating point + // comparison to do that because cvttps2dq maps the negative range + // [-0x0.ffffffp0;-0.0] to 0. We can't simply look at the sign bits of in + // because -0.0 is a valid input. + // TODO: It may be faster to let ool code deal with -0.0 and skip the + // vcmpleps here. + masm.zeroFloat32x4(scratch); + masm.vcmpleps(Operand(in), scratch, scratch); + masm.vmovmskps(scratch, temp); + masm.cmp32(temp, Imm32(15)); + bailoutIf(Assembler::NotEqual, ins->snapshot()); + + // TODO: If the majority of lanes are A-lanes, it could be faster to compute + // A first, use vmovmskps to check for any non-A-lanes and handle them in + // ool code. OTOH, we we're wrong about the lane distribution, that would be + // slower. + + // Compute B in |scratch|. + static const float Adjust = 0x80000000; // 0x1.0p31f for the benefit of MSVC. + static const SimdConstant Bias = SimdConstant::SplatX4(-Adjust); + masm.loadConstantFloat32x4(Bias, scratch); + masm.packedAddFloat32(Operand(in), scratch); + masm.convertFloat32x4ToInt32x4(scratch, scratch); + + // Compute A in |out|. This is the last time we use |in| and the first time + // we use |out|, so we can tolerate if they are the same register. + masm.convertFloat32x4ToInt32x4(in, out); + + // Since we filtered out N-lanes, we can identify A-lanes by the sign bits + // in A: Any A-lanes will be positive in A, and B-lanes and V-lanes will be + // 0x80000000 in A. Compute a mask of non-A-lanes into |tempF|. + masm.zeroFloat32x4(tempF); + masm.packedGreaterThanInt32x4(Operand(out), tempF); + + // Clear the A-lanes in B. + masm.bitwiseAndX4(Operand(tempF), scratch); + + // Compute the final result: A for A-lanes, A|B for B-lanes. + masm.bitwiseOrX4(Operand(scratch), out); + + // We still need to filter out the V-lanes. They would show up as 0x80000000 + // in both A and B. Since we cleared the valid A-lanes in B, the V-lanes are + // the remaining negative lanes in B. + masm.vmovmskps(scratch, temp); + masm.cmp32(temp, Imm32(0)); + bailoutIf(Assembler::NotEqual, ins->snapshot()); +} + void CodeGeneratorX86Shared::visitSimdValueInt32x4(LSimdValueInt32x4* ins) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 48829e1e15f..5e047e0c247 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -261,6 +261,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitFloat32x4(LFloat32x4* ins); void visitInt32x4ToFloat32x4(LInt32x4ToFloat32x4* ins); void visitFloat32x4ToInt32x4(LFloat32x4ToInt32x4* ins); + void visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4* ins); void visitSimdReinterpretCast(LSimdReinterpretCast* lir); void visitSimdExtractElementB(LSimdExtractElementB* lir); void visitSimdExtractElementI(LSimdExtractElementI* lir); From 1d0b38ad37bd0c3160f8280f441623c6c451230b Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:07 -0800 Subject: [PATCH 63/91] Bug 1240796 - Implement unsigned SIMD compares. r=sunfish Add a MSimdBinaryComp::AddLegalized function which expands unsigned compares on target platforms that don't support them directly. The early expansion exposes the constants to MIR optimizations. Unsigned comparison is expressed in terms of signed comparison by offsetting both sides by INT_MIN. --- js/src/jit-test/tests/SIMD/compare.js | 10 +++++ js/src/jit/Lowering.cpp | 2 + js/src/jit/MCallOptimize.cpp | 7 +-- js/src/jit/MIR.cpp | 43 +++++++++++++++++++ js/src/jit/MIR.h | 35 +++++++++++---- js/src/jit/arm/Assembler-arm.h | 3 ++ js/src/jit/arm64/Assembler-arm64.h | 3 ++ js/src/jit/mips32/Assembler-mips32.h | 3 ++ js/src/jit/mips64/Assembler-mips64.h | 3 ++ js/src/jit/none/Architecture-none.h | 3 ++ .../jit/x86-shared/Architecture-x86-shared.h | 5 ++- 11 files changed, 103 insertions(+), 14 deletions(-) diff --git a/js/src/jit-test/tests/SIMD/compare.js b/js/src/jit-test/tests/SIMD/compare.js index 6b33c635f41..21dca20cd8f 100644 --- a/js/src/jit-test/tests/SIMD/compare.js +++ b/js/src/jit-test/tests/SIMD/compare.js @@ -9,6 +9,9 @@ function f() { var i1 = SIMD.Int32x4(1, 2, -3, 4); var i2 = SIMD.Int32x4(1, -2, 3, 0); + var u1 = SIMD.Uint32x4(1, 2, -3, 4); + var u2 = SIMD.Uint32x4(1, -2, 3, 0x80000000); + for (var i = 0; i < 150; i++) { assertEqX4(SIMD.Int32x4.lessThan(i1, i2), [false, false, true, false]); assertEqX4(SIMD.Int32x4.lessThanOrEqual(i1, i2), [true, false, true, false]); @@ -17,6 +20,13 @@ function f() { assertEqX4(SIMD.Int32x4.greaterThan(i1, i2), [false, true, false, true]); assertEqX4(SIMD.Int32x4.greaterThanOrEqual(i1, i2), [true, true, false, true]); + assertEqX4(SIMD.Uint32x4.lessThan(u1, u2), [false, true, false, true]); + assertEqX4(SIMD.Uint32x4.lessThanOrEqual(u1, u2), [true, true, false, true]); + assertEqX4(SIMD.Uint32x4.equal(u1, u2), [true, false, false, false]); + assertEqX4(SIMD.Uint32x4.notEqual(u1, u2), [false, true, true, true]); + assertEqX4(SIMD.Uint32x4.greaterThan(u1, u2), [false, false, true, false]); + assertEqX4(SIMD.Uint32x4.greaterThanOrEqual(u1, u2), [true, false, true, false]); + assertEqX4(SIMD.Float32x4.lessThan(f1, f2), [false, true, true, false]); assertEqX4(SIMD.Float32x4.lessThanOrEqual(f1, f2), [false, true, true, false]); assertEqX4(SIMD.Float32x4.equal(f1, f2), [false, false, false, false]); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index d36cb23e1df..c227357e1c0 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4310,9 +4310,11 @@ LIRGenerator::visitSimdBinaryComp(MSimdBinaryComp* ins) ins->reverse(); if (ins->specialization() == MIRType_Int32x4) { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); LSimdBinaryCompIx4* add = new(alloc()) LSimdBinaryCompIx4(); lowerForCompIx4(add, ins, ins->lhs(), ins->rhs()); } else if (ins->specialization() == MIRType_Float32x4) { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); LSimdBinaryCompFx4* add = new(alloc()) LSimdBinaryCompFx4(); lowerForCompFx4(add, ins, ins->lhs(), ins->rhs()); } else { diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 36e54932064..0bb9ccac557 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3414,10 +3414,6 @@ IonBuilder::inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp: if (!canInlineSimd(callInfo, native, 2, &templateObj)) return InliningStatus_NotInlined; - // TODO JSO: Implement unsigned integer comparisons. - if (sign == SimdSign::Unsigned) - return InliningStatus_NotInlined; - // If the type of any of the arguments is neither a SIMD type, an Object // type, or a Value, then the applyTypes phase will add a fallible box & // unbox sequence. This does not matter much as all binary SIMD @@ -3425,7 +3421,8 @@ IonBuilder::inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp: // with non SIMD-arguments. MDefinition* lhs = callInfo.getArg(0); MDefinition* rhs = callInfo.getArg(1); - MSimdBinaryComp* ins = MSimdBinaryComp::New(alloc(), lhs, rhs, op, mirType); + MInstruction* ins = + MSimdBinaryComp::AddLegalized(alloc(), current, lhs, rhs, op, mirType, sign); return boxSimd(callInfo, ins, templateObj); } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 3e18282f6c9..14f2da68851 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1136,6 +1136,49 @@ MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition MOZ_CRASH("Unhandled SIMD type conversion"); } +MInstruction* +MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, + MDefinition* right, Operation op, MIRType opType, SimdSign sign) +{ + bool IsEquality = op == equal || op == notEqual; + + if (!SupportsUint32x4Compares && sign == SimdSign::Unsigned && !IsEquality) { + MOZ_ASSERT(opType == MIRType_Int32x4); + // This is an order comparison of Uint32x4 vectors which are not supported on this target. + // Simply offset |left| and |right| by INT_MIN, then do a signed comparison. + MInstruction* bias = + MSimdConstant::New(alloc, SimdConstant::SplatX4(int32_t(0x80000000)), opType); + addTo->add(bias); + + // Add the bias. + MInstruction* bleft = + MSimdBinaryArith::New(alloc, left, bias, MSimdBinaryArith::Op_add, opType); + addTo->add(bleft); + MInstruction* bright = + MSimdBinaryArith::New(alloc, right, bias, MSimdBinaryArith::Op_add, opType); + addTo->add(bright); + + // Do the equivalent signed comparison. + MInstruction* result = + MSimdBinaryComp::New(alloc, bleft, bright, op, opType, SimdSign::Signed); + addTo->add(result); + + return result; + } + + if (!SupportsUint32x4Compares && sign == SimdSign::Unsigned && opType == MIRType_Int32x4) { + // The sign doesn't matter for equality tests. Flip it to make the + // backend assertions happy. + MOZ_ASSERT(IsEquality); + sign = SimdSign::Signed; + } + + // This is a legal operation already. Just create the instruction requested. + MInstruction* result = MSimdBinaryComp::New(alloc, left, right, op, opType, sign); + addTo->add(result); + return result; +} + template static void PrintOpcodeOperation(T* mir, GenericPrinter& out) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 91100753233..2ec4fe82a72 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2133,8 +2133,10 @@ class MSimdUnaryArith }; // Compares each value of a SIMD vector to each corresponding lane's value of -// another SIMD vector, and returns a int32x4 vector containing the results of +// another SIMD vector, and returns a boolean vector containing the results of // the comparison: all bits are set to 1 if the comparison is true, 0 otherwise. +// When comparing integer vectors, a SimdSign must be provided to request signed +// or unsigned comparison. class MSimdBinaryComp : public MBinaryInstruction, public SimdAllPolicy::Data @@ -2157,10 +2159,14 @@ class MSimdBinaryComp private: Operation operation_; + SimdSign sign_; - MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, MIRType opType) - : MBinaryInstruction(left, right), operation_(op) + MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, MIRType opType, + SimdSign sign) + : MBinaryInstruction(left, right), operation_(op), sign_(sign) { + MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(opType), + "Signedness must be specified for integer SIMD compares"); setResultType(MIRType_Bool32x4); specialization_ = opType; setMovable(); @@ -2175,20 +2181,32 @@ class MSimdBinaryComp { MOZ_ASSERT(IsSimdType(left->type())); MOZ_ASSERT(left->type() == right->type()); - return new(alloc) MSimdBinaryComp(left, right, op, left->type()); + // AsmJS only has signed vectors for now. + SimdSign sign = + IsIntegerSimdType(left->type()) ? SimdSign::Signed : SimdSign::NotApplicable; + return new (alloc) MSimdBinaryComp(left, right, op, left->type(), sign); } static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, - Operation op, MIRType opType) + Operation op, MIRType opType, SimdSign sign) { - return new(alloc) MSimdBinaryComp(left, right, op, opType); + return new(alloc) MSimdBinaryComp(left, right, op, opType, sign); } - AliasSet getAliasSet() const override { + // Create a MSimdBinaryComp or an equivalent sequence of instructions + // supported by the current target. + // Add all instructions to the basic block |addTo|. + static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, + MDefinition* right, Operation op, MIRType opType, + SimdSign sign); + + AliasSet getAliasSet() const override + { return AliasSet::None(); } Operation operation() const { return operation_; } + SimdSign signedness() const { return sign_; } MIRType specialization() const { return specialization_; } // Swap the operands and reverse the comparison predicate. @@ -2211,7 +2229,8 @@ class MSimdBinaryComp return false; const MSimdBinaryComp* other = ins->toSimdBinaryComp(); return specialization_ == other->specialization() && - operation_ == other->operation(); + operation_ == other->operation() && + sign_ == other->signedness(); } void printOpcode(GenericPrinter& out) const override; diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index a67406c101b..adea5edc29b 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -223,6 +223,9 @@ static const uint32_t AsmJSStackAlignment = SimdMemoryAlignment; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; +// Does this architecture support comparisons of unsigned 32x4 integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; + static const Scale ScalePointer = TimesFour; class Instruction; diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index a836e7728db..22424aecae9 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -179,6 +179,9 @@ static const int32_t AsmJSGlobalRegBias = 1024; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; +// Does this architecture support comparisons of unsigned 32x4 integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; + class Assembler : public vixl::Assembler { public: diff --git a/js/src/jit/mips32/Assembler-mips32.h b/js/src/jit/mips32/Assembler-mips32.h index 727fcab7a5a..d0519ae21e1 100644 --- a/js/src/jit/mips32/Assembler-mips32.h +++ b/js/src/jit/mips32/Assembler-mips32.h @@ -101,6 +101,9 @@ static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; +// Does this architecture support comparisons of unsigned 32x4 integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; + static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesFour; class Assembler : public AssemblerMIPSShared diff --git a/js/src/jit/mips64/Assembler-mips64.h b/js/src/jit/mips64/Assembler-mips64.h index 68ac086bb05..d5484a828eb 100644 --- a/js/src/jit/mips64/Assembler-mips64.h +++ b/js/src/jit/mips64/Assembler-mips64.h @@ -112,6 +112,9 @@ static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; +// Does this architecture support comparisons of unsigned 32x4 integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; + static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesEight; class Assembler : public AssemblerMIPSShared diff --git a/js/src/jit/none/Architecture-none.h b/js/src/jit/none/Architecture-none.h index bece31f4727..2bef237f72a 100644 --- a/js/src/jit/none/Architecture-none.h +++ b/js/src/jit/none/Architecture-none.h @@ -21,6 +21,9 @@ static const uint32_t AsmJSStackAlignment = 8; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; +// Does this architecture support comparisons of unsigned 32x4 integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; + class Registers { public: diff --git a/js/src/jit/x86-shared/Architecture-x86-shared.h b/js/src/jit/x86-shared/Architecture-x86-shared.h index 53126115b7c..134145ef67e 100644 --- a/js/src/jit/x86-shared/Architecture-x86-shared.h +++ b/js/src/jit/x86-shared/Architecture-x86-shared.h @@ -21,7 +21,10 @@ namespace js { namespace jit { // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? -static const bool SupportsUint32x4FloatConversions = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; + +// Does this architecture support comparisons of unsigned 32x4 integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; #if defined(JS_CODEGEN_X86) // In bytes: slots needed for potential memory->memory move spills. From 4ab3fe84ba61be5636cb5925abac8b20d008cfa9 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 1 Feb 2016 14:55:07 -0800 Subject: [PATCH 64/91] Bug 1240796 - Implement Uint32x4 extractLane in Ion. r=nbp Since Uint32 can't be represented in a MIRType_Int32, this function should return a MIRType_Double. Allow MSimdExtractElement(Uint32x4) to return a MIRType_Int32 too. It will work like the double version followed by MTruncateToInt32 which bitcasts the Uint32 value range into the Int32 value range. --- js/src/jit-test/tests/SIMD/getters.js | 6 +++ js/src/jit/Lowering.cpp | 11 ++++- js/src/jit/MCallOptimize.cpp | 11 ++--- js/src/jit/MIR.h | 36 +++++++++++++---- js/src/jit/shared/LIR-shared.h | 18 +++++++++ js/src/jit/shared/LOpcodes-shared.h | 1 + .../x86-shared/CodeGenerator-x86-shared.cpp | 40 +++++++++++-------- .../jit/x86-shared/CodeGenerator-x86-shared.h | 3 ++ 8 files changed, 96 insertions(+), 30 deletions(-) diff --git a/js/src/jit-test/tests/SIMD/getters.js b/js/src/jit-test/tests/SIMD/getters.js index ff20de41f4e..5a895bbe2f1 100644 --- a/js/src/jit-test/tests/SIMD/getters.js +++ b/js/src/jit-test/tests/SIMD/getters.js @@ -4,6 +4,7 @@ setJitCompilerOption("ion.warmup.trigger", 50); function f() { var i4 = SIMD.Int32x4(1, -2, 3, -4); + var u4 = SIMD.Uint32x4(1, -2, 3, 0x88000000); var b4 = SIMD.Bool32x4(true, true, false, true); @@ -19,6 +20,11 @@ function f() { assertEq(SIMD.Int32x4.extractLane(i4, 2), 3); assertEq(SIMD.Int32x4.extractLane(i4, 3), -4); + assertEq(SIMD.Uint32x4.extractLane(u4, 0), 1); + assertEq(SIMD.Uint32x4.extractLane(u4, 1), -2 >>> 0); + assertEq(SIMD.Uint32x4.extractLane(u4, 2), 3); + assertEq(SIMD.Uint32x4.extractLane(u4, 3), 0x88000000); + assertEq(SIMD.Float32x4.extractLane(f4, 0), v); assertEq(SIMD.Float32x4.extractLane(f4, 1), NaN); assertEq(SIMD.Float32x4.extractLane(f4, 2), Infinity); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index c227357e1c0..1318f2cc275 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4149,18 +4149,27 @@ LIRGenerator::visitSimdExtractElement(MSimdExtractElement* ins) switch (ins->input()->type()) { case MIRType_Int32x4: { + MOZ_ASSERT(ins->signedness() != SimdSign::NotApplicable); // Note: there could be int16x8 in the future, which doesn't use the // same instruction. We either need to pass the arity or create new LIns. LUse use = useRegisterAtStart(ins->input()); - define(new(alloc()) LSimdExtractElementI(use), ins); + if (ins->type() == MIRType_Double) { + // Extract an Uint32 lane into a double. + MOZ_ASSERT(ins->signedness() == SimdSign::Unsigned); + define(new (alloc()) LSimdExtractElementU2D(use, temp()), ins); + } else { + define(new (alloc()) LSimdExtractElementI(use), ins); + } break; } case MIRType_Float32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); LUse use = useRegisterAtStart(ins->input()); define(new(alloc()) LSimdExtractElementF(use), ins); break; } case MIRType_Bool32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); LUse use = useRegisterAtStart(ins->input()); define(new(alloc()) LSimdExtractElementB(use), ins); break; diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 0bb9ccac557..1d8339f13ac 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3468,10 +3468,6 @@ IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, return InliningStatus_NotInlined; } - // TODO JSO: Implement unsigned integer lane values. - if (sign == SimdSign::Unsigned) - return InliningStatus_NotInlined; - MDefinition* arg = callInfo.getArg(1); if (!arg->isConstantValue() || arg->type() != MIRType_Int32) return InliningStatus_NotInlined; @@ -3481,8 +3477,13 @@ IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, // See comment in inlineSimdBinary MIRType laneType = SimdTypeToLaneType(vecType); + + // An Uint32 lane can't be represented in MIRType_Int32. Get it as a double. + if (sign == SimdSign::Unsigned && vecType == MIRType_Int32x4) + laneType = MIRType_Double; + MSimdExtractElement* ins = MSimdExtractElement::New(alloc(), callInfo.getArg(0), - vecType, laneType, SimdLane(lane)); + vecType, laneType, SimdLane(lane), sign); current->add(ins); current->push(ins); callInfo.setImplicitlyUsedUnchecked(); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 2ec4fe82a72..5d078f0ad52 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1642,23 +1642,38 @@ class MSimdReinterpretCast }; // Extracts a lane element from a given vector type, given by its lane symbol. +// +// For integer SIMD types, a SimdSign must be provided so the lane value can be +// converted to a scalar correctly. class MSimdExtractElement : public MUnaryInstruction, public SimdPolicy<0>::Data { protected: SimdLane lane_; + SimdSign sign_; - MSimdExtractElement(MDefinition* obj, MIRType vecType, MIRType laneType, SimdLane lane) - : MUnaryInstruction(obj), lane_(lane) + MSimdExtractElement(MDefinition* obj, MIRType vecType, MIRType laneType, SimdLane lane, + SimdSign sign) + : MUnaryInstruction(obj), lane_(lane), sign_(sign) { MOZ_ASSERT(IsSimdType(vecType)); MOZ_ASSERT(uint32_t(lane) < SimdTypeToLength(vecType)); MOZ_ASSERT(!IsSimdType(laneType)); + MOZ_ASSERT((sign != SimdSign::NotApplicable) == IsIntegerSimdType(vecType), + "Signedness must be specified for integer SIMD extractLanes"); // The resulting type should match the lane type. // Allow extracting boolean lanes directly into an Int32 (for asm.js). + // Allow extracting Uint32 lanes into a double. + // + // We also allow extracting Uint32 lanes into a MIRType_Int32. This is + // equivalent to extracting the Uint32 lane to a double and then + // applying MTruncateToInt32, but it bypasses the conversion to/from + // double. MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType || - (IsBooleanSimdType(vecType) && laneType == MIRType_Int32)); + (IsBooleanSimdType(vecType) && laneType == MIRType_Int32) || + (vecType == MIRType_Int32x4 && laneType == MIRType_Double && + sign == SimdSign::Unsigned)); setMovable(); specialization_ = vecType; @@ -1671,19 +1686,26 @@ class MSimdExtractElement static MSimdExtractElement* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType type, SimdLane lane) { - return new(alloc) MSimdExtractElement(obj, obj->type(), type, lane); + // Only signed integer types in AsmJS so far. + SimdSign sign = + IsIntegerSimdType(obj->type()) ? SimdSign::Signed : SimdSign::NotApplicable; + return new (alloc) MSimdExtractElement(obj, obj->type(), type, lane, sign); } static MSimdExtractElement* New(TempAllocator& alloc, MDefinition* obj, MIRType vecType, - MIRType scalarType, SimdLane lane) + MIRType scalarType, SimdLane lane, SimdSign sign) { - return new(alloc) MSimdExtractElement(obj, vecType, scalarType, lane); + return new(alloc) MSimdExtractElement(obj, vecType, scalarType, lane, sign); } SimdLane lane() const { return lane_; } + SimdSign signedness() const { + return sign_; + } + AliasSet getAliasSet() const override { return AliasSet::None(); } @@ -1691,7 +1713,7 @@ class MSimdExtractElement if (!ins->isSimdExtractElement()) return false; const MSimdExtractElement* other = ins->toSimdExtractElement(); - if (other->lane_ != lane_) + if (other->lane_ != lane_ || other->sign_ != sign_) return false; return congruentIfOperandsEqual(other); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index e7ae13d2579..17757252759 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -280,6 +280,24 @@ class LSimdExtractElementF : public LSimdExtractElementBase {} }; +// Extracts an element from an Uint32x4 SIMD vector, converts to double. +class LSimdExtractElementU2D : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(SimdExtractElementU2D); + explicit LSimdExtractElementU2D(const LAllocation& base, const LDefinition& temp) { + setOperand(0, base); + setTemp(0, temp); + } + SimdLane lane() const { + return mir_->toSimdExtractElement()->lane(); + } + const LDefinition* temp() { + return getTemp(0); + } +}; + + class LSimdInsertElementBase : public LInstructionHelper<1, 2, 0> { protected: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 9a9830375e8..e31456844f1 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -26,6 +26,7 @@ _(SimdAnyTrue) \ _(SimdReinterpretCast) \ _(SimdExtractElementI) \ + _(SimdExtractElementU2D) \ _(SimdExtractElementB) \ _(SimdExtractElementF) \ _(SimdInsertElementI) \ diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 359c56f2a0c..012115ccbff 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2484,13 +2484,10 @@ CodeGeneratorX86Shared::visitSimdReinterpretCast(LSimdReinterpretCast* ins) } } +// Extract an integer lane from the vector register |input| and place it in |output|. void -CodeGeneratorX86Shared::visitSimdExtractElementB(LSimdExtractElementB* ins) +CodeGeneratorX86Shared::emitSimdExtractLane(FloatRegister input, Register output, unsigned lane) { - FloatRegister input = ToFloatRegister(ins->input()); - Register output = ToRegister(ins->output()); - - SimdLane lane = ins->lane(); if (lane == LaneX) { // The value we want to extract is in the low double-word masm.moveLowInt32(input, output); @@ -2501,6 +2498,15 @@ CodeGeneratorX86Shared::visitSimdExtractElementB(LSimdExtractElementB* ins) masm.shuffleInt32(mask, input, ScratchSimd128Reg); masm.moveLowInt32(ScratchSimd128Reg, output); } +} + +void +CodeGeneratorX86Shared::visitSimdExtractElementB(LSimdExtractElementB* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + emitSimdExtractLane(input, output, ins->lane()); // We need to generate a 0/1 value. We have 0/-1. masm.and32(Imm32(1), output); @@ -2512,18 +2518,18 @@ CodeGeneratorX86Shared::visitSimdExtractElementI(LSimdExtractElementI* ins) FloatRegister input = ToFloatRegister(ins->input()); Register output = ToRegister(ins->output()); - SimdLane lane = ins->lane(); - if (lane == LaneX) { - // The value we want to extract is in the low double-word - masm.moveLowInt32(input, output); - } else if (AssemblerX86Shared::HasSSE41()) { - masm.vpextrd(lane, input, output); - } else { - uint32_t mask = MacroAssembler::ComputeShuffleMask(lane); - ScratchSimd128Scope scratch(masm); - masm.shuffleInt32(mask, input, scratch); - masm.moveLowInt32(scratch, output); - } + emitSimdExtractLane(input, output, ins->lane()); +} + +void +CodeGeneratorX86Shared::visitSimdExtractElementU2D(LSimdExtractElementU2D* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + Register temp = ToRegister(ins->temp()); + + emitSimdExtractLane(input, temp, ins->lane()); + masm.convertUInt32ToDouble(temp, output); } void diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 5e047e0c247..73f46f49397 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -185,6 +185,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base); + void emitSimdExtractLane(FloatRegister input, Register output, unsigned lane); + public: CodeGeneratorX86Shared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); @@ -265,6 +267,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdReinterpretCast(LSimdReinterpretCast* lir); void visitSimdExtractElementB(LSimdExtractElementB* lir); void visitSimdExtractElementI(LSimdExtractElementI* lir); + void visitSimdExtractElementU2D(LSimdExtractElementU2D* lir); void visitSimdExtractElementF(LSimdExtractElementF* lir); void visitSimdInsertElementI(LSimdInsertElementI* lir); void visitSimdInsertElementF(LSimdInsertElementF* lir); From da2a62eca6a10a1cfa9d1a9d2966ddfc1e43dacd Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Mon, 1 Feb 2016 16:50:57 -0600 Subject: [PATCH 65/91] Bug 1243413 - Avoid hiding windowed plugins that aren't contained within the active scroll frame. r=roc --- layout/generic/nsGfxScrollFrame.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index b5058637ca1..e8055cfed8c 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1992,8 +1992,12 @@ ScrollFrameHelper::CompleteAsyncScroll(const nsRect &aRange, nsIAtom* aOrigin) } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +struct PluginSearchCtx { + nsIFrame* outer; + bool begin; +}; static void -NotifyPluginFramesCallback(nsISupports* aSupports, void* aFlag) +NotifyPluginFramesCallback(nsISupports* aSupports, void* aCtx) { nsCOMPtr content = do_QueryInterface(aSupports); if (content) { @@ -2001,16 +2005,22 @@ NotifyPluginFramesCallback(nsISupports* aSupports, void* aFlag) if (frame) { nsPluginFrame* plugin = do_QueryFrame(frame); if (plugin) { - plugin->SetScrollVisibility(aFlag != nullptr); + PluginSearchCtx* pCtx = static_cast(aCtx); + // Check to be sure this plugin is contained within a subframe of + // the nsGfxScrollFrame that initiated this callback. + if (nsLayoutUtils::IsAncestorFrameCrossDoc(pCtx->outer, plugin, nullptr)) { + plugin->SetScrollVisibility(pCtx->begin); + } } } } } + static bool -NotifyPluginSubframesCallback(nsIDocument* aDocument, void* aFlag) +NotifyPluginSubframesCallback(nsIDocument* aDocument, void* aCtx) { aDocument->EnumerateActivityObservers(NotifyPluginFramesCallback, - aFlag); + aCtx); return true; } #endif @@ -2025,11 +2035,11 @@ ScrollFrameHelper::NotifyPluginFrames(AsyncScrollEventType aEvent) if (XRE_IsContentProcess()) { if (aEvent != mAsyncScrollEvent) { nsPresContext* presContext = mOuter->PresContext(); - bool begin = (aEvent == BEGIN_DOM); + PluginSearchCtx ctx = { mOuter, (aEvent == BEGIN_DOM) }; presContext->Document()->EnumerateActivityObservers(NotifyPluginFramesCallback, - (void*)begin); + (void*)&ctx); presContext->Document()->EnumerateSubDocuments(NotifyPluginSubframesCallback, - (void*)begin); + (void*)&ctx); mAsyncScrollEvent = aEvent; } From bab58a21ea9deadcddab593ddc5eaabc54495240 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Mon, 1 Feb 2016 15:35:25 -0800 Subject: [PATCH 66/91] Bug 993311 - Followup to fix b2g builds r=khuey --- dom/webidl/MozNetworkStats.webidl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/webidl/MozNetworkStats.webidl b/dom/webidl/MozNetworkStats.webidl index c6303cadb6b..27243f3ba25 100644 --- a/dom/webidl/MozNetworkStats.webidl +++ b/dom/webidl/MozNetworkStats.webidl @@ -14,7 +14,7 @@ dictionary NetworkStatsGetOptions * Note that, these two options cannot be specified at the same time for now; * others, an NS_ERROR_NOT_IMPLMENTED exception will be thrown. */ - DOMString appManifestURL? = null; + DOMString? appManifestURL = null; DOMString serviceType = ""; /** * If it is set as true, only the browsing traffic, which is generated from From 9b81d559624ee357c2ceb464b5853e5800afcbc3 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Tue, 2 Feb 2016 11:05:43 +1100 Subject: [PATCH 67/91] Bug 1244092 - Require Update 1 to build if using Visual C++ 2015. r=gps --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 8e9fb5feedb..f5dd5ac5d0e 100644 --- a/configure.in +++ b/configure.in @@ -530,7 +530,7 @@ case "$target" in MSVS_VERSION=2013 MSVC_C_RUNTIME_DLL=msvcr120.dll MSVC_CXX_RUNTIME_DLL=msvcp120.dll - elif test "$_CC_MAJOR_VERSION" = "19"; then + elif test "$_CC_MAJOR_VERSION" = "19" -a "$_CC_BUILD_VERSION" -ge "23506"; then _CC_SUITE=14 MSVS_VERSION=2015 MSVC_C_RUNTIME_DLL=vcruntime140.dll @@ -557,7 +557,7 @@ case "$target" in CXXFLAGS="$CXXFLAGS -wd4091" else AC_MSG_ERROR([This version (${_CC_MAJOR_VERSION}.${_CC_MINOR_VERSION}.${_CC_BUILD_VERSION}) of the MSVC compiler is unsupported. -You must install Visual C++ 2013 Update 3 or newer in order to build. +You must install Visual C++ 2013 Update 3, Visual C++ 2015 Update 1, or newer in order to build. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) fi AC_SUBST(MSVS_VERSION) From c02445787877e272e566075bbd73a9d055c256f3 Mon Sep 17 00:00:00 2001 From: Yoshi Huang Date: Fri, 29 Jan 2016 15:19:56 -0800 Subject: [PATCH 68/91] Bug 1240651 - Annotate addonId into crash report (r=bholley) --- caps/BasePrincipal.cpp | 11 ++++++++++- caps/moz.build | 7 ++++++- caps/nsScriptSecurityManager.cpp | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index 052571d6995..aa4b7b49ab1 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -7,6 +7,9 @@ #include "mozilla/BasePrincipal.h" #include "nsDocShell.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif #include "nsIAddonPolicyService.h" #include "nsIContentSecurityPolicy.h" #include "nsIObjectInputStream.h" @@ -121,7 +124,13 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const } if (!mAddonId.IsEmpty()) { - MOZ_RELEASE_ASSERT(mAddonId.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound); + if (mAddonId.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) != kNotFound) { +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Crash_AddonId"), + NS_ConvertUTF16toUTF8(mAddonId)); +#endif + MOZ_CRASH(); + } params->Set(NS_LITERAL_STRING("addonId"), mAddonId); } diff --git a/caps/moz.build b/caps/moz.build index 365e8ddf8a6..92afc4b7b9e 100644 --- a/caps/moz.build +++ b/caps/moz.build @@ -32,8 +32,13 @@ EXPORTS.mozilla = [ 'BasePrincipal.h' ] -UNIFIED_SOURCES += [ +SOURCES += [ + # Compile this separately since nsExceptionHandler.h conflicts + # with something from nsNullPrincipal.cpp. 'BasePrincipal.cpp', +] + +UNIFIED_SOURCES += [ 'DomainPolicy.cpp', 'nsJSPrincipals.cpp', 'nsNullPrincipal.cpp', diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index febd4de1222..3ab4accdd40 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -27,6 +27,7 @@ #include "nsXPIDLString.h" #include "nsCRT.h" #include "nsCRTGlue.h" +#include "nsDocShell.h" #include "nsError.h" #include "nsDOMCID.h" #include "nsIXPConnect.h" From 055f5299604c616366689cbcfe1952e46c34cccc Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 1 Feb 2016 16:07:11 -0800 Subject: [PATCH 69/91] Bug 1241704 - Make sure nsIWebNavigation exists on docshell. r=mccr8 --- docshell/test/browser/browser.ini | 1 - docshell/test/browser/browser_bug670318.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docshell/test/browser/browser.ini b/docshell/test/browser/browser.ini index 34356b3a202..55c94f4e113 100644 --- a/docshell/test/browser/browser.ini +++ b/docshell/test/browser/browser.ini @@ -72,7 +72,6 @@ skip-if = buildapp == 'mulet' [browser_bug655270.js] [browser_bug655273.js] [browser_bug670318.js] -skip-if = (os == 'mac' && debug) # Bug 1241704 [browser_bug673467.js] [browser_bug852909.js] [browser_bug92473.js] diff --git a/docshell/test/browser/browser_bug670318.js b/docshell/test/browser/browser_bug670318.js index 841b5507ec3..0fb6e658aef 100644 --- a/docshell/test/browser/browser_bug670318.js +++ b/docshell/test/browser/browser_bug670318.js @@ -14,7 +14,7 @@ add_task(function* test() { yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) { yield ContentTask.spawn(browser, URL, function* (URL) { - let history = docShell.sessionHistory; + let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory; let count = 0; let testDone = {}; From 1747619c3f1abd48cf21cba8cfeb3beb07afd4ac Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 1 Feb 2016 16:10:08 -0800 Subject: [PATCH 70/91] Bug 1244736 - Remove `mach update-uuids` and related code; r=froydnj We no longer update UUIDs when changing XPIDL interfaces. `mach update-uuids` was invented to make this process easier. Delete the command and related code since it is no longer needed. --- tools/mach_commands.py | 115 ----------------------------------------- 1 file changed, 115 deletions(-) diff --git a/tools/mach_commands.py b/tools/mach_commands.py index f97bedd1a09..5edec6db628 100644 --- a/tools/mach_commands.py +++ b/tools/mach_commands.py @@ -68,81 +68,6 @@ class SearchProvider(object): self.mxr(term) -class Interface(object): - ''' - Represents an XPIDL interface, in what file it is defined, what it derives - from, what its uuid is, and where in the source file the uuid is. - ''' - def __init__(self, filename, production): - from xpidl import xpidl - assert isinstance(production, xpidl.Interface) - self.name = production.name - self.base = production.base - self.filename = filename - self.uuid = production.attributes.uuid - location = production.location - data = location._lexdata - attr_pos = data.rfind(b'[', 0, location._lexpos) - # uuid is always lowercase, but actual file content may not be. - self.uuid_pos = data[attr_pos:location._lexpos].lower() \ - .rfind(self.uuid) + attr_pos - - -class InterfaceRegistry(object): - ''' - Tracks XPIDL interfaces, and allow to search them by name and by the - interface they derive from. - ''' - def __init__(self): - self.by_name = {} - self.by_base = {} - - def get_by_name(self, name): - return self.by_name.get(name, []) - - def get_by_base(self, base): - return self.by_base.get(base, []) - - def add(self, interface): - l = self.by_name.setdefault(interface.name, []) - l.append(interface) - l = self.by_base.setdefault(interface.base, []) - l.append(interface) - - -class IDLUpdater(object): - ''' - Updates interfaces uuids in IDL files. - ''' - def __init__(self, interfaces): - from mozpack.copier import FileRegistry - self.interfaces = interfaces; - self.registry = FileRegistry() - - def add(self, name): - for interface in self.interfaces.get_by_name(name): - self._add(interface) - - def _add(self, interface): - from mozpack.files import GeneratedFile - from uuid import uuid4 - path = interface.filename - if not self.registry.contains(path): - self.registry.add(path, GeneratedFile(open(path).read())) - content = self.registry[path].content - content = content[:interface.uuid_pos] + str(uuid4()) + \ - content[interface.uuid_pos + len(interface.uuid):] - self.registry[path].content = content - - # Recurse through all the interfaces deriving from this one - for derived in self.interfaces.get_by_base(interface.name): - self._add(derived) - - def update(self): - for p, f in self.registry: - f.copy(p) - - @CommandProvider class UUIDProvider(object): @Command('uuid', category='misc', @@ -162,46 +87,6 @@ class UUIDProvider(object): pairs = tuple(map(lambda n: u[n:n+2], range(16, 32, 2))) print((' { ' + '0x%s, ' * 7 + '0x%s } }') % pairs) - @Command('update-uuids', category='misc', - description='Update IDL files with new UUIDs.') - @CommandArgument('--path', default='.', - help='Base path under which uuids will be searched.') - @CommandArgument('interfaces', nargs='+', - help='Changed interfaces whose UUIDs need to be updated. ' + - 'Their descendants are updated as well.') - def update_uuids(self, path, interfaces): - import os - from xpidl import xpidl - from mozpack.files import FileFinder - import mozpack.path as mozpath - from tempfile import mkdtemp - - finder = FileFinder(path, find_executables=False) - # Avoid creating xpidllex and xpidlyacc in the current directory. - tmpdir = mkdtemp() - try: - parser = xpidl.IDLParser(outputdir=tmpdir) - registry = InterfaceRegistry() - for p, f in finder.find('**/*.idl'): - p = mozpath.join(path, p) - try: - content = f.open().read() - idl = parser.parse(content, filename=p) - except Exception: - continue - for prod in idl.productions: - if isinstance(prod, xpidl.Interface): - registry.add(Interface(p, prod)) - finally: - import shutil - shutil.rmtree(tmpdir) - - updates = IDLUpdater(registry) - - for interface in interfaces: - updates.add(interface) - - updates.update() @CommandProvider class PastebinProvider(object): From a945fa6da29d3e925cd2bc30e7e11b3486fcc5e0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 2 Feb 2016 06:27:57 +1100 Subject: [PATCH 71/91] Bug 1244606 - Remove PL_DHASH_FASTCALL. r=froydnj. FindFreeEntry() has one caller, so using MOZ_ALWAYS_INLINE should be good enough for it. As for SearchTable(), NS_FASTCALL is the same as PL_DHASH_FASTCALL and so can be used instead. --- xpcom/glue/PLDHashTable.cpp | 4 ++-- xpcom/glue/PLDHashTable.h | 12 ++---------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/xpcom/glue/PLDHashTable.cpp b/xpcom/glue/PLDHashTable.cpp index a75ab4f6ec8..fa121bbacd2 100644 --- a/xpcom/glue/PLDHashTable.cpp +++ b/xpcom/glue/PLDHashTable.cpp @@ -336,7 +336,7 @@ PLDHashTable::Clear() // distinction is a bit grotty but this function is hot enough that these // differences are worthwhile. template -PLDHashEntryHdr* PL_DHASH_FASTCALL +PLDHashEntryHdr* NS_FASTCALL PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash) { MOZ_ASSERT(mEntryStore.Get()); @@ -405,7 +405,7 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash) // structure. // Avoiding the need for |aKey| means we can avoid needing a way to map entries // to keys, which means callers can use complex key types more easily. -PLDHashEntryHdr* PL_DHASH_FASTCALL +MOZ_ALWAYS_INLINE PLDHashEntryHdr* PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash) { MOZ_ASSERT(mEntryStore.Get()); diff --git a/xpcom/glue/PLDHashTable.h b/xpcom/glue/PLDHashTable.h index 9929862b975..88a9f3ab5fa 100644 --- a/xpcom/glue/PLDHashTable.h +++ b/xpcom/glue/PLDHashTable.h @@ -15,14 +15,6 @@ #include "mozilla/Types.h" #include "nscore.h" -#if defined(__GNUC__) && defined(__i386__) -#define PL_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) -#elif defined(XP_WIN) -#define PL_DHASH_FASTCALL __fastcall -#else -#define PL_DHASH_FASTCALL -#endif - typedef uint32_t PLDHashNumber; class PLDHashTable; @@ -550,10 +542,10 @@ private: enum SearchReason { ForSearchOrRemove, ForAdd }; template - PLDHashEntryHdr* PL_DHASH_FASTCALL + PLDHashEntryHdr* NS_FASTCALL SearchTable(const void* aKey, PLDHashNumber aKeyHash); - PLDHashEntryHdr* PL_DHASH_FASTCALL FindFreeEntry(PLDHashNumber aKeyHash); + PLDHashEntryHdr* FindFreeEntry(PLDHashNumber aKeyHash); bool ChangeTable(int aDeltaLog2); From 0d7177e5315841dfcf366c6b2ccb33d78b871ab2 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 1 Feb 2016 16:22:51 -0800 Subject: [PATCH 72/91] Fix stale intermediate surfaces in the D3D9 compositor. (bug 1244821, r=mattwoodrow) --- gfx/layers/d3d9/TextureD3D9.cpp | 15 ++++++++++----- gfx/layers/d3d9/TextureD3D9.h | 1 - 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp index fa5d0e78ab9..25f103d1a6f 100644 --- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -113,7 +113,6 @@ CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DTexture9* aTex const gfx::IntRect& aRect) : CompositingRenderTarget(aRect.TopLeft()) , mInitMode(aInit) - , mInitialized(false) { MOZ_COUNT_CTOR(CompositingRenderTargetD3D9); MOZ_ASSERT(aTexture); @@ -122,6 +121,10 @@ CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DTexture9* aTex mTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface)); NS_ASSERTION(mSurface, "Couldn't create surface for texture"); TextureSourceD3D9::SetSize(aRect.Size()); + + if (aInit == INIT_MODE_CLEAR) { + ClearOnBind(); + } } CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DSurface9* aSurface, @@ -130,11 +133,14 @@ CompositingRenderTargetD3D9::CompositingRenderTargetD3D9(IDirect3DSurface9* aSur : CompositingRenderTarget(aRect.TopLeft()) , mSurface(aSurface) , mInitMode(aInit) - , mInitialized(false) { MOZ_COUNT_CTOR(CompositingRenderTargetD3D9); MOZ_ASSERT(mSurface); TextureSourceD3D9::SetSize(aRect.Size()); + + if (aInit == INIT_MODE_CLEAR) { + ClearOnBind(); + } } CompositingRenderTargetD3D9::~CompositingRenderTargetD3D9() @@ -146,10 +152,9 @@ void CompositingRenderTargetD3D9::BindRenderTarget(IDirect3DDevice9* aDevice) { aDevice->SetRenderTarget(0, mSurface); - if (!mInitialized && - mInitMode == INIT_MODE_CLEAR) { - mInitialized = true; + if (mClearOnBind) { aDevice->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0); + mClearOnBind = false; } } diff --git a/gfx/layers/d3d9/TextureD3D9.h b/gfx/layers/d3d9/TextureD3D9.h index bc152648a4d..83a1b0b0a45 100644 --- a/gfx/layers/d3d9/TextureD3D9.h +++ b/gfx/layers/d3d9/TextureD3D9.h @@ -416,7 +416,6 @@ private: RefPtr mSurface; SurfaceInitMode mInitMode; - bool mInitialized; }; } From c4cf08e7cdb5f84123ebab8a8abde2226345a3b9 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 1 Feb 2016 16:27:38 -0800 Subject: [PATCH 73/91] Remove Compositor::mRenderBounds. (bug 1243071 part 1, r=mattwoodrow) --- gfx/layers/Compositor.cpp | 26 ++++++++++++++++++++------ gfx/layers/Compositor.h | 2 -- gfx/layers/opengl/CompositorOGL.cpp | 9 +++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp index 43c3ebf6651..b891eae8a7b 100644 --- a/gfx/layers/Compositor.cpp +++ b/gfx/layers/Compositor.cpp @@ -357,23 +357,37 @@ DecomposeIntoNoRepeatRects(const gfx::Rect& aRect, gfx::IntRect Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect, const gfx::Rect& aClipRect, - const gfx::Matrix4x4& aTransform) + const gfx::Matrix4x4& aTransform, + gfx::Matrix4x4* aOutTransform) { - gfx::Rect renderBounds = mRenderBounds; - // Compute the clip. - gfx::IntPoint offset = GetCurrentRenderTarget()->GetOrigin(); + gfx::IntPoint rtOffset = GetCurrentRenderTarget()->GetOrigin(); + gfx::IntSize rtSize = GetCurrentRenderTarget()->GetSize(); + + gfx::Rect renderBounds(0, 0, rtSize.width, rtSize.height); renderBounds.IntersectRect(renderBounds, aClipRect); - renderBounds.MoveBy(offset); + renderBounds.MoveBy(rtOffset); // Apply the layer transform. gfx::Rect dest = aTransform.TransformAndClipBounds(aRect, renderBounds); - dest -= offset; + dest -= rtOffset; + + // Ensure we don't round out to -1, which trips up Direct3D. + dest.IntersectRect(dest, gfx::Rect(0, 0, rtSize.width, rtSize.height)); // Round out to integer. gfx::IntRect result; dest.RoundOut(); dest.ToIntRect(&result); + + // Create a transform from adjusted clip space to render target space, + // translate it for the backdrop rect, then transform it into the backdrop's + // uv-space. + gfx::Matrix4x4 transform; + transform.PostScale(rtSize.width, rtSize.height, 1.0); + transform.PostTranslate(-result.x, -result.y, 0.0); + transform.PostScale(1 / float(result.width), 1 / float(result.height), 1.0); + *aOutTransform = transform; return result; } diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 5415f949d08..881c98671d5 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -552,8 +552,6 @@ protected: RefPtr mTarget; gfx::IntRect mTargetBounds; - gfx::Rect mRenderBounds; - #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 FenceHandle mReleaseFenceHandle; #endif diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 1f6c35cfac1..6c2adf9bb61 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -486,8 +486,6 @@ CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget) // Set the viewport correctly. mGLContext->fViewport(0, 0, size.width, size.height); - mRenderBounds = Rect(0, 0, size.width, size.height); - mViewportSize = size; if (!aRenderTarget->HasComplexProjection()) { @@ -1007,7 +1005,9 @@ CompositorOGL::DrawQuad(const Rect& aRect, } IntPoint offset = mCurrentRenderTarget->GetOrigin(); - Rect renderBound = mRenderBounds; + IntSize size = mCurrentRenderTarget->GetSize(); + + Rect renderBound(0, 0, size.width, size.height); renderBound.IntersectRect(renderBound, aClipRect); renderBound.MoveBy(offset); @@ -1021,7 +1021,8 @@ CompositorOGL::DrawQuad(const Rect& aRect, // Inflate a small size to avoid some numerical imprecision issue. destRect.Inflate(1, 1); destRect.MoveBy(-offset); - if (!mRenderBounds.Intersects(destRect)) { + renderBound = Rect(0, 0, size.width, size.height); + if (!renderBound.Intersects(destRect)) { return; } From 81b1d74b678c7f287739be5e219156ba34879c56 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 1 Feb 2016 16:27:49 -0800 Subject: [PATCH 74/91] Use double precision when computing the backdrop copy rect. (bug 1243071 part 2, r=mattwoodrow) --- gfx/2d/Rect.h | 4 ++-- gfx/layers/Compositor.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index f5b0f1a378f..35e5078cac8 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -184,8 +184,8 @@ struct RectTyped : { *aOut = IntRectTyped(int32_t(this->X()), int32_t(this->Y()), int32_t(this->Width()), int32_t(this->Height())); - return RectTyped(F(aOut->x), F(aOut->y), - F(aOut->width), F(aOut->height)) + return RectTyped(F(aOut->x), F(aOut->y), + F(aOut->width), F(aOut->height)) .IsEqualEdges(*this); } diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp index b891eae8a7b..1e4304c14e3 100644 --- a/gfx/layers/Compositor.cpp +++ b/gfx/layers/Compositor.cpp @@ -369,11 +369,13 @@ Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect, renderBounds.MoveBy(rtOffset); // Apply the layer transform. - gfx::Rect dest = aTransform.TransformAndClipBounds(aRect, renderBounds); + gfx::RectDouble dest = aTransform.TransformAndClipBounds( + gfx::RectDouble(aRect.x, aRect.y, aRect.width, aRect.height), + gfx::RectDouble(renderBounds.x, renderBounds.y, renderBounds.width, renderBounds.height)); dest -= rtOffset; // Ensure we don't round out to -1, which trips up Direct3D. - dest.IntersectRect(dest, gfx::Rect(0, 0, rtSize.width, rtSize.height)); + dest.IntersectRect(dest, gfx::RectDouble(0, 0, rtSize.width, rtSize.height)); // Round out to integer. gfx::IntRect result; From b4a533e07e13e2319cf535ebcc7083ab788e5b27 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 1 Feb 2016 16:28:00 -0800 Subject: [PATCH 75/91] Add software mix-blend mode support to the D3D9 compositor. (bug 1243071 part 3, r=bas,mattwoodrow) --- gfx/layers/Compositor.cpp | 7 +- gfx/layers/Compositor.h | 7 +- gfx/layers/d3d9/CompositorD3D9.cpp | 259 +++++++++++++++++++++++++++- gfx/layers/d3d9/CompositorD3D9.h | 14 ++ gfx/layers/opengl/CompositorOGL.cpp | 11 +- layout/tools/reftest/reftest.js | 2 + 6 files changed, 285 insertions(+), 15 deletions(-) diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp index 1e4304c14e3..e10b20a1ac0 100644 --- a/gfx/layers/Compositor.cpp +++ b/gfx/layers/Compositor.cpp @@ -358,7 +358,8 @@ gfx::IntRect Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect, const gfx::Rect& aClipRect, const gfx::Matrix4x4& aTransform, - gfx::Matrix4x4* aOutTransform) + gfx::Matrix4x4* aOutTransform, + gfx::Rect* aOutLayerQuad) { // Compute the clip. gfx::IntPoint rtOffset = GetCurrentRenderTarget()->GetOrigin(); @@ -377,6 +378,10 @@ Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect, // Ensure we don't round out to -1, which trips up Direct3D. dest.IntersectRect(dest, gfx::RectDouble(0, 0, rtSize.width, rtSize.height)); + if (aOutLayerQuad) { + *aOutLayerQuad = gfx::Rect(dest.x, dest.y, dest.width, dest.height); + } + // Round out to integer. gfx::IntRect result; dest.RoundOut(); diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 881c98671d5..1ec0fe763bd 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -518,11 +518,16 @@ protected: * Given a layer rect, clip, and transform, compute the area of the backdrop that * needs to be copied for mix-blending. The output transform translates from 0..1 * space into the backdrop rect space. + * + * The transformed layer quad is also optionally returned - this is the same as + * the result rect, before rounding. */ gfx::IntRect ComputeBackdropCopyRect( const gfx::Rect& aRect, const gfx::Rect& aClipRect, - const gfx::Matrix4x4& aTransform); + const gfx::Matrix4x4& aTransform, + gfx::Matrix4x4* aOutTransform, + gfx::Rect* aOutLayerQuad = nullptr); /** * Render time for the current composition. diff --git a/gfx/layers/d3d9/CompositorD3D9.cpp b/gfx/layers/d3d9/CompositorD3D9.cpp index eee154a9f23..fd8d3c03be4 100644 --- a/gfx/layers/d3d9/CompositorD3D9.cpp +++ b/gfx/layers/d3d9/CompositorD3D9.cpp @@ -73,6 +73,11 @@ CompositorD3D9::GetTextureFactoryIdentifier() ident.mMaxTextureSize = GetMaxTextureSize(); ident.mParentBackend = LayersBackend::LAYERS_D3D9; ident.mParentProcessId = XRE_GetProcessType(); + for (uint8_t op = 0; op < uint8_t(gfx::CompositionOp::OP_COUNT); op++) { + if (BlendOpIsMixBlendMode(gfx::CompositionOp(op))) { + ident.mSupportedBlendModes += gfx::CompositionOp(op); + } + } return ident; } @@ -128,10 +133,10 @@ CompositorD3D9::CreateRenderTarget(const gfx::IntRect &aRect, return MakeAndAddRef(texture, aInit, aRect); } -already_AddRefed -CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect, - const CompositingRenderTarget *aSource, - const gfx::IntPoint &aSourcePoint) +already_AddRefed +CompositorD3D9::CreateTexture(const gfx::IntRect& aRect, + const CompositingRenderTarget* aSource, + const gfx::IntPoint& aSourcePoint) { MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); @@ -189,6 +194,16 @@ CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect, } } + return texture.forget(); +} + +already_AddRefed +CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect, + const CompositingRenderTarget *aSource, + const gfx::IntPoint &aSourcePoint) +{ + RefPtr texture = CreateTexture(aRect, aSource, aSourcePoint); + return MakeAndAddRef(texture, INIT_MODE_NONE, aRect); @@ -288,6 +303,40 @@ CompositorD3D9::DrawQuad(const gfx::Rect &aRect, } } + gfx::Rect backdropDest; + gfx::IntRect backdropRect; + gfx::Matrix4x4 backdropTransform; + RefPtr backdropTexture; + gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER; + + if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { + EffectBlendMode *blendEffect = + static_cast(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()); + blendMode = blendEffect->mBlendMode; + + // Pixel Shader Model 2.0 is too limited to perform blending in the same way + // as Direct3D 11 - there are too many instructions, and we don't have + // configurable shaders (as we do with OGL) that would avoid a huge shader + // matrix. + // + // Instead, we use a multi-step process for blending on D3D9: + // (1) Capture the backdrop into a temporary surface. + // (2) Render the effect chain onto the backdrop, with OP_SOURCE. + // (3) Capture the backdrop again into another surface - these are our source pixels. + // (4) Perform a final blend step using software. + // (5) Blit the blended result back to the render target. + if (BlendOpIsMixBlendMode(blendMode)) { + backdropRect = ComputeBackdropCopyRect( + aRect, aClipRect, aTransform, &backdropTransform, &backdropDest); + + // If this fails, don't set a blend op. + backdropTexture = CreateTexture(backdropRect, mCurrentRT, backdropRect.TopLeft()); + if (!backdropTexture) { + blendMode = gfx::CompositionOp::OP_OVER; + } + } + } + RECT scissor; scissor.left = aClipRect.x; scissor.right = aClipRect.XMost(); @@ -474,15 +523,35 @@ CompositorD3D9::DrawQuad(const gfx::Rect &aRect, SetMask(aEffectChain, maskTexture); + if (BlendOpIsMixBlendMode(blendMode)) { + // Use SOURCE instead of OVER to get the original source pixels without + // having to render to another intermediate target. + d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + } if (!isPremultiplied) { d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); } d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + // Restore defaults. + if (BlendOpIsMixBlendMode(blendMode)) { + d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } if (!isPremultiplied) { d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); } + + // Final pass - if mix-blending, do it now that we have the backdrop and + // source textures. + if (BlendOpIsMixBlendMode(blendMode)) { + FinishMixBlend( + backdropRect, + backdropDest, + backdropTransform, + backdropTexture, + blendMode); + } } void @@ -777,5 +846,187 @@ CompositorD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode) gfx::LogFailure(msg); } +static inline already_AddRefed +GetSurfaceOfTexture(IDirect3DTexture9* aTexture) +{ + RefPtr surface; + HRESULT hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(surface)); + if (FAILED(hr)) { + gfxCriticalNote << "Failed to grab texture surface " << hexa(hr); + return nullptr; + } + return surface.forget(); +} + +static inline already_AddRefed +CreateDataSurfaceForTexture(IDirect3DDevice9* aDevice, + IDirect3DSurface9* aSource, + const D3DSURFACE_DESC& aDesc) +{ + RefPtr dest; + HRESULT hr = aDevice->CreateOffscreenPlainSurface( + aDesc.Width, aDesc.Height, + aDesc.Format, D3DPOOL_SYSTEMMEM, + getter_AddRefs(dest), nullptr); + if (FAILED(hr) || !dest) { + gfxCriticalNote << "Failed to create offscreen plain surface " << hexa(hr); + return nullptr; + } + + hr = aDevice->GetRenderTargetData(aSource, dest); + if (FAILED(hr)) { + gfxCriticalNote << "Failed to get render target data " << hexa(hr); + return nullptr; + } + + return dest.forget(); +} + +class AutoSurfaceLock +{ + public: + AutoSurfaceLock(IDirect3DSurface9* aSurface, DWORD aFlags = 0) { + PodZero(&mRect); + + HRESULT hr = aSurface->LockRect(&mRect, nullptr, aFlags); + if (FAILED(hr)) { + gfxCriticalNote << "Failed to lock surface rect " << hexa(hr); + return; + } + mSurface = aSurface; + } + ~AutoSurfaceLock() { + if (mSurface) { + mSurface->UnlockRect(); + } + } + + bool Okay() const { + return !!mSurface; + } + int Pitch() const { + MOZ_ASSERT(Okay()); + return mRect.Pitch; + } + uint8_t* Bits() const { + MOZ_ASSERT(Okay()); + return reinterpret_cast(mRect.pBits); + } + + private: + RefPtr mSurface; + D3DLOCKED_RECT mRect; +}; + +void +CompositorD3D9::FinishMixBlend(const gfx::IntRect& aBackdropRect, + const gfx::Rect& aBackdropDest, + const gfx::Matrix4x4& aBackdropTransform, + RefPtr aBackdrop, + gfx::CompositionOp aBlendMode) +{ + HRESULT hr; + + RefPtr source = + CreateTexture(aBackdropRect, mCurrentRT, aBackdropRect.TopLeft()); + if (!source) { + return; + } + + // Slow path - do everything in software. Unfortunately this requires + // a lot of copying, since we have to readback the source and backdrop, + // then upload the blended result, then blit it back. + + IDirect3DDevice9* d3d9Device = device(); + + // Query geometry/format of the two surfaces. + D3DSURFACE_DESC backdropDesc, sourceDesc; + if (FAILED(aBackdrop->GetLevelDesc(0, &backdropDesc)) || + FAILED(source->GetLevelDesc(0, &sourceDesc))) + { + gfxCriticalNote << "Failed to query mix-blend texture descriptor"; + return; + } + + MOZ_ASSERT(backdropDesc.Format == D3DFMT_A8R8G8B8); + MOZ_ASSERT(sourceDesc.Format == D3DFMT_A8R8G8B8); + + // Acquire a temporary data surface for the backdrop texture. + RefPtr backdropSurface = GetSurfaceOfTexture(aBackdrop); + if (!backdropSurface) { + return; + } + RefPtr tmpBackdrop = + CreateDataSurfaceForTexture(d3d9Device, backdropSurface, backdropDesc); + if (!tmpBackdrop) { + return; + } + + // New scope for locks and temporary surfaces. + { + // Acquire a temporary data surface for the source texture. + RefPtr sourceSurface = GetSurfaceOfTexture(source); + if (!sourceSurface) { + return; + } + RefPtr tmpSource = + CreateDataSurfaceForTexture(d3d9Device, sourceSurface, sourceDesc); + if (!tmpSource) { + return; + } + + // Perform the readback and blend in software. + AutoSurfaceLock backdropLock(tmpBackdrop); + AutoSurfaceLock sourceLock(tmpSource, D3DLOCK_READONLY); + if (!backdropLock.Okay() || !sourceLock.Okay()) { + return; + } + + RefPtr source = Factory::CreateWrappingDataSourceSurface( + sourceLock.Bits(), sourceLock.Pitch(), + gfx::IntSize(sourceDesc.Width, sourceDesc.Height), + SurfaceFormat::B8G8R8A8); + + RefPtr dest = Factory::CreateDrawTargetForData( + BackendType::CAIRO, + backdropLock.Bits(), + gfx::IntSize(backdropDesc.Width, backdropDesc.Height), + backdropLock.Pitch(), + SurfaceFormat::B8G8R8A8); + + // The backdrop rect is rounded out - account for any difference between + // it and the actual destination. + gfx::Rect destRect( + aBackdropDest.x - aBackdropRect.x, + aBackdropDest.y - aBackdropRect.y, + aBackdropDest.width, + aBackdropDest.height); + + dest->DrawSurface( + source, destRect, destRect, + gfx::DrawSurfaceOptions(), + gfx::DrawOptions(1.0f, aBlendMode)); + } + + // Upload the new blended surface to the backdrop texture. + d3d9Device->UpdateSurface(tmpBackdrop, nullptr, backdropSurface, nullptr); + + // Finally, drop in the new backdrop. We don't need to do another + // DrawPrimitive() since the software blend will have included the + // final OP_OVER step for us. + RECT destRect = { + aBackdropRect.x, aBackdropRect.y, + aBackdropRect.XMost(), aBackdropRect.YMost() + }; + hr = d3d9Device->StretchRect(backdropSurface, + nullptr, + mCurrentRT->GetD3D9Surface(), + &destRect, + D3DTEXF_NONE); + if (FAILED(hr)) { + gfxCriticalNote << "StretcRect with mix-blend failed " << hexa(hr); + } +} + } } diff --git a/gfx/layers/d3d9/CompositorD3D9.h b/gfx/layers/d3d9/CompositorD3D9.h index c23acdeae36..5ea6f1b6fd7 100644 --- a/gfx/layers/d3d9/CompositorD3D9.h +++ b/gfx/layers/d3d9/CompositorD3D9.h @@ -137,6 +137,20 @@ private: */ bool EnsureSwapChain(); + already_AddRefed + CreateTexture(const gfx::IntRect& aRect, + const CompositingRenderTarget* aSource, + const gfx::IntPoint& aSourcePoint); + + /** + * Complete a mix-blend step at the end of DrawQuad(). + */ + void FinishMixBlend(const gfx::IntRect& aBackdropRect, + const gfx::Rect& aBackdropDest, + const gfx::Matrix4x4& aBackdropTransform, + RefPtr aBackdrop, + gfx::CompositionOp aBlendMode); + /** * DeviceManagerD3D9 keeps a count of the number of times its device is * reset or recreated. We keep a parallel count (mDeviceResetCount). It diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 6c2adf9bb61..04c47b39746 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -1124,16 +1124,9 @@ CompositorOGL::DrawQuad(const Rect& aRect, } if (BlendOpIsMixBlendMode(blendMode)) { - gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform); - mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO()); - - // Create a transform from adjusted clip space to render target space, - // translate it for the backdrop rect, then transform it into the backdrop's - // uv-space. gfx::Matrix4x4 transform; - transform.PostScale(mRenderBounds.width, mRenderBounds.height, 1.0); - transform.PostTranslate(-rect.x, -rect.y, 0.0); - transform.PostScale(1 / float(rect.width), 1 / float(rect.height), 1.0); + gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, &transform); + mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO()); program->SetBackdropTransform(transform); } diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index 47ad7e021b0..728a1b74eb8 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -656,6 +656,8 @@ function BuildConditionSandbox(aURL) { gWindowUtils.layerManagerType != "Basic"; sandbox.d3d11 = gWindowUtils.layerManagerType == "Direct3D 11"; + sandbox.d3d9 = + gWindowUtils.layerManagerType == "Direct3D 9"; sandbox.layersOpenGL = gWindowUtils.layerManagerType == "OpenGL"; sandbox.layersOMTC = From bb23ab9245805bc884a5abc7d47804bec4dbbb2d Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 1 Feb 2016 16:45:09 -0800 Subject: [PATCH 76/91] Bug 1241575 - Use transform property syntax to parse WebKitCSSMatrix transform list. r=heycam --- dom/base/WebKitCSSMatrix.cpp | 53 +++++- .../general/test_WebKitCSSMatrix.html | 15 ++ layout/base/ActiveLayerTracker.cpp | 4 +- layout/base/nsDisplayList.cpp | 8 +- layout/style/StyleAnimationValue.cpp | 4 +- layout/style/nsCSSParser.cpp | 177 ++++++++++++++---- layout/style/nsCSSParser.h | 9 + layout/style/nsCSSProps.h | 3 + layout/style/nsCSSValue.h | 4 +- layout/style/nsComputedDOMStyle.cpp | 4 +- layout/style/nsStyleTransformMatrix.cpp | 34 +++- layout/style/nsStyleTransformMatrix.h | 10 +- 12 files changed, 270 insertions(+), 55 deletions(-) diff --git a/dom/base/WebKitCSSMatrix.cpp b/dom/base/WebKitCSSMatrix.cpp index 3bdc3448fd1..b5baf25a0a9 100644 --- a/dom/base/WebKitCSSMatrix.cpp +++ b/dom/base/WebKitCSSMatrix.cpp @@ -8,6 +8,8 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/WebKitCSSMatrixBinding.h" +#include "nsCSSParser.h" +#include "nsStyleTransformMatrix.h" namespace mozilla { namespace dom { @@ -56,11 +58,58 @@ WebKitCSSMatrix* WebKitCSSMatrix::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) { - DOMMatrix::SetMatrixValue(aTransformList, aRv); - if (NS_WARN_IF(aRv.Failed())) { + // An empty string is a no-op. + if (aTransformList.IsEmpty()) { + return this; + } + + nsCSSValue value; + nsCSSParser parser; + bool parseSuccess = parser.ParseTransformProperty(aTransformList, + true, + value); + if (!parseSuccess) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } + // A value of "none" results in a 2D identity matrix. + if (value.GetUnit() == eCSSUnit_None) { + mMatrix3D = nullptr; + mMatrix2D = new gfx::Matrix(); + return this; + } + + // A value other than a transform-list is a syntax error. + if (value.GetUnit() != eCSSUnit_SharedList) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + + RuleNodeCacheConditions dummy; + nsStyleTransformMatrix::TransformReferenceBox dummyBox; + bool contains3dTransform = false; + gfx::Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms( + value.GetSharedListValue()->mHead, + nullptr, nullptr, dummy, dummyBox, + nsPresContext::AppUnitsPerCSSPixel(), + &contains3dTransform); + + if (!contains3dTransform) { + mMatrix3D = nullptr; + mMatrix2D = new gfx::Matrix(); + + SetA(transform._11); + SetB(transform._12); + SetC(transform._21); + SetD(transform._22); + SetE(transform._41); + SetF(transform._42); + } else { + mMatrix3D = new gfx::Matrix4x4(transform); + mMatrix2D = nullptr; + } + return this; } diff --git a/dom/tests/mochitest/general/test_WebKitCSSMatrix.html b/dom/tests/mochitest/general/test_WebKitCSSMatrix.html index cf118d4e67d..ca6dac4655c 100644 --- a/dom/tests/mochitest/general/test_WebKitCSSMatrix.html +++ b/dom/tests/mochitest/general/test_WebKitCSSMatrix.html @@ -308,4 +308,19 @@ test(function() { var m = new WebKitCSSMatrix("matrix(1,0,0,0,0,0)"); assert_throws("NotSupportedError", function() { m.inverse(); }, "Inverting an invertible matrix should throw.") }, "Test that inverting an invertible matrix throws."); + +test(function() { + var m1 = new WebKitCSSMatrix("translate(10px, 10px)"); + var m2 = new DOMMatrix(); + m2.translateSelf(10, 10); + assert_true(RoughCompareMatrix(m1, m2), "translate in constructor should result in translated matrix"); + + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("translate(10em, 10em)"); }, "Transform function may not contain relative units.") + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("translate(10%, 10%)"); }, "Transform function may not contain percentage.") +}, "Test constructor with translate"); + +test(function() { + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("initial"); }, "initial is not a valid constructor argument.") + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("inherit"); }, "inherit is not a valid constructor arugment.") +}, "Test invalid constructor arguments."); diff --git a/layout/base/ActiveLayerTracker.cpp b/layout/base/ActiveLayerTracker.cpp index 95e8695dc4e..cd071b739a6 100644 --- a/layout/base/ActiveLayerTracker.cpp +++ b/layout/base/ActiveLayerTracker.cpp @@ -262,13 +262,15 @@ IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, LayerActivity* aActivity) // Compute the new scale due to the CSS transform property. nsPresContext* presContext = aFrame->PresContext(); RuleNodeCacheConditions dummy; + bool dummyBool; nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame); Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead, aFrame->StyleContext(), presContext, dummy, refBox, - presContext->AppUnitsPerCSSPixel()); + presContext->AppUnitsPerCSSPixel(), + &dummyBool); Matrix transform2D; if (!transform.Is2D(&transform2D)) { // We don't attempt to handle 3D transforms; just assume the scale changed. diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index e56b70c8e7e..d6f99f31a06 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -326,12 +326,14 @@ static void AddTransformFunctions(nsCSSValueList* aList, } case eCSSKeyword_interpolatematrix: { + bool dummy; Matrix4x4 matrix; nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array, aContext, aPresContext, conditions, - aRefBox); + aRefBox, + &dummy); aFunctions.AppendElement(TransformMatrix(matrix)); break; } @@ -5449,6 +5451,7 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp /* Get the matrix, then change its basis to factor in the origin. */ RuleNodeCacheConditions dummy; + bool dummyBool; Matrix4x4 result; // Call IsSVGTransformed() regardless of the value of // disp->mSpecifiedTransform, since we still need any transformFromSVGParent. @@ -5462,7 +5465,8 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead, frame ? frame->StyleContext() : nullptr, frame ? frame->PresContext() : nullptr, - dummy, refBox, aAppUnitsPerPixel); + dummy, refBox, aAppUnitsPerPixel, + &dummyBool); } else if (hasSVGTransforms) { // Correct the translation components for zoom: float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() / diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index c798a7f07c1..553941a9b91 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -3656,12 +3656,14 @@ StyleAnimationValue::GetScaleValue(const nsIFrame* aForFrame) const MOZ_ASSERT(list->mHead); RuleNodeCacheConditions dontCare; + bool dontCareBool; nsStyleTransformMatrix::TransformReferenceBox refBox(aForFrame); Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms( list->mHead, aForFrame->StyleContext(), aForFrame->PresContext(), dontCare, refBox, - aForFrame->PresContext()->AppUnitsPerDevPixel()); + aForFrame->PresContext()->AppUnitsPerDevPixel(), + &dontCareBool); Matrix transform2d; bool canDraw2D = transform.CanDraw2D(&transform2d); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 41797272ec0..d5fc8f65add 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -186,6 +186,10 @@ public: nsIPrincipal* aSheetPrincipal, nsCSSValue& aValue); + bool ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aResult); + void ParseMediaList(const nsSubstring& aBuffer, nsIURI* aURL, // for error reporting uint32_t aLineNumber, // for error reporting @@ -910,13 +914,13 @@ protected: bool ParseBorderStyle(); bool ParseBorderWidth(); - bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask); + bool ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask); bool ParseCalcAdditiveExpression(nsCSSValue& aValue, - int32_t& aVariantMask); + uint32_t& aVariantMask); bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue, - int32_t& aVariantMask, - bool *aHadFinalWS); - bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask); + uint32_t& aVariantMask, + bool *aHadFinalWS); + bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask); bool RequireWhitespace(); // For "flex" shorthand property, defined in CSS Flexbox spec @@ -1028,7 +1032,7 @@ protected: bool ParseListStyleType(nsCSSValue& aValue); bool ParseMargin(); bool ParseClipPath(); - bool ParseTransform(bool aIsPrefixed); + bool ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues = false); bool ParseObjectPosition(); bool ParseOutline(); bool ParseOverflow(); @@ -1153,7 +1157,7 @@ protected: // Variant parsing methods CSSParseResult ParseVariant(nsCSSValue& aValue, - int32_t aVariantMask, + uint32_t aVariantMask, const KTableEntry aKeywordTable[]); CSSParseResult ParseVariantWithRestrictions(nsCSSValue& aValue, int32_t aVariantMask, @@ -1249,7 +1253,7 @@ protected: bool ParseAttr(nsCSSValue& aValue); bool ParseSymbols(nsCSSValue& aValue); bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL); - bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask, + bool TranslateDimension(nsCSSValue& aValue, uint32_t aVariantMask, float aNumber, const nsString& aUnit); bool ParseImageOrientation(nsCSSValue& aAngle); bool ParseImageRect(nsCSSValue& aImage); @@ -1302,12 +1306,13 @@ protected: bool ParseInsetFunction(nsCSSValue& aValue); /* Functions for transform Parsing */ - bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue); - bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[], - int32_t aVariantMaskAll, uint16_t aMinElems, + bool ParseSingleTransform(bool aIsPrefixed, bool aDisallowRelativeValues, + nsCSSValue& aValue); + bool ParseFunction(nsCSSKeyword aFunction, const uint32_t aAllowedTypes[], + uint32_t aVariantMaskAll, uint16_t aMinElems, uint16_t aMaxElems, nsCSSValue &aValue); - bool ParseFunctionInternals(const int32_t aVariantMask[], - int32_t aVariantMaskAll, + bool ParseFunctionInternals(const uint32_t aVariantMask[], + uint32_t aVariantMaskAll, uint16_t aMinElems, uint16_t aMaxElems, InfallibleTArray& aOutput); @@ -1864,6 +1869,48 @@ CSSParserImpl::ParseLonghandProperty(const nsCSSProperty aPropID, } } +bool +CSSParserImpl::ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aValue) +{ + RefPtr declaration = new Declaration(); + declaration->InitializeEmpty(); + + mData.AssertInitialState(); + mTempData.AssertInitialState(); + + nsCSSScanner scanner(aPropValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr); + InitScanner(scanner, reporter, nullptr, nullptr, nullptr); + + bool parsedOK = ParseTransform(false, aDisallowRelativeValues); + // We should now be at EOF + if (parsedOK && GetToken(true)) { + parsedOK = false; + } + + bool changed = false; + if (parsedOK) { + declaration->ExpandTo(&mData); + changed = mData.TransferFromBlock(mTempData, eCSSProperty_transform, + PropertyEnabledState(), false, + true, false, declaration, + GetDocument()); + declaration->CompressFrom(&mData); + } + + if (changed) { + aValue = *declaration->GetNormalBlock()->ValueFor(eCSSProperty_transform); + } else { + aValue.Reset(); + } + + ReleaseScanner(); + + return parsedOK; +} + void CSSParserImpl::ParseProperty(const nsCSSProperty aPropID, const nsAString& aPropValue, @@ -7348,7 +7395,7 @@ const UnitInfo UnitData[] = { bool CSSParserImpl::TranslateDimension(nsCSSValue& aValue, - int32_t aVariantMask, + uint32_t aVariantMask, float aNumber, const nsString& aUnit) { @@ -7379,6 +7426,11 @@ CSSParserImpl::TranslateDimension(nsCSSValue& aValue, // inside an @page declaration. Fail. return false; } + + if ((VARIANT_ABSOLUTE_DIMENSION & aVariantMask) != 0 && + !nsCSSValue::IsPixelLengthUnit(units)) { + return false; + } } else { // Must be a zero number... NS_ASSERTION(0 == aNumber, "numbers without units must be 0"); @@ -7524,7 +7576,7 @@ CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, // Assigns to aValue iff it returns CSSParseResult::Ok. CSSParseResult CSSParserImpl::ParseVariant(nsCSSValue& aValue, - int32_t aVariantMask, + uint32_t aVariantMask, const KTableEntry aKeywordTable[]) { NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) || @@ -12899,7 +12951,7 @@ CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty) // Parse the top level of a calc() expression. bool -CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask) +CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask) { // Parsing calc expressions requires, in a number of cases, looking // for a token that is *either* a value of the property or a number. @@ -12947,7 +12999,7 @@ CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask) // data structure. bool CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue, - int32_t& aVariantMask) + uint32_t& aVariantMask) { MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); nsCSSValue *storage = &aValue; @@ -13007,7 +13059,7 @@ struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps, // aHadFinalWS parameter. bool CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue, - int32_t& aVariantMask, + uint32_t& aVariantMask, bool *aHadFinalWS) { MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); @@ -13016,7 +13068,7 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue, nsCSSValue *storage = &aValue; for (;;) { - int32_t variantMask; + uint32_t variantMask; if (afterDivision || gotValue) { variantMask = VARIANT_NUMBER; } else { @@ -13104,7 +13156,7 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue, // aVariantMask*** to reflect which one it has parsed by either // removing VARIANT_NUMBER or removing all other bits. bool -CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask) +CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask) { MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); if (!GetToken(true)) @@ -15027,8 +15079,8 @@ CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue) * ParseFunction. */ bool -CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], - int32_t aVariantMaskAll, +CSSParserImpl::ParseFunctionInternals(const uint32_t aVariantMask[], + uint32_t aVariantMaskAll, uint16_t aMinElems, uint16_t aMaxElems, InfallibleTArray &aOutput) @@ -15039,7 +15091,7 @@ CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], for (uint16_t index = 0; index < aMaxElems; ++index) { nsCSSValue newValue; - int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index]; + uint32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index]; if (ParseVariant(newValue, m, nullptr) != CSSParseResult::Ok) { break; } @@ -15087,8 +15139,8 @@ CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], */ bool CSSParserImpl::ParseFunction(nsCSSKeyword aFunction, - const int32_t aAllowedTypes[], - int32_t aAllowedTypesAll, + const uint32_t aAllowedTypes[], + uint32_t aAllowedTypesAll, uint16_t aMinElems, uint16_t aMaxElems, nsCSSValue &aValue) { @@ -15139,6 +15191,10 @@ CSSParserImpl::ParseFunction(nsCSSKeyword aFunction, * returns an error. * * @param aToken The token identifying the function. + * @param aIsPrefixed If true, parse matrices using the matrix syntax + * for -moz-transform. + * @param aDisallowRelativeValues If true, only allow variants that are + * numbers or have non-relative dimensions. * @param aMinElems [out] The minimum number of elements to read. * @param aMaxElems [out] The maximum number of elements to read * @param aVariantMask [out] The variant mask to use during parsing @@ -15146,9 +15202,10 @@ CSSParserImpl::ParseFunction(nsCSSKeyword aFunction, */ static bool GetFunctionParseInformation(nsCSSKeyword aToken, bool aIsPrefixed, + bool aDisallowRelativeValues, uint16_t &aMinElems, uint16_t &aMaxElems, - const int32_t *& aVariantMask) + const uint32_t *& aVariantMask) { /* These types represent the common variant masks that will be used to * parse out the individual functions. The order in the enumeration @@ -15156,12 +15213,16 @@ static bool GetFunctionParseInformation(nsCSSKeyword aToken, */ enum { eLengthPercentCalc, eLengthCalc, + eAbsoluteLengthCalc, eTwoLengthPercentCalcs, + eTwoAbsoluteLengthCalcs, eTwoLengthPercentCalcsOneLengthCalc, + eThreeAbsoluteLengthCalc, eAngle, eTwoAngles, eNumber, ePositiveLength, + ePositiveAbsoluteLength, eTwoNumbers, eThreeNumbers, eThreeNumbersOneAngle, @@ -15171,15 +15232,19 @@ static bool GetFunctionParseInformation(nsCSSKeyword aToken, eMatrix3dPrefixed, eNumVariantMasks }; static const int32_t kMaxElemsPerFunction = 16; - static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = { + static const uint32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = { {VARIANT_LPCALC}, - {VARIANT_LENGTH|VARIANT_CALC}, + {VARIANT_LCALC}, + {VARIANT_LB}, {VARIANT_LPCALC, VARIANT_LPCALC}, - {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC}, + {VARIANT_LBCALC, VARIANT_LBCALC}, + {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LCALC}, + {VARIANT_LBCALC, VARIANT_LBCALC, VARIANT_LBCALC}, {VARIANT_ANGLE_OR_ZERO}, {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO}, {VARIANT_NUMBER}, {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION}, + {VARIANT_LB|VARIANT_POSITIVE_DIMENSION}, {VARIANT_NUMBER, VARIANT_NUMBER}, {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}, {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO}, @@ -15195,10 +15260,31 @@ static bool GetFunctionParseInformation(nsCSSKeyword aToken, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}}; + // Map from a mask to a congruent mask that excludes relative variants. + static const int32_t kNonRelativeVariantMap[eNumVariantMasks] = { + eAbsoluteLengthCalc, + eAbsoluteLengthCalc, + eAbsoluteLengthCalc, + eTwoAbsoluteLengthCalcs, + eTwoAbsoluteLengthCalcs, + eThreeAbsoluteLengthCalc, + eThreeAbsoluteLengthCalc, + eAngle, + eTwoAngles, + eNumber, + ePositiveAbsoluteLength, + ePositiveAbsoluteLength, + eTwoNumbers, + eThreeNumbers, + eThreeNumbersOneAngle, + eMatrix, + eMatrix, + eMatrix3d, + eMatrix3d }; #ifdef DEBUG static const uint8_t kVariantMaskLengths[eNumVariantMasks] = - {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16}; + {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 6, 16, 16}; #endif int32_t variantIndex = eNumVariantMasks; @@ -15304,6 +15390,10 @@ static bool GetFunctionParseInformation(nsCSSKeyword aToken, return false; } + if (aDisallowRelativeValues) { + variantIndex = kNonRelativeVariantMap[variantIndex]; + } + NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!"); NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!"); NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!"); @@ -15376,7 +15466,9 @@ bool CSSParserImpl::ParseWillChange() * error if something goes wrong. */ bool -CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue) +CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, + bool aDisallowRelativeValues, + nsCSSValue& aValue) { if (!GetToken(true)) return false; @@ -15386,12 +15478,14 @@ CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue) return false; } - const int32_t* variantMask; + const uint32_t* variantMask; uint16_t minElems, maxElems; nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); if (!GetFunctionParseInformation(keyword, aIsPrefixed, - minElems, maxElems, variantMask)) + aDisallowRelativeValues, + minElems, maxElems, + variantMask)) return false; return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue); @@ -15400,7 +15494,8 @@ CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue) /* Parses a transform property list by continuously reading in properties * and constructing a matrix from it. */ -bool CSSParserImpl::ParseTransform(bool aIsPrefixed) +bool +CSSParserImpl::ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues) { nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' must be alone @@ -15411,7 +15506,8 @@ bool CSSParserImpl::ParseTransform(bool aIsPrefixed) list->mHead = new nsCSSValueList; nsCSSValueList* cur = list->mHead; for (;;) { - if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) { + if (!ParseSingleTransform(aIsPrefixed, aDisallowRelativeValues, + cur->mValue)) { return false; } if (CheckEndProperty()) { @@ -15785,7 +15881,7 @@ CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue) } // Set up the parsing rules based on the filter function. - int32_t variantMask = VARIANT_PN; + uint32_t variantMask = VARIANT_PN; bool rejectNegativeArgument = true; bool clampArgumentToOne = false; switch (functionName) { @@ -17191,6 +17287,15 @@ nsCSSParser::ParseLonghandProperty(const nsCSSProperty aPropID, aSheetPrincipal, aResult); } +bool +nsCSSParser::ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aResult) +{ + return static_cast(mImpl)-> + ParseTransformProperty(aPropValue, aDisallowRelativeValues, aResult); +} + void nsCSSParser::ParseVariable(const nsAString& aVariableName, const nsAString& aPropValue, diff --git a/layout/style/nsCSSParser.h b/layout/style/nsCSSParser.h index c0adfd7e3ec..4ab3c8abedc 100644 --- a/layout/style/nsCSSParser.h +++ b/layout/style/nsCSSParser.h @@ -135,6 +135,15 @@ public: nsIPrincipal* aSheetPrincipal, nsCSSValue& aResult); + // Parse the value of a CSS transform property. Returns + // whether the value was successfully parsed. If + // aDisallowRelativeValues is true then this method will + // only successfully parse if all values are numbers or + // have non-relative dimensions. + bool ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aResult); + // The same as ParseProperty but for a variable. void ParseVariable(const nsAString& aVariableName, const nsAString& aPropValue, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index b331a81d548..767686bd384 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -55,6 +55,7 @@ #define VARIANT_NONNEGATIVE_DIMENSION 0x20000000 // Only lengths greater than or equal to 0.0 // Keyword used iff gfx.font_rendering.opentype_svg.enabled is true: #define VARIANT_OPENTYPE_SVG_KEYWORD 0x40000000 +#define VARIANT_ABSOLUTE_DIMENSION 0x80000000 // B Only lengths with absolute length unit // Variants that can consume more than one token #define VARIANT_MULTIPLE_TOKENS \ @@ -106,6 +107,8 @@ #define VARIANT_UK (VARIANT_URL | VARIANT_KEYWORD) #define VARIANT_UO (VARIANT_URL | VARIANT_NONE) #define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE) +#define VARIANT_LB (VARIANT_LENGTH | VARIANT_ABSOLUTE_DIMENSION) +#define VARIANT_LBCALC (VARIANT_LB | VARIANT_CALC) #define VARIANT_LCALC (VARIANT_LENGTH | VARIANT_CALC) #define VARIANT_LPCALC (VARIANT_LCALC | VARIANT_PERCENT) #define VARIANT_LNCALC (VARIANT_LCALC | VARIANT_NUMBER) diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index 1d912fa5be6..c7282a84402 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -467,8 +467,10 @@ public: /** * A "pixel" length unit is a some multiple of CSS pixels. */ + static bool IsPixelLengthUnit(nsCSSUnit aUnit) + { return eCSSUnit_Point <= aUnit && aUnit <= eCSSUnit_Pixel; } bool IsPixelLengthUnit() const - { return eCSSUnit_Point <= mUnit && mUnit <= eCSSUnit_Pixel; } + { return IsPixelLengthUnit(mUnit); } bool IsAngularUnit() const { return eCSSUnit_Degree <= mUnit && mUnit <= eCSSUnit_Turn; } bool IsFrequencyUnit() const diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 0dd0d51c466..b8680fe23e5 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1343,13 +1343,15 @@ nsComputedDOMStyle::DoGetTransform() nsSize(0, 0)); RuleNodeCacheConditions dummy; + bool dummyBool; gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead, mStyleContext, mStyleContext->PresContext(), dummy, refBox, - float(mozilla::AppUnitsPerCSSPixel())); + float(mozilla::AppUnitsPerCSSPixel()), + &dummyBool); return MatrixToCSSValue(matrix); } diff --git a/layout/style/nsStyleTransformMatrix.cpp b/layout/style/nsStyleTransformMatrix.cpp index d4a3916f372..d0ef7512bd2 100644 --- a/layout/style/nsStyleTransformMatrix.cpp +++ b/layout/style/nsStyleTransformMatrix.cpp @@ -279,7 +279,8 @@ ProcessInterpolateMatrix(Matrix4x4& aMatrix, nsStyleContext* aContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions, - TransformReferenceBox& aRefBox) + TransformReferenceBox& aRefBox, + bool* aContains3dTransform) { NS_PRECONDITION(aData->Count() == 4, "Invalid array!"); @@ -288,13 +289,15 @@ ProcessInterpolateMatrix(Matrix4x4& aMatrix, matrix1 = nsStyleTransformMatrix::ReadTransforms(aData->Item(1).GetListValue(), aContext, aPresContext, aConditions, - aRefBox, nsPresContext::AppUnitsPerCSSPixel()); + aRefBox, nsPresContext::AppUnitsPerCSSPixel(), + aContains3dTransform); } if (aData->Item(2).GetUnit() == eCSSUnit_List) { matrix2 = ReadTransforms(aData->Item(2).GetListValue(), aContext, aPresContext, aConditions, - aRefBox, nsPresContext::AppUnitsPerCSSPixel()); + aRefBox, nsPresContext::AppUnitsPerCSSPixel(), + aContains3dTransform); } double progress = aData->Item(3).GetPercentValue(); @@ -614,8 +617,10 @@ MatrixForTransformFunction(Matrix4x4& aMatrix, nsStyleContext* aContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions, - TransformReferenceBox& aRefBox) + TransformReferenceBox& aRefBox, + bool* aContains3dTransform) { + MOZ_ASSERT(aContains3dTransform); NS_PRECONDITION(aData, "Why did you want to get data from a null array?"); // It's OK if aContext and aPresContext are null if the caller already // knows that all length units have been converted to pixels (as @@ -633,6 +638,7 @@ MatrixForTransformFunction(Matrix4x4& aMatrix, aConditions, aRefBox); break; case eCSSKeyword_translatez: + *aContains3dTransform = true; ProcessTranslateZ(aMatrix, aData, aContext, aPresContext, aConditions); break; @@ -641,6 +647,7 @@ MatrixForTransformFunction(Matrix4x4& aMatrix, aConditions, aRefBox); break; case eCSSKeyword_translate3d: + *aContains3dTransform = true; ProcessTranslate3D(aMatrix, aData, aContext, aPresContext, aConditions, aRefBox); break; @@ -651,12 +658,14 @@ MatrixForTransformFunction(Matrix4x4& aMatrix, ProcessScaleY(aMatrix, aData); break; case eCSSKeyword_scalez: + *aContains3dTransform = true; ProcessScaleZ(aMatrix, aData); break; case eCSSKeyword_scale: ProcessScale(aMatrix, aData); break; case eCSSKeyword_scale3d: + *aContains3dTransform = true; ProcessScale3D(aMatrix, aData); break; case eCSSKeyword_skewx: @@ -669,16 +678,20 @@ MatrixForTransformFunction(Matrix4x4& aMatrix, ProcessSkew(aMatrix, aData); break; case eCSSKeyword_rotatex: + *aContains3dTransform = true; ProcessRotateX(aMatrix, aData); break; case eCSSKeyword_rotatey: + *aContains3dTransform = true; ProcessRotateY(aMatrix, aData); break; case eCSSKeyword_rotatez: + *aContains3dTransform = true; case eCSSKeyword_rotate: ProcessRotateZ(aMatrix, aData); break; case eCSSKeyword_rotate3d: + *aContains3dTransform = true; ProcessRotate3D(aMatrix, aData); break; case eCSSKeyword_matrix: @@ -686,14 +699,17 @@ MatrixForTransformFunction(Matrix4x4& aMatrix, aConditions, aRefBox); break; case eCSSKeyword_matrix3d: + *aContains3dTransform = true; ProcessMatrix3D(aMatrix, aData, aContext, aPresContext, aConditions, aRefBox); break; case eCSSKeyword_interpolatematrix: ProcessInterpolateMatrix(aMatrix, aData, aContext, aPresContext, - aConditions, aRefBox); + aConditions, aRefBox, + aContains3dTransform); break; case eCSSKeyword_perspective: + *aContains3dTransform = true; ProcessPerspective(aMatrix, aData, aContext, aPresContext, aConditions); break; @@ -719,7 +735,8 @@ ReadTransforms(const nsCSSValueList* aList, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions, TransformReferenceBox& aRefBox, - float aAppUnitsPerMatrixUnit) + float aAppUnitsPerMatrixUnit, + bool* aContains3dTransform) { Matrix4x4 result; @@ -737,13 +754,14 @@ ReadTransforms(const nsCSSValueList* aList, /* Read in a single transform matrix. */ MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext, - aPresContext, aConditions, aRefBox); + aPresContext, aConditions, aRefBox, + aContains3dTransform); } float scale = float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit; result.PreScale(1/scale, 1/scale, 1/scale); result.PostScale(scale, scale, scale); - + return result; } diff --git a/layout/style/nsStyleTransformMatrix.h b/layout/style/nsStyleTransformMatrix.h index 0c2580cfc1d..a03a0cfd505 100644 --- a/layout/style/nsStyleTransformMatrix.h +++ b/layout/style/nsStyleTransformMatrix.h @@ -140,7 +140,8 @@ namespace nsStyleTransformMatrix { nsStyleContext* aContext, nsPresContext* aPresContext, mozilla::RuleNodeCacheConditions& aConditions, - TransformReferenceBox& aBounds); + TransformReferenceBox& aBounds, + bool* aContains3dTransform); /** * Given an nsCSSValueList containing -moz-transform functions, @@ -153,6 +154,9 @@ namespace nsStyleTransformMatrix { * result cannot be cached in the rule tree, otherwise untouched. * @param aBounds The frame's bounding rectangle. * @param aAppUnitsPerMatrixUnit The number of app units per device pixel. + * @param aContains3dTransform [out] Set to true if aList contains at least + * one 3d transform function (as defined in the CSS transforms + * specification), false otherwise. * * aContext and aPresContext may be null if all of the (non-percent) * length values in aData are already known to have been converted to @@ -163,8 +167,8 @@ namespace nsStyleTransformMatrix { nsPresContext* aPresContext, mozilla::RuleNodeCacheConditions& aConditions, TransformReferenceBox& aBounds, - float aAppUnitsPerMatrixUnit); - + float aAppUnitsPerMatrixUnit, + bool* aContains3dTransform); } // namespace nsStyleTransformMatrix #endif From 34609a08eb7a14475e237674d39fdfc4dc6485b8 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Mon, 1 Feb 2016 19:59:23 -0500 Subject: [PATCH 77/91] Bug 1244901 - When recording transform duration for potential checkerboarding time, do so on the compositor thread. r=botond --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 3 +- .../PotentialCheckerboardDurationTracker.cpp | 30 ++++++++++++------- .../PotentialCheckerboardDurationTracker.h | 11 ++----- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 97d12679b0a..51ae4c49be8 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -3149,6 +3149,7 @@ AsyncPanZoomController::ReportCheckerboard(const TimeStamp& aSampleTime) if (!mCheckerboardEvent && (recordTrace || forTelemetry)) { mCheckerboardEvent = MakeUnique(recordTrace); } + mPotentialCheckerboardTracker.InTransform(IsTransformingState(mState)); if (magnitude) { mPotentialCheckerboardTracker.CheckerboardSeen(); } @@ -3634,7 +3635,6 @@ void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldSt if (RefPtr controller = GetGeckoContentController()) { if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) { - mPotentialCheckerboardTracker.TransformStarted(); controller->NotifyAPZStateChange( GetGuid(), APZStateChange::TransformBegin); #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) @@ -3645,7 +3645,6 @@ void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldSt } #endif } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) { - mPotentialCheckerboardTracker.TransformStopped(); controller->NotifyAPZStateChange( GetGuid(), APZStateChange::TransformEnd); #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) diff --git a/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp index 0f9a1d32d86..c83b9f45c24 100644 --- a/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp +++ b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.cpp @@ -40,21 +40,29 @@ PotentialCheckerboardDurationTracker::CheckerboardDone() } void -PotentialCheckerboardDurationTracker::TransformStarted() +PotentialCheckerboardDurationTracker::InTransform(bool aInTransform) { - MOZ_ASSERT(!mInTransform); - if (!Tracking()) { - mCurrentPeriodStart = TimeStamp::Now(); + if (aInTransform == mInTransform) { + // no-op + return; } - mInTransform = true; -} -void -PotentialCheckerboardDurationTracker::TransformStopped() -{ - MOZ_ASSERT(mInTransform); - mInTransform = false; if (!Tracking()) { + // Because !Tracking(), mInTransform must be false, and so aInTransform + // must be true (or we would have early-exited this function already). + // Therefore, we are starting a potential checkerboard period. + mInTransform = aInTransform; + mCurrentPeriodStart = TimeStamp::Now(); + return; + } + + mInTransform = aInTransform; + + if (!Tracking()) { + // Tracking() must have been true at the start of this function, or we + // would have taken the other !Tracking branch above. If it's false now, + // it means we just stopped tracking, so we are ending a potential + // checkerboard period. mozilla::Telemetry::AccumulateTimeDelta( mozilla::Telemetry::CHECKERBOARD_POTENTIAL_DURATION, mCurrentPeriodStart); diff --git a/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h index b6bd3ea882b..6154003ad63 100644 --- a/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h +++ b/gfx/layers/apz/src/PotentialCheckerboardDurationTracker.h @@ -39,15 +39,10 @@ public: void CheckerboardDone(); /** - * This should be called when a transform is started. Calls to this must be - * interleaved with calls to TransformStopped(). + * This should be called at composition time, to indicate if the APZC is in + * a transforming state or not. */ - void TransformStarted(); - /** - * This should be called when a transform is stopped. Calls to this must be - * interleaved with calls to TransformStarted(). - */ - void TransformStopped(); + void InTransform(bool aInTransform); private: bool Tracking() const; From aebc5149eea2520dfcc764e9e5f1b7a83910f085 Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Fri, 29 Jan 2016 18:50:05 -0800 Subject: [PATCH 78/91] Bug 1244575 - add repack template config for mozharness beetmover, DONTBUILD NPOTB r=rail --- .../configs/beetmover/repacks.yml.tmpl | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 testing/mozharness/configs/beetmover/repacks.yml.tmpl diff --git a/testing/mozharness/configs/beetmover/repacks.yml.tmpl b/testing/mozharness/configs/beetmover/repacks.yml.tmpl new file mode 100644 index 00000000000..ff819df7a04 --- /dev/null +++ b/testing/mozharness/configs/beetmover/repacks.yml.tmpl @@ -0,0 +1,61 @@ +--- +metadata: + name: "Beet Mover Manifest" + description: "Maps artifact locations to s3 key names for the non en-US locales" + owner: "release@mozilla.com" + +mapping: +{% for locale in locales %} + # common deliverables + {{ locale }}: + complete_mar: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.complete.mar + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/update/{{ platform }}/{{ locale }}/firefox-{{ version }}.complete.mar +# Bug 1242782 - release promotion mozharness l10n tasks should upload checksum and sigs +# checksum: +# artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.checksums +# s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/firefox-{{ version }}.checksums +# checksum_sig: +# artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.checksums.asc +# s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/firefox-{{ version }}.checksums.asc + + {% if platform == "win32" %} + full_installer: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.installer.exe + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/Firefox Setup {{ version }}.exe + stub_installer: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.installer-stub.exe + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/Firefox Setup Stub {{ version }}.exe + package: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.zip + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/firefox-{{ version }}.zip + {% endif %} + + {% if platform == "win64" %} + full_installer: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.installer.exe + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/Firefox Setup {{ version }}.exe + package: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.zip + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/firefox-{{ version }}.zip + {% endif %} + + {% if platform == "linux-i686" %} + package: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.tar.bz2 + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/firefox-{{ version }}.tar.bz2 + {% endif %} + + {% if platform == "linux-x86_64" %} + package: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.tar.bz2 + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/firefox-{{ version }}.tar.bz2 + {% endif %} + + {% if platform == "mac" %} + package: + artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.dmg + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/{{ platform }}/{{ locale }}/Firefox {{ version }}.dmg + {% endif %} + +{% endfor %} From 1632f24fb6a1878d98257f74bec7483ae2ec4c6c Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Thu, 28 Jan 2016 17:38:02 -0500 Subject: [PATCH 79/91] bug 1244128 - stop exporting js friend api symbols from libxul r=glandium --- js/src/jstypes.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/js/src/jstypes.h b/js/src/jstypes.h index 1293aed267e..914674abe3a 100644 --- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -70,18 +70,16 @@ #if defined(STATIC_JS_API) # define JS_PUBLIC_API(t) t # define JS_PUBLIC_DATA(t) t +# define JS_FRIEND_API(t) t +# define JS_FRIEND_DATA(t) t #elif defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API) # define JS_PUBLIC_API(t) MOZ_EXPORT t # define JS_PUBLIC_DATA(t) MOZ_EXPORT t -#else -# define JS_PUBLIC_API(t) MOZ_IMPORT_API t -# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA t -#endif - -#if defined(STATIC_JS_API) || defined(EXPORT_JS_API) || defined(STATIC_EXPORTABLE_JS_API) # define JS_FRIEND_API(t) MOZ_EXPORT t # define JS_FRIEND_DATA(t) MOZ_EXPORT t #else +# define JS_PUBLIC_API(t) MOZ_IMPORT_API t +# define JS_PUBLIC_DATA(t) MOZ_IMPORT_DATA t # define JS_FRIEND_API(t) MOZ_IMPORT_API t # define JS_FRIEND_DATA(t) MOZ_IMPORT_DATA t #endif From 5d5adb69daae44ec4e4a4b296a25b3c4581be78e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 2 Feb 2016 09:33:14 +0900 Subject: [PATCH 80/91] Bug 1244941 - Don't fill install manifest with artifacts. r=nalexander Since bug 1239217, artifacts builds are using a hybrid build system that uses the fastermake rules to copy files to dist/bin, which means artifact files are not removed by the processing of the dist/bin install manifest. This means we don't need to add them to the recursivemake install manifest anymore. --- python/mozbuild/mozbuild/artifacts.py | 26 ++++++++--------------- python/mozbuild/mozbuild/mach_commands.py | 19 +---------------- 2 files changed, 10 insertions(+), 35 deletions(-) diff --git a/python/mozbuild/mozbuild/artifacts.py b/python/mozbuild/mozbuild/artifacts.py index 7c6db5c999c..7c66d9d1c4b 100644 --- a/python/mozbuild/mozbuild/artifacts.py +++ b/python/mozbuild/mozbuild/artifacts.py @@ -729,7 +729,7 @@ class Artifacts(object): return urls return None - def install_from_file(self, filename, distdir, install_callback=None): + def install_from_file(self, filename, distdir): self.log(logging.INFO, 'artifact', {'filename': filename}, 'Installing from {filename}') @@ -769,19 +769,17 @@ class Artifacts(object): perms = info.external_attr >> 16 # See http://stackoverflow.com/a/434689. perms |= stat.S_IWUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH # u+w, a+r. os.chmod(n, perms) - if install_callback: - install_callback(info.filename, file_existed, file_updated) return 0 - def install_from_url(self, url, distdir, install_callback=None): + def install_from_url(self, url, distdir): self.log(logging.INFO, 'artifact', {'url': url}, 'Installing from {url}') with self._artifact_cache as artifact_cache: # The with block handles persistence. filename = artifact_cache.fetch(url) - return self.install_from_file(filename, distdir, install_callback=install_callback) + return self.install_from_file(filename, distdir) - def install_from_hg(self, revset, distdir, install_callback=None): + def install_from_hg(self, revset, distdir): if not revset: revset = '.' rev_pushheads = self._find_pushheads(revset) @@ -797,7 +795,7 @@ class Artifacts(object): self._job, rev, trees) if urls: for url in urls: - if self.install_from_url(url, distdir, install_callback=install_callback): + if self.install_from_url(url, distdir): return 1 return 0 self.log(logging.ERROR, 'artifact', @@ -805,21 +803,15 @@ class Artifacts(object): 'No built artifacts for {revset} found.') return 1 - def install_from(self, source, distdir, install_callback=None): + def install_from(self, source, distdir): """Install artifacts from a ``source`` into the given ``distdir``. - - If ``callback`` is given, it is called once with arguments ``(path, - existed, updated)``, where ``path`` is the file path written relative - to ``distdir``; ``existed`` is a boolean indicating whether the file - existed; and ``updated`` is a boolean indicating whether the file was - updated. """ if source and os.path.isfile(source): - return self.install_from_file(source, distdir, install_callback=install_callback) + return self.install_from_file(source, distdir) elif source and urlparse.urlparse(source).scheme: - return self.install_from_url(source, distdir, install_callback=install_callback) + return self.install_from_url(source, distdir) else: - return self.install_from_hg(source, distdir, install_callback=install_callback) + return self.install_from_hg(source, distdir) def print_last(self): self.log(logging.INFO, 'artifact', diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 02a7122777e..6b7af752ab5 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -1503,24 +1503,7 @@ class PackageFrontend(MachCommandBase): job = self._compute_platform(job) artifacts = self._make_artifacts(tree=tree, job=job) - manifest_path = mozpath.join(self.topobjdir, '_build_manifests', 'install', 'dist_bin') - manifest = InstallManifest(manifest_path) - - def install_callback(path, file_existed, file_updated): - # Our paths are either under dist/bin or dist/plugins (for test - # plugins). dist/plugins. does not have an install manifest. - if not path.startswith('bin/'): - return - path = path[len('bin/'):] - if path not in manifest: - manifest.add_optional_exists(path) - - retcode = artifacts.install_from(source, self.distdir, install_callback=install_callback) - - if retcode == 0: - manifest.write(manifest_path) - - return retcode + return artifacts.install_from(source, self.distdir) @ArtifactSubCommand('artifact', 'last', 'Print the last pre-built artifact installed.') From 7a80ebb8f8cb57ee896a07c6df96cc922306f278 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Sun, 31 Jan 2016 00:01:17 -0800 Subject: [PATCH 81/91] Bug 1236979 part 1: Add event names & enums for webkit-prefixed transition & animation events. r=smaug --- dom/base/nsGkAtomList.h | 4 ++++ dom/events/EventNameList.h | 19 +++++++++++++++++++ widget/EventMessageList.h | 8 +++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 008825a2cc9..c7cf6c62b27 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -931,6 +931,10 @@ GK_ATOM(onupgradeneeded, "onupgradeneeded") GK_ATOM(onussdreceived, "onussdreceived") GK_ATOM(onversionchange, "onversionchange") GK_ATOM(onvoicechange, "onvoicechange") +GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd") +GK_ATOM(onwebkitAnimationIteration, "onwebkitAnimationIteration") +GK_ATOM(onwebkitAnimationStart, "onwebkitAnimationStart") +GK_ATOM(onwebkitTransitionEnd, "onwebkitTransitionEnd") GK_ATOM(onwheel, "onwheel") GK_ATOM(open, "open") GK_ATOM(optgroup, "optgroup") diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 1fb978f32ef..b3846b3ee3d 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -924,6 +924,7 @@ NON_IDL_EVENT(MozEdgeUICompleted, EventNameType_None, eSimpleGestureEventClass) +// CSS Transition & Animation events: NON_IDL_EVENT(transitionend, eTransitionEnd, EventNameType_None, @@ -941,6 +942,24 @@ NON_IDL_EVENT(animationiteration, EventNameType_None, eAnimationEventClass) +// Webkit-prefixed versions of Transition & Animation events, for web compat: +NON_IDL_EVENT(webkitTransitionEnd, + eWebkitTransitionEnd, + EventNameType_None, + eTransitionEventClass) +NON_IDL_EVENT(webkitAnimationEnd, + eWebkitAnimationEnd, + EventNameType_None, + eAnimationEventClass) +NON_IDL_EVENT(webkitAnimationIteration, + eWebkitAnimationIteration, + EventNameType_None, + eAnimationEventClass) +NON_IDL_EVENT(webkitAnimationStart, + eWebkitAnimationStart, + EventNameType_None, + eAnimationEventClass) + NON_IDL_EVENT(audioprocess, eAudioProcess, EventNameType_None, diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 38d4deb9a6f..4adadf6437d 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -312,12 +312,18 @@ NS_EVENT_MESSAGE(eGestureNotify) NS_EVENT_MESSAGE(eScrolledAreaChanged) +// CSS Transition & Animation events: NS_EVENT_MESSAGE(eTransitionEnd) - NS_EVENT_MESSAGE(eAnimationStart) NS_EVENT_MESSAGE(eAnimationEnd) NS_EVENT_MESSAGE(eAnimationIteration) +// Webkit-prefixed versions of Transition & Animation events, for web compat: +NS_EVENT_MESSAGE(eWebkitTransitionEnd) +NS_EVENT_MESSAGE(eWebkitAnimationStart) +NS_EVENT_MESSAGE(eWebkitAnimationEnd) +NS_EVENT_MESSAGE(eWebkitAnimationIteration) + NS_EVENT_MESSAGE(eSMILBeginEvent) NS_EVENT_MESSAGE(eSMILEndEvent) NS_EVENT_MESSAGE(eSMILRepeatEvent) From 52a8a877db9cc9f94a5a79150eee3894f6d4a35f Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 1 Feb 2016 19:05:11 -0800 Subject: [PATCH 82/91] Bug 1236979 part 2: Create an RAII helper-class to temporarily override an Event's mMessage (i.e. its DOM-exposed 'type') r=smaug --- dom/events/Event.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/dom/events/Event.h b/dom/events/Event.h index 2dd50c50961..85fb0642d09 100644 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -30,6 +30,7 @@ namespace mozilla { namespace dom { class EventTarget; +class EventMessageAutoOverride; class WantsPopupControlCheck; #define GENERATED_EVENT(EventClass_) class EventClass_; #include "mozilla/dom/GeneratedEventList.h" @@ -239,6 +240,7 @@ protected: void SetEventType(const nsAString& aEventTypeArg); already_AddRefed GetTargetFromFrame(); + friend class EventMessageAutoOverride; friend class WantsPopupControlCheck; void SetWantsPopupControlCheck(bool aCheck) { @@ -268,6 +270,48 @@ protected: bool mWantsPopupControlCheck; }; +/** + * RAII helper-class to override an event's message (i.e. its DOM-exposed + * type), for as long as the object is alive. Restores the original + * EventMessage when destructed. + * + * Notable requirements: + * - The original & overriding messages must be known (not eUnidentifiedEvent). + * - The original & overriding messages must be different. + * - The passed-in nsIDOMEvent must outlive this RAII helper. + */ +class MOZ_RAII EventMessageAutoOverride +{ +public: + explicit EventMessageAutoOverride(nsIDOMEvent* aEvent, + EventMessage aOverridingMessage) + : mEvent(aEvent->InternalDOMEvent()), + mOrigMessage(mEvent->mEvent->mMessage) + { + MOZ_ASSERT(aOverridingMessage != mOrigMessage, + "Don't use this class if you're not actually overriding"); + MOZ_ASSERT(aOverridingMessage != eUnidentifiedEvent, + "Only use this class with a valid overriding EventMessage"); + MOZ_ASSERT(mOrigMessage != eUnidentifiedEvent && + mEvent->mEvent->typeString.IsEmpty(), + "Only use this class on events whose overridden type is " + "known (so we can restore it properly)"); + + mEvent->mEvent->mMessage = aOverridingMessage; + } + + ~EventMessageAutoOverride() + { + mEvent->mEvent->mMessage = mOrigMessage; + } + +protected: + // Non-owning ref, which should be safe since we're a stack-allocated object + // with limited lifetime. Whoever creates us should keep mEvent alive. + Event* const MOZ_NON_OWNING_REF mEvent; + const EventMessage mOrigMessage; +}; + class MOZ_STACK_CLASS WantsPopupControlCheck { public: From d3dfd167cfcb2ebbecf1d3b55a730d7137fb61e9 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 1 Feb 2016 19:05:12 -0800 Subject: [PATCH 83/91] Bug 1236979 part 3: If there are no listeners for a transition or animation event, check listeners again using a webkit-prefixed event name. r=smaug --- dom/events/EventListenerManager.cpp | 192 +++++++++++++++++++--------- dom/events/EventListenerManager.h | 4 +- 2 files changed, 138 insertions(+), 58 deletions(-) diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 3ddb7dd9e48..ecc3d95eb8d 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -19,7 +19,9 @@ #include "mozilla/HalSensor.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/JSEventHandler.h" +#include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/Preferences.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" @@ -95,6 +97,46 @@ MutationBitForEventType(EventMessage aEventType) uint32_t EventListenerManager::sMainThreadCreatedCount = 0; +static bool +IsWebkitPrefixSupportEnabled() +{ + static bool sIsWebkitPrefixSupportEnabled; + static bool sIsPrefCached = false; + + if (!sIsPrefCached) { + sIsPrefCached = true; + Preferences::AddBoolVarCache(&sIsWebkitPrefixSupportEnabled, + "layout.css.prefixes.webkit"); + } + + return sIsWebkitPrefixSupportEnabled; +} + +// If the given EventMessage has a legacy version that we support, then this +// function returns that legacy version. Otherwise, this function simply +// returns the passed-in EventMessage. +EventMessage +GetLegacyEventMessage(EventMessage aEventMessage) +{ + if (IsWebkitPrefixSupportEnabled()) { + // webkit-prefixed legacy events: + if (aEventMessage == eTransitionEnd) { + return eWebkitTransitionEnd; + } + if (aEventMessage == eAnimationStart) { + return eWebkitAnimationStart; + } + if (aEventMessage == eAnimationEnd) { + return eWebkitAnimationEnd; + } + if (aEventMessage == eAnimationIteration) { + return eWebkitAnimationIteration; + } + } + + return aEventMessage; +} + EventListenerManagerBase::EventListenerManagerBase() : mNoListenerForEvent(eVoidEvent) , mMayHavePaintEventListener(false) @@ -603,9 +645,15 @@ EventListenerManager::RemoveEventListenerInternal( } bool -EventListenerManager::ListenerCanHandle(Listener* aListener, - WidgetEvent* aEvent) +EventListenerManager::ListenerCanHandle(const Listener* aListener, + const WidgetEvent* aEvent, + EventMessage aEventMessage) const + { + MOZ_ASSERT(aEventMessage == aEvent->mMessage || + aEventMessage == GetLegacyEventMessage(aEvent->mMessage), + "aEvent and aEventMessage should agree, modulo legacyness"); + // This is slightly different from EVENT_TYPE_EQUALS in that it returns // true even when aEvent->mMessage == eUnidentifiedEvent and // aListener=>mEventMessage != eUnidentifiedEvent as long as the atoms are @@ -620,7 +668,7 @@ EventListenerManager::ListenerCanHandle(Listener* aListener, return aListener->mTypeString.Equals(aEvent->typeString); } MOZ_ASSERT(mIsMainThreadELM); - return aListener->mEventMessage == aEvent->mMessage; + return aListener->mEventMessage == aEventMessage; } void @@ -1104,77 +1152,107 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, aEvent->mFlags.mDefaultPrevented = true; } - nsAutoTObserverArray::EndLimitedIterator iter(mListeners); Maybe popupStatePusher; if (mIsMainThreadELM) { popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent, *aDOMEvent)); } bool hasListener = false; - while (iter.HasMore()) { - if (aEvent->mFlags.mImmediatePropagationStopped) { - break; - } - Listener* listener = &iter.GetNext(); - // Check that the phase is same in event and event listener. - // Handle only trusted events, except when listener permits untrusted events. - if (ListenerCanHandle(listener, aEvent)) { - hasListener = true; - if (listener->IsListening(aEvent) && - (aEvent->mFlags.mIsTrusted || - listener->mFlags.mAllowUntrustedEvents)) { - if (!*aDOMEvent) { - // This is tiny bit slow, but happens only once per event. - nsCOMPtr et = - do_QueryInterface(aEvent->originalTarget); - RefPtr event = EventDispatcher::CreateEvent(et, aPresContext, + bool usingLegacyMessage = false; + EventMessage eventMessage = aEvent->mMessage; + + while (true) { + nsAutoTObserverArray::EndLimitedIterator iter(mListeners); + Maybe legacyAutoOverride; + while (iter.HasMore()) { + if (aEvent->mFlags.mImmediatePropagationStopped) { + break; + } + Listener* listener = &iter.GetNext(); + // Check that the phase is same in event and event listener. + // Handle only trusted events, except when listener permits untrusted events. + if (ListenerCanHandle(listener, aEvent, eventMessage)) { + hasListener = true; + if (listener->IsListening(aEvent) && + (aEvent->mFlags.mIsTrusted || + listener->mFlags.mAllowUntrustedEvents)) { + if (!*aDOMEvent) { + // This is tiny bit slow, but happens only once per event. + nsCOMPtr et = + do_QueryInterface(aEvent->originalTarget); + RefPtr event = EventDispatcher::CreateEvent(et, aPresContext, aEvent, EmptyString()); - event.forget(aDOMEvent); - } - if (*aDOMEvent) { - if (!aEvent->currentTarget) { - aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); - if (!aEvent->currentTarget) { - break; - } + event.forget(aDOMEvent); } - - // Maybe add a marker to the docshell's timeline, but only - // bother with all the logic if some docshell is recording. - nsDocShell* docShell; - RefPtr timelines = TimelineConsumers::Get(); - bool needsEndEventMarker = false; - - if (mIsMainThreadELM && - listener->mListenerType != Listener::eNativeListener) { - nsCOMPtr docShellComPtr = GetDocShellForTarget(); - if (docShellComPtr) { - docShell = static_cast(docShellComPtr.get()); - if (timelines && timelines->HasConsumer(docShell)) { - needsEndEventMarker = true; - nsAutoString typeStr; - (*aDOMEvent)->GetType(typeStr); - uint16_t phase; - (*aDOMEvent)->GetEventPhase(&phase); - timelines->AddMarkerForDocShell(docShell, Move( - MakeUnique( - typeStr, phase, MarkerTracingType::START))); + if (*aDOMEvent) { + if (!aEvent->currentTarget) { + aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); + if (!aEvent->currentTarget) { + break; } } - } + if (usingLegacyMessage && !legacyAutoOverride) { + // Override the aDOMEvent's event-message (its .type) until we + // finish traversing listeners (when legacyAutoOverride destructs) + legacyAutoOverride.emplace(*aDOMEvent, eventMessage); + } - if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) { - aEvent->mFlags.mExceptionHasBeenRisen = true; - } + // Maybe add a marker to the docshell's timeline, but only + // bother with all the logic if some docshell is recording. + nsDocShell* docShell; + RefPtr timelines = TimelineConsumers::Get(); + bool needsEndEventMarker = false; - if (needsEndEventMarker) { - timelines->AddMarkerForDocShell( - docShell, "DOMEvent", MarkerTracingType::END); + if (mIsMainThreadELM && + listener->mListenerType != Listener::eNativeListener) { + nsCOMPtr docShellComPtr = GetDocShellForTarget(); + if (docShellComPtr) { + docShell = static_cast(docShellComPtr.get()); + if (timelines && timelines->HasConsumer(docShell)) { + needsEndEventMarker = true; + nsAutoString typeStr; + (*aDOMEvent)->GetType(typeStr); + uint16_t phase; + (*aDOMEvent)->GetEventPhase(&phase); + timelines->AddMarkerForDocShell(docShell, Move( + MakeUnique( + typeStr, phase, MarkerTracingType::START))); + } + } + } + + if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) { + aEvent->mFlags.mExceptionHasBeenRisen = true; + } + + if (needsEndEventMarker) { + timelines->AddMarkerForDocShell( + docShell, "DOMEvent", MarkerTracingType::END); + } } } } } + + // If we didn't find any matching listeners, and our event has a legacy + // version, we'll now switch to looking for that legacy version and we'll + // recheck our listeners. + if (hasListener || usingLegacyMessage) { + // (No need to recheck listeners, because we already found a match, or we + // already rechecked them.) + break; + } + EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage); + if (legacyEventMessage == eventMessage) { + break; // There's no legacy version of our event; no need to recheck. + } + MOZ_ASSERT(GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage, + "Legacy event messages should not themselves have legacy versions"); + + // Recheck our listeners, using the legacy event message we just looked up: + eventMessage = legacyEventMessage; + usingLegacyMessage = true; } aEvent->currentTarget = nullptr; diff --git a/dom/events/EventListenerManager.h b/dom/events/EventListenerManager.h index 85660e77b37..fbe5a737e5e 100644 --- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -571,7 +571,9 @@ protected: nsPIDOMWindowInner* GetInnerWindowForTarget(); already_AddRefed GetTargetAsInnerWindow() const; - bool ListenerCanHandle(Listener* aListener, WidgetEvent* aEvent); + bool ListenerCanHandle(const Listener* aListener, + const WidgetEvent* aEvent, + EventMessage aEventMessage) const; // BE AWARE, a lot of instances of EventListenerManager will be created. // Therefor, we need to keep this class compact. When you add integer From 8c03ed4b8aa59c975d8a1e94e38f7bcec012da92 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 1 Feb 2016 19:05:12 -0800 Subject: [PATCH 84/91] Bug 1236979 part 4: Add test for firing of prefixed legacy event-types. r=smaug --- dom/events/test/mochitest.ini | 1 + dom/events/test/test_legacy_event.html | 256 +++++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 dom/events/test/test_legacy_event.html diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 732087811a3..91e6582dc27 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -168,6 +168,7 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_eventTimeStamp.html] [test_focus_disabled.html] skip-if = buildapp == 'mulet' +[test_legacy_event.html] [test_messageEvent.html] [test_moz_mouse_pixel_scroll_event.html] skip-if = buildapp == 'b2g' # bug 1126090, no wheel events on b2g diff --git a/dom/events/test/test_legacy_event.html b/dom/events/test/test_legacy_event.html new file mode 100644 index 00000000000..c877975e080 --- /dev/null +++ b/dom/events/test/test_legacy_event.html @@ -0,0 +1,256 @@ + + + + + + Test for Bug 1236979 (events that have legacy alternative versions) + + + + + +Mozilla Bug 1236979 +

+ +
+
+
+ + From 77e89ae1cd6c8f5f721151153f80b4d7bbcf2dc8 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Mon, 1 Feb 2016 19:53:55 -0800 Subject: [PATCH 85/91] Back out 2 changesets (bug 1244122) for a 50% failure rate in (at least) 10.6 e10s browser_force_refresh.js Backed out changeset d383f170be3f (bug 1244122) Backed out changeset b1eacad41db4 (bug 1244122) --- .../serviceworkers/browser_force_refresh.js | 27 +++++-------------- netwerk/cache2/CacheStorage.cpp | 6 ++--- netwerk/cache2/nsICacheStorage.idl | 6 ----- netwerk/protocol/http/nsHttpChannel.cpp | 1 - 4 files changed, 8 insertions(+), 32 deletions(-) diff --git a/dom/workers/test/serviceworkers/browser_force_refresh.js b/dom/workers/test/serviceworkers/browser_force_refresh.js index e8fc77653f9..5f235f6f5fa 100644 --- a/dom/workers/test/serviceworkers/browser_force_refresh.js +++ b/dom/workers/test/serviceworkers/browser_force_refresh.js @@ -30,9 +30,7 @@ function test() { ['dom.serviceWorkers.exemptFromPerDomainMax', true], ['dom.serviceWorkers.testing.enabled', true], ['dom.serviceWorkers.interception.enabled', true], - ['dom.caches.enabled', true], - ['browser.cache.disk.enable', false], - ['browser.cache.memory.enable', false]]}, + ['dom.caches.enabled', true]]}, function() { var url = gTestRoot + 'browser_base_force_refresh.html'; var tab = gBrowser.addTab(); @@ -41,37 +39,24 @@ function test() { tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); gBrowser.loadURI(url); - function done() { - tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler); - - gBrowser.removeTab(tab); - executeSoon(finish); - } - var cachedLoad = false; - var baseLoadCount = 0; function eventHandler(msg) { if (msg.data.type === 'base-load') { - baseLoadCount += 1; if (cachedLoad) { - is(baseLoadCount, 2, 'cached load should occur before second base load'); - return done(); - } - if (baseLoadCount !== 1) { - ok(false, 'base load without cached load should only occur once'); - return done(); + tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler); + + gBrowser.removeTab(tab); + executeSoon(finish); } } else if (msg.data.type === 'base-register') { ok(!cachedLoad, 'cached load should not occur before base register'); - is(baseLoadCount, 1, 'register should occur after first base load'); + refresh(); } else if (msg.data.type === 'base-sw-ready') { ok(!cachedLoad, 'cached load should not occur before base ready'); - is(baseLoadCount, 1, 'ready should occur after first base load'); refresh(); } else if (msg.data.type === 'cached-load') { ok(!cachedLoad, 'cached load should not occur twice'); - is(baseLoadCount, 1, 'cache load occur after first base load'); cachedLoad = true; forceRefresh(); } diff --git a/netwerk/cache2/CacheStorage.cpp b/netwerk/cache2/CacheStorage.cpp index 5630071cce9..b69a009e5b8 100644 --- a/netwerk/cache2/CacheStorage.cpp +++ b/netwerk/cache2/CacheStorage.cpp @@ -48,14 +48,12 @@ NS_IMETHODIMP CacheStorage::AsyncOpenURI(nsIURI *aURI, if (!CacheStorageService::Self()) return NS_ERROR_NOT_INITIALIZED; - if (MOZ_UNLIKELY(!CacheObserver::UseDiskCache()) && mWriteToDisk && - !(aFlags & OPEN_INTERCEPTED)) { + if (MOZ_UNLIKELY(!CacheObserver::UseDiskCache()) && mWriteToDisk) { aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_NOT_AVAILABLE); return NS_OK; } - if (MOZ_UNLIKELY(!CacheObserver::UseMemoryCache()) && !mWriteToDisk && - !(aFlags & OPEN_INTERCEPTED)) { + if (MOZ_UNLIKELY(!CacheObserver::UseMemoryCache()) && !mWriteToDisk) { aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_NOT_AVAILABLE); return NS_OK; } diff --git a/netwerk/cache2/nsICacheStorage.idl b/netwerk/cache2/nsICacheStorage.idl index f500a300b9e..49812a590da 100644 --- a/netwerk/cache2/nsICacheStorage.idl +++ b/netwerk/cache2/nsICacheStorage.idl @@ -56,12 +56,6 @@ interface nsICacheStorage : nsISupports */ const uint32_t OPEN_SECRETLY = 1 << 5; - /** - * Entry is being opened as part of a service worker interception. Do not - * allow the cache to be disabled in this case. - */ - const uint32_t OPEN_INTERCEPTED = 1 << 6; - /** * Asynchronously opens a cache entry for the specified URI. * Result is fetched asynchronously via the callback. diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index f311d95ce8b..32b9103a6e7 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -2991,7 +2991,6 @@ nsHttpChannel::OpenCacheEntry(bool isHttps) intercepted->NotifyController(); } else { if (mInterceptCache == INTERCEPTED) { - cacheEntryOpenFlags |= nsICacheStorage::OPEN_INTERCEPTED; DebugOnly exists; MOZ_ASSERT(NS_SUCCEEDED(cacheStorage->Exists(openURI, extension, &exists)) && exists, "The entry must exist in the cache after we create it here"); From be2f8010b017793713d2fc5bed54870dc0f89226 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 1 Feb 2016 17:24:39 -0800 Subject: [PATCH 86/91] Bug 1191145 - Stop blocking scripts when handling IPC messages (r=dvander) --- dom/ipc/ContentChild.cpp | 4 ---- ipc/glue/MessageChannel.cpp | 43 +------------------------------------ ipc/glue/MessageChannel.h | 10 --------- 3 files changed, 1 insertion(+), 56 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 4c460a1c2d9..f09f0ceecd5 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -673,10 +673,6 @@ ContentChild::Init(MessageLoop* aIOLoop, } sSingleton = this; - // Make sure there's an nsAutoScriptBlocker on the stack when dispatching - // urgent messages. - GetIPCChannel()->BlockScripts(); - // If communications with the parent have broken down, take the process // down so it's not hanging around. bool abortOnError = true; diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index eb18108d4c5..f27e187eb27 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -280,31 +280,6 @@ private: CxxStackFrame& operator=(const CxxStackFrame&) = delete; }; -namespace { - -class MOZ_RAII MaybeScriptBlocker { -public: - explicit MaybeScriptBlocker(MessageChannel *aChannel, bool aBlock - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : mBlocked(aChannel->ShouldBlockScripts() && aBlock) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (mBlocked) { - nsContentUtils::AddScriptBlocker(); - } - } - ~MaybeScriptBlocker() { - if (mBlocked) { - nsContentUtils::RemoveScriptBlocker(); - } - } -private: - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER - bool mBlocked; -}; - -} // namespace - MessageChannel::MessageChannel(MessageListener *aListener) : mListener(aListener), mChannelState(ChannelClosed), @@ -332,7 +307,6 @@ MessageChannel::MessageChannel(MessageListener *aListener) mSawInterruptOutMsg(false), mIsWaitingForIncoming(false), mAbortOnError(false), - mBlockScripts(false), mFlags(REQUIRE_DEFAULT), mPeerPidSet(false), mPeerPid(-1) @@ -868,9 +842,6 @@ MessageChannel::Send(Message* aMsg, Message* aReply) { nsAutoPtr msg(aMsg); - // See comment in DispatchSyncMessage. - MaybeScriptBlocker scriptBlocker(this, true); - // Sanity checks. AssertWorkerThread(); mMonitor->AssertNotCurrentThreadOwns(); @@ -1430,15 +1401,10 @@ MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply) int prio = aMsg.priority(); - // We don't want to run any code that might run a nested event loop here, so - // we avoid running event handlers. Once we've sent the response to the - // urgent message, it's okay to run event handlers again since the parent is - // no longer blocked. MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread()); - MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL); MessageChannel* dummy; - MessageChannel*& blockingVar = ShouldBlockScripts() ? gParentProcessBlocker : dummy; + MessageChannel*& blockingVar = mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy; Result rv; { @@ -1978,13 +1944,6 @@ MessageChannel::CloseWithTimeout() mChannelState = ChannelTimeout; } -void -MessageChannel::BlockScripts() -{ - MOZ_ASSERT(NS_IsMainThread()); - mBlockScripts = true; -} - void MessageChannel::Close() { diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 1f2ec07f938..27b6e5ae078 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -120,13 +120,6 @@ class MessageChannel : HasResultCodes void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; } ChannelFlags GetChannelFlags() { return mFlags; } - void BlockScripts(); - - bool ShouldBlockScripts() const - { - return mBlockScripts; - } - // Asynchronously send a message to the other side of the channel bool Send(Message* aMsg); @@ -776,9 +769,6 @@ class MessageChannel : HasResultCodes // a channel error occurs? bool mAbortOnError; - // Should we prevent scripts from running while dispatching urgent messages? - bool mBlockScripts; - // See SetChannelFlags ChannelFlags mFlags; From 951d19a0746102bbed9c74ba5fc7171b544a2656 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 2 Feb 2016 11:38:49 +1100 Subject: [PATCH 87/91] Bug 1244523: P1. Allow video/quicktime mimetype. r=cpearce It's just like video/mp4 --- dom/media/fmp4/MP4Decoder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index 6e3846932f1..2e41f4fff81 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -107,6 +107,7 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs, aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP) || #endif aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") || + aMIMETypeExcludingCodecs.EqualsASCII("video/quicktime") || aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v"); if (!isMP4Audio && !isMP4Video) { return false; From 91b04a5c80ce25692321c1e46b9d2293547674aa Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 2 Feb 2016 11:38:50 +1100 Subject: [PATCH 88/91] Bug 1244523: [mp4] P2. Have sniffer recognise ftyp qt subtype. r=cpearce --- toolkit/components/mediasniffer/nsMediaSniffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toolkit/components/mediasniffer/nsMediaSniffer.cpp b/toolkit/components/mediasniffer/nsMediaSniffer.cpp index fa21493164e..eb1fcb1a750 100644 --- a/toolkit/components/mediasniffer/nsMediaSniffer.cpp +++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp @@ -42,7 +42,8 @@ nsMediaSnifferEntry sFtypEntries[] = { PATTERN_ENTRY("\xFF\xFF\xFF", "avc", VIDEO_MP4), // Could be avc1, avc2, ... PATTERN_ENTRY("\xFF\xFF\xFF", "3gp", VIDEO_3GPP), // Could be 3gp4, 3gp5, ... PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "M4A ", AUDIO_MP4), - PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "M4P ", AUDIO_MP4) + PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "M4P ", AUDIO_MP4), + PATTERN_ENTRY("\xFF\xFF\xFF\xFF", "qt ", VIDEO_MP4), }; static bool MatchesBrands(const uint8_t aData[4], nsACString& aSniffedType) From 3c49e8785837675ea5c1722658ab713758071648 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 2 Feb 2016 11:38:53 +1100 Subject: [PATCH 89/91] Bug 1244523: [mp4] P3. Skip four bytes when we hit a zero length box, r=kentuckyfriedtakahe Similar to bug 1180101, but don't handle only the last box in the file. --- .../av/media/libstagefright/MPEG4Extractor.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index b83e758ef09..c4c2d591796 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -651,14 +651,14 @@ static bool ValidInputSize(int32_t size) { status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { ALOGV("entering parseChunk %lld/%d", *offset, depth); uint32_t hdr[2]; - ssize_t nbytes; - if ((nbytes = mDataSource->readAt(*offset, hdr, 8)) < 8) { - if (nbytes == 4) { - if (!hdr[0]) { - *offset += 4; - return OK; - } - } + if (mDataSource->readAt(*offset, hdr, 4) < 4) { + return ERROR_IO; + } + if (!hdr[0]) { + *offset += 4; + return OK; + } + if (mDataSource->readAt(*offset + 4, hdr + 1, 4) < 4) { return ERROR_IO; } uint64_t chunk_size = ntohl(hdr[0]); From 14f7b6cd7e651617113ec8855bc9f271c88ac22b Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 2 Feb 2016 11:38:39 +1100 Subject: [PATCH 90/91] Bug 1244523: [mp4] P4. Add gtest. r=kentuckyfriedtakahe --- dom/media/gtest/TestMP4Demuxer.cpp | 11 ++++++++++- dom/media/gtest/moz.build | 1 + dom/media/gtest/short-zero-inband.mov | Bin 0 -> 93641 bytes 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 dom/media/gtest/short-zero-inband.mov diff --git a/dom/media/gtest/TestMP4Demuxer.cpp b/dom/media/gtest/TestMP4Demuxer.cpp index 86fbfd4d8ce..0f5b53adbdf 100644 --- a/dom/media/gtest/TestMP4Demuxer.cpp +++ b/dom/media/gtest/TestMP4Demuxer.cpp @@ -427,7 +427,7 @@ TEST(MP4Demuxer, GetNextKeyframe) }); } -TEST(MP4Demuxer, ZeroInMoov) +TEST(MP4Demuxer, ZeroInLastMoov) { RefPtr binding = new MP4DemuxerBinding("short-zero-in-moov.mp4"); binding->RunTestAndWait([binding] () { @@ -436,3 +436,12 @@ TEST(MP4Demuxer, ZeroInMoov) }); } + +TEST(MP4Demuxer, ZeroInMoovQuickTime) +{ + RefPtr binding = new MP4DemuxerBinding("short-zero-inband.mov"); + binding->RunTestAndWait([binding] () { + // It demuxes without error. That is sufficient. + binding->mTaskQueue->BeginShutdown(); + }); +} diff --git a/dom/media/gtest/moz.build b/dom/media/gtest/moz.build index d553f03fff3..77ef8045dc9 100644 --- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -49,6 +49,7 @@ TEST_HARNESS_FILES.gtest += [ 'noise.mp3', 'noise_vbr.mp3', 'short-zero-in-moov.mp4', + 'short-zero-inband.mov', 'small-shot.mp3', 'test.webm', 'test_case_1224361.vp8.ivf', diff --git a/dom/media/gtest/short-zero-inband.mov b/dom/media/gtest/short-zero-inband.mov new file mode 100644 index 0000000000000000000000000000000000000000..9c1864286526a0db4a4ee98bdcb34111539d2625 GIT binary patch literal 93641 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 RYjB0*HHI(4N=mg<{|7evEV}>z literal 0 HcmV?d00001 From 7265fc2ded577a5dec855e538808d86303c481fa Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 1 Feb 2016 21:57:38 -0800 Subject: [PATCH 91/91] backout bug 1236979 (changesets 2f4de652525e, d7136545a16e, f62e6638add1, f8b1e085b161) for xpcshell test failures --- dom/base/nsGkAtomList.h | 4 - dom/events/Event.h | 44 ----- dom/events/EventListenerManager.cpp | 192 ++++++------------- dom/events/EventListenerManager.h | 4 +- dom/events/EventNameList.h | 19 -- dom/events/test/mochitest.ini | 1 - dom/events/test/test_legacy_event.html | 256 ------------------------- widget/EventMessageList.h | 8 +- 8 files changed, 59 insertions(+), 469 deletions(-) delete mode 100644 dom/events/test/test_legacy_event.html diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index c7cf6c62b27..008825a2cc9 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -931,10 +931,6 @@ GK_ATOM(onupgradeneeded, "onupgradeneeded") GK_ATOM(onussdreceived, "onussdreceived") GK_ATOM(onversionchange, "onversionchange") GK_ATOM(onvoicechange, "onvoicechange") -GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd") -GK_ATOM(onwebkitAnimationIteration, "onwebkitAnimationIteration") -GK_ATOM(onwebkitAnimationStart, "onwebkitAnimationStart") -GK_ATOM(onwebkitTransitionEnd, "onwebkitTransitionEnd") GK_ATOM(onwheel, "onwheel") GK_ATOM(open, "open") GK_ATOM(optgroup, "optgroup") diff --git a/dom/events/Event.h b/dom/events/Event.h index 85fb0642d09..2dd50c50961 100644 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -30,7 +30,6 @@ namespace mozilla { namespace dom { class EventTarget; -class EventMessageAutoOverride; class WantsPopupControlCheck; #define GENERATED_EVENT(EventClass_) class EventClass_; #include "mozilla/dom/GeneratedEventList.h" @@ -240,7 +239,6 @@ protected: void SetEventType(const nsAString& aEventTypeArg); already_AddRefed GetTargetFromFrame(); - friend class EventMessageAutoOverride; friend class WantsPopupControlCheck; void SetWantsPopupControlCheck(bool aCheck) { @@ -270,48 +268,6 @@ protected: bool mWantsPopupControlCheck; }; -/** - * RAII helper-class to override an event's message (i.e. its DOM-exposed - * type), for as long as the object is alive. Restores the original - * EventMessage when destructed. - * - * Notable requirements: - * - The original & overriding messages must be known (not eUnidentifiedEvent). - * - The original & overriding messages must be different. - * - The passed-in nsIDOMEvent must outlive this RAII helper. - */ -class MOZ_RAII EventMessageAutoOverride -{ -public: - explicit EventMessageAutoOverride(nsIDOMEvent* aEvent, - EventMessage aOverridingMessage) - : mEvent(aEvent->InternalDOMEvent()), - mOrigMessage(mEvent->mEvent->mMessage) - { - MOZ_ASSERT(aOverridingMessage != mOrigMessage, - "Don't use this class if you're not actually overriding"); - MOZ_ASSERT(aOverridingMessage != eUnidentifiedEvent, - "Only use this class with a valid overriding EventMessage"); - MOZ_ASSERT(mOrigMessage != eUnidentifiedEvent && - mEvent->mEvent->typeString.IsEmpty(), - "Only use this class on events whose overridden type is " - "known (so we can restore it properly)"); - - mEvent->mEvent->mMessage = aOverridingMessage; - } - - ~EventMessageAutoOverride() - { - mEvent->mEvent->mMessage = mOrigMessage; - } - -protected: - // Non-owning ref, which should be safe since we're a stack-allocated object - // with limited lifetime. Whoever creates us should keep mEvent alive. - Event* const MOZ_NON_OWNING_REF mEvent; - const EventMessage mOrigMessage; -}; - class MOZ_STACK_CLASS WantsPopupControlCheck { public: diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index ecc3d95eb8d..3ddb7dd9e48 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -19,9 +19,7 @@ #include "mozilla/HalSensor.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/JSEventHandler.h" -#include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/Preferences.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" @@ -97,46 +95,6 @@ MutationBitForEventType(EventMessage aEventType) uint32_t EventListenerManager::sMainThreadCreatedCount = 0; -static bool -IsWebkitPrefixSupportEnabled() -{ - static bool sIsWebkitPrefixSupportEnabled; - static bool sIsPrefCached = false; - - if (!sIsPrefCached) { - sIsPrefCached = true; - Preferences::AddBoolVarCache(&sIsWebkitPrefixSupportEnabled, - "layout.css.prefixes.webkit"); - } - - return sIsWebkitPrefixSupportEnabled; -} - -// If the given EventMessage has a legacy version that we support, then this -// function returns that legacy version. Otherwise, this function simply -// returns the passed-in EventMessage. -EventMessage -GetLegacyEventMessage(EventMessage aEventMessage) -{ - if (IsWebkitPrefixSupportEnabled()) { - // webkit-prefixed legacy events: - if (aEventMessage == eTransitionEnd) { - return eWebkitTransitionEnd; - } - if (aEventMessage == eAnimationStart) { - return eWebkitAnimationStart; - } - if (aEventMessage == eAnimationEnd) { - return eWebkitAnimationEnd; - } - if (aEventMessage == eAnimationIteration) { - return eWebkitAnimationIteration; - } - } - - return aEventMessage; -} - EventListenerManagerBase::EventListenerManagerBase() : mNoListenerForEvent(eVoidEvent) , mMayHavePaintEventListener(false) @@ -645,15 +603,9 @@ EventListenerManager::RemoveEventListenerInternal( } bool -EventListenerManager::ListenerCanHandle(const Listener* aListener, - const WidgetEvent* aEvent, - EventMessage aEventMessage) const - +EventListenerManager::ListenerCanHandle(Listener* aListener, + WidgetEvent* aEvent) { - MOZ_ASSERT(aEventMessage == aEvent->mMessage || - aEventMessage == GetLegacyEventMessage(aEvent->mMessage), - "aEvent and aEventMessage should agree, modulo legacyness"); - // This is slightly different from EVENT_TYPE_EQUALS in that it returns // true even when aEvent->mMessage == eUnidentifiedEvent and // aListener=>mEventMessage != eUnidentifiedEvent as long as the atoms are @@ -668,7 +620,7 @@ EventListenerManager::ListenerCanHandle(const Listener* aListener, return aListener->mTypeString.Equals(aEvent->typeString); } MOZ_ASSERT(mIsMainThreadELM); - return aListener->mEventMessage == aEventMessage; + return aListener->mEventMessage == aEvent->mMessage; } void @@ -1152,107 +1104,77 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, aEvent->mFlags.mDefaultPrevented = true; } + nsAutoTObserverArray::EndLimitedIterator iter(mListeners); Maybe popupStatePusher; if (mIsMainThreadELM) { popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent, *aDOMEvent)); } bool hasListener = false; - bool usingLegacyMessage = false; - EventMessage eventMessage = aEvent->mMessage; - - while (true) { - nsAutoTObserverArray::EndLimitedIterator iter(mListeners); - Maybe legacyAutoOverride; - while (iter.HasMore()) { - if (aEvent->mFlags.mImmediatePropagationStopped) { - break; - } - Listener* listener = &iter.GetNext(); - // Check that the phase is same in event and event listener. - // Handle only trusted events, except when listener permits untrusted events. - if (ListenerCanHandle(listener, aEvent, eventMessage)) { - hasListener = true; - if (listener->IsListening(aEvent) && - (aEvent->mFlags.mIsTrusted || - listener->mFlags.mAllowUntrustedEvents)) { - if (!*aDOMEvent) { - // This is tiny bit slow, but happens only once per event. - nsCOMPtr et = - do_QueryInterface(aEvent->originalTarget); - RefPtr event = EventDispatcher::CreateEvent(et, aPresContext, + while (iter.HasMore()) { + if (aEvent->mFlags.mImmediatePropagationStopped) { + break; + } + Listener* listener = &iter.GetNext(); + // Check that the phase is same in event and event listener. + // Handle only trusted events, except when listener permits untrusted events. + if (ListenerCanHandle(listener, aEvent)) { + hasListener = true; + if (listener->IsListening(aEvent) && + (aEvent->mFlags.mIsTrusted || + listener->mFlags.mAllowUntrustedEvents)) { + if (!*aDOMEvent) { + // This is tiny bit slow, but happens only once per event. + nsCOMPtr et = + do_QueryInterface(aEvent->originalTarget); + RefPtr event = EventDispatcher::CreateEvent(et, aPresContext, aEvent, EmptyString()); - event.forget(aDOMEvent); - } - if (*aDOMEvent) { + event.forget(aDOMEvent); + } + if (*aDOMEvent) { + if (!aEvent->currentTarget) { + aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); if (!aEvent->currentTarget) { - aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); - if (!aEvent->currentTarget) { - break; + break; + } + } + + // Maybe add a marker to the docshell's timeline, but only + // bother with all the logic if some docshell is recording. + nsDocShell* docShell; + RefPtr timelines = TimelineConsumers::Get(); + bool needsEndEventMarker = false; + + if (mIsMainThreadELM && + listener->mListenerType != Listener::eNativeListener) { + nsCOMPtr docShellComPtr = GetDocShellForTarget(); + if (docShellComPtr) { + docShell = static_cast(docShellComPtr.get()); + if (timelines && timelines->HasConsumer(docShell)) { + needsEndEventMarker = true; + nsAutoString typeStr; + (*aDOMEvent)->GetType(typeStr); + uint16_t phase; + (*aDOMEvent)->GetEventPhase(&phase); + timelines->AddMarkerForDocShell(docShell, Move( + MakeUnique( + typeStr, phase, MarkerTracingType::START))); } } - if (usingLegacyMessage && !legacyAutoOverride) { - // Override the aDOMEvent's event-message (its .type) until we - // finish traversing listeners (when legacyAutoOverride destructs) - legacyAutoOverride.emplace(*aDOMEvent, eventMessage); - } + } - // Maybe add a marker to the docshell's timeline, but only - // bother with all the logic if some docshell is recording. - nsDocShell* docShell; - RefPtr timelines = TimelineConsumers::Get(); - bool needsEndEventMarker = false; + if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) { + aEvent->mFlags.mExceptionHasBeenRisen = true; + } - if (mIsMainThreadELM && - listener->mListenerType != Listener::eNativeListener) { - nsCOMPtr docShellComPtr = GetDocShellForTarget(); - if (docShellComPtr) { - docShell = static_cast(docShellComPtr.get()); - if (timelines && timelines->HasConsumer(docShell)) { - needsEndEventMarker = true; - nsAutoString typeStr; - (*aDOMEvent)->GetType(typeStr); - uint16_t phase; - (*aDOMEvent)->GetEventPhase(&phase); - timelines->AddMarkerForDocShell(docShell, Move( - MakeUnique( - typeStr, phase, MarkerTracingType::START))); - } - } - } - - if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) { - aEvent->mFlags.mExceptionHasBeenRisen = true; - } - - if (needsEndEventMarker) { - timelines->AddMarkerForDocShell( - docShell, "DOMEvent", MarkerTracingType::END); - } + if (needsEndEventMarker) { + timelines->AddMarkerForDocShell( + docShell, "DOMEvent", MarkerTracingType::END); } } } } - - // If we didn't find any matching listeners, and our event has a legacy - // version, we'll now switch to looking for that legacy version and we'll - // recheck our listeners. - if (hasListener || usingLegacyMessage) { - // (No need to recheck listeners, because we already found a match, or we - // already rechecked them.) - break; - } - EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage); - if (legacyEventMessage == eventMessage) { - break; // There's no legacy version of our event; no need to recheck. - } - MOZ_ASSERT(GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage, - "Legacy event messages should not themselves have legacy versions"); - - // Recheck our listeners, using the legacy event message we just looked up: - eventMessage = legacyEventMessage; - usingLegacyMessage = true; } aEvent->currentTarget = nullptr; diff --git a/dom/events/EventListenerManager.h b/dom/events/EventListenerManager.h index fbe5a737e5e..85660e77b37 100644 --- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -571,9 +571,7 @@ protected: nsPIDOMWindowInner* GetInnerWindowForTarget(); already_AddRefed GetTargetAsInnerWindow() const; - bool ListenerCanHandle(const Listener* aListener, - const WidgetEvent* aEvent, - EventMessage aEventMessage) const; + bool ListenerCanHandle(Listener* aListener, WidgetEvent* aEvent); // BE AWARE, a lot of instances of EventListenerManager will be created. // Therefor, we need to keep this class compact. When you add integer diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index b3846b3ee3d..1fb978f32ef 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -924,7 +924,6 @@ NON_IDL_EVENT(MozEdgeUICompleted, EventNameType_None, eSimpleGestureEventClass) -// CSS Transition & Animation events: NON_IDL_EVENT(transitionend, eTransitionEnd, EventNameType_None, @@ -942,24 +941,6 @@ NON_IDL_EVENT(animationiteration, EventNameType_None, eAnimationEventClass) -// Webkit-prefixed versions of Transition & Animation events, for web compat: -NON_IDL_EVENT(webkitTransitionEnd, - eWebkitTransitionEnd, - EventNameType_None, - eTransitionEventClass) -NON_IDL_EVENT(webkitAnimationEnd, - eWebkitAnimationEnd, - EventNameType_None, - eAnimationEventClass) -NON_IDL_EVENT(webkitAnimationIteration, - eWebkitAnimationIteration, - EventNameType_None, - eAnimationEventClass) -NON_IDL_EVENT(webkitAnimationStart, - eWebkitAnimationStart, - EventNameType_None, - eAnimationEventClass) - NON_IDL_EVENT(audioprocess, eAudioProcess, EventNameType_None, diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 91e6582dc27..732087811a3 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -168,7 +168,6 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_eventTimeStamp.html] [test_focus_disabled.html] skip-if = buildapp == 'mulet' -[test_legacy_event.html] [test_messageEvent.html] [test_moz_mouse_pixel_scroll_event.html] skip-if = buildapp == 'b2g' # bug 1126090, no wheel events on b2g diff --git a/dom/events/test/test_legacy_event.html b/dom/events/test/test_legacy_event.html deleted file mode 100644 index c877975e080..00000000000 --- a/dom/events/test/test_legacy_event.html +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - Test for Bug 1236979 (events that have legacy alternative versions) - - - - - -Mozilla Bug 1236979 -

- -
-
-
- - diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index 4adadf6437d..38d4deb9a6f 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -312,18 +312,12 @@ NS_EVENT_MESSAGE(eGestureNotify) NS_EVENT_MESSAGE(eScrolledAreaChanged) -// CSS Transition & Animation events: NS_EVENT_MESSAGE(eTransitionEnd) + NS_EVENT_MESSAGE(eAnimationStart) NS_EVENT_MESSAGE(eAnimationEnd) NS_EVENT_MESSAGE(eAnimationIteration) -// Webkit-prefixed versions of Transition & Animation events, for web compat: -NS_EVENT_MESSAGE(eWebkitTransitionEnd) -NS_EVENT_MESSAGE(eWebkitAnimationStart) -NS_EVENT_MESSAGE(eWebkitAnimationEnd) -NS_EVENT_MESSAGE(eWebkitAnimationIteration) - NS_EVENT_MESSAGE(eSMILBeginEvent) NS_EVENT_MESSAGE(eSMILEndEvent) NS_EVENT_MESSAGE(eSMILRepeatEvent)