author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Tue, 07 Oct 2014 15:08:19 +0200 | |
changeset 232327 | c5e445dba3ce2576dbca6c9b521e954e6f2fbb45 |
parent 232326 | 09c00a0035503f39e1e4b7f90acc86ffce272e1f (current diff) |
parent 232322 | 25a98bac926420e5fcca88d79b400c1b1c10faa5 (diff) |
child 232328 | 0c07298d0b580121445ee7167274c1d10330a183 |
push id | 4187 |
push user | bhearsum@mozilla.com |
push date | Fri, 28 Nov 2014 15:29:12 +0000 |
treeherder | mozilla-beta@f23cc6a30c11 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 35.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/components/loop/MozLoopService.jsm +++ b/browser/components/loop/MozLoopService.jsm @@ -672,17 +672,19 @@ let MozLoopServiceInternal = { // bug 1046039 for background. Services.prefs.setCharPref("loop.seenToS", "seen"); // Request the information on the new call(s) associated with this version. // The registered FxA session is checked first, then the anonymous session. // Make the call to get the GUEST session regardless of whether the FXA // request fails. - this._getCalls(LOOP_SESSION_TYPE.FXA, version).catch(() => {}); + if (MozLoopService.userProfile) { + this._getCalls(LOOP_SESSION_TYPE.FXA, version).catch(() => {}); + } this._getCalls(LOOP_SESSION_TYPE.GUEST, version).catch( error => {this._hawkRequestError(error);}); }, /** * Make a hawkRequest to GET/calls?=version for this session type. * * @param {LOOP_SESSION_TYPE} sessionType - type of hawk token used
--- a/browser/components/loop/test/xpcshell/test_loopservice_busy.js +++ b/browser/components/loop/test/xpcshell/test_loopservice_busy.js @@ -47,16 +47,20 @@ function test_send_busy_on_call() { add_test(test_send_busy_on_call); //FXA call accepted, Guest call rejected add_test(test_send_busy_on_call); //No FXA call, first Guest call accepted, second rejected function run_test() { setupFakeLoopServer(); + // Setup fake login (profile) state so we get FxA requests. + const serviceGlobal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}); + serviceGlobal.gFxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"}; + // For each notification received from the PushServer, MozLoopService will first query // for any pending calls on the FxA hawk session and then again using the guest session. // A pair of response objects in the callsResponses array will be consumed for each // notification. The even calls object is for the FxA session, the odd the Guest session. let callsRespCount = 0; let callsResponses = [ {calls: [{callId: firstCallId, @@ -92,14 +96,17 @@ function run_test() response.processAsync(); response.finish(); }); do_register_cleanup(function() { // Revert original Chat.open implementation Chat.open = openChatOrig; + // Revert fake login state + serviceGlobal.gFxAOAuthProfile = null; + // clear test pref Services.prefs.clearUserPref("loop.seenToS"); }); run_next_test(); }
--- a/browser/config/mozconfigs/linux32/common-opt +++ b/browser/config/mozconfigs/linux32/common-opt @@ -1,13 +1,14 @@ # This file is sourced by nightly, beta, and release mozconfigs. ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} ac_add_options --enable-update-packaging ac_add_options --with-google-api-keyfile=/builds/gapi.data +ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key . $topsrcdir/build/unix/mozconfig.linux32 # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1
--- a/browser/config/mozconfigs/linux64/common-opt +++ b/browser/config/mozconfigs/linux64/common-opt @@ -1,13 +1,14 @@ # This file is sourced by the nightly, beta, and release mozconfigs. ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} ac_add_options --enable-update-packaging ac_add_options --with-google-api-keyfile=/builds/gapi.data +ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key . $topsrcdir/build/unix/mozconfig.linux # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1
--- a/browser/config/mozconfigs/macosx-universal/common-opt +++ b/browser/config/mozconfigs/macosx-universal/common-opt @@ -3,16 +3,17 @@ . $topsrcdir/build/macosx/universal/mozconfig # Universal builds override the default of browser (bug 575283 comment 29) ac_add_options --enable-application=browser ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} ac_add_options --enable-update-packaging ac_add_options --with-google-api-keyfile=/builds/gapi.data +ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors
--- a/browser/config/mozconfigs/win32/common-opt +++ b/browser/config/mozconfigs/win32/common-opt @@ -9,16 +9,22 @@ ac_add_options --enable-require-all-d3dc if [ -f /c/builds/gapi.data ]; then _gapi_keyfile=/c/builds/gapi.data else _gapi_keyfile=/e/builds/gapi.data fi ac_add_options --with-google-api-keyfile=${_gapi_keyfile} +if [ -f /c/builds/google-oauth-api.key ]; then + _google_oauth_api_keyfile=/c/builds/google-oauth-api.key +else + _google_oauth_api_keyfile=/e/builds/google-oauth-api.key +fi +ac_add_options --with-google-api-keyfile=${_google_oauth_api_keyfile} # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then . $topsrcdir/build/win32/mozconfig.vs2010-win64
--- a/browser/config/mozconfigs/win64/common-opt +++ b/browser/config/mozconfigs/win64/common-opt @@ -7,15 +7,22 @@ ac_add_options --enable-update-packaging ac_add_options --enable-jemalloc if [ -f /c/builds/gapi.data ]; then _gapi_keyfile=/c/builds/gapi.data else _gapi_keyfile=/e/builds/gapi.data fi ac_add_options --with-google-api-keyfile=${_gapi_keyfile} +if [ -f /c/builds/google-oauth-api.key ]; then + _google_oauth_api_keyfile=/c/builds/google-oauth-api.key +else + _google_oauth_api_keyfile=/e/builds/google-oauth-api.key +fi +ac_add_options --with-google-api-keyfile=${_google_oauth_api_keyfile} + # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 export MOZ_TELEMETRY_REPORTING=1 # Package js shell. export MOZ_PACKAGE_JSSHELL=1
--- a/browser/modules/UITour.jsm +++ b/browser/modules/UITour.jsm @@ -93,17 +93,20 @@ this.UITour = { "class", "toolbarbutton-icon"); }, widgetName: "PanelUI-customize", }], ["help", {query: "#PanelUI-help"}], ["home", {query: "#home-button"}], ["loop", {query: "#loop-call-button"}], - ["forget", {query: "#panic-button"}], + ["forget", { + query: "#panic-button", + widgetName: "panic-button", + allowAdd: true }], ["privateWindow", {query: "#privatebrowsing-button"}], ["quit", {query: "#PanelUI-quit"}], ["search", { query: "#searchbar", widgetName: "search-container", }], ["searchProvider", { query: (aDocument) => { @@ -429,16 +432,25 @@ this.UITour = { break; } case "resetFirefox": { // Open a reset profile dialog window. ResetProfile.openConfirmationDialog(window); break; } + + case "addNavBarWidget": { + // Add a widget to the toolbar + let targetPromise = this.getTarget(window, data.name); + targetPromise.then(target => { + this.addNavBarWidget(target, contentDocument, data.callbackID); + }).then(null, Cu.reportError); + break; + } } if (!this.originTabs.has(window)) this.originTabs.set(window, new Set()); this.originTabs.get(window).add(tab); tab.addEventListener("TabClose", this); tab.addEventListener("TabBecomingWindow", this); @@ -693,16 +705,17 @@ this.UITour = { } deferred.resolve({ addTargetListener: targetObject.addTargetListener, node: node, removeTargetListener: targetObject.removeTargetListener, targetName: aTargetName, widgetName: targetObject.widgetName, + allowAdd: targetObject.allowAdd, }); }).then(null, Cu.reportError); return deferred.promise; }, targetIsInAppMenu: function(aTarget) { let placement = CustomizableUI.getPlacementOfWidget(aTarget.widgetName || aTarget.node.id); if (placement && placement.area == CustomizableUI.AREA_PANEL) { @@ -1172,16 +1185,34 @@ this.UITour = { }, (err) => { Cu.reportError(err); this.sendPageCallback(aContentDocument, aCallbackID, { targets: [], }); }); }, + addNavBarWidget: function (aTarget, aContentDocument, aCallbackID) { + if (aTarget.node) { + Cu.reportError("UITour: can't add a widget already present: " + data.target); + return; + } + if (!aTarget.allowAdd) { + Cu.reportError("UITour: not allowed to add this widget: " + data.target); + return; + } + if (!aTarget.widgetName) { + Cu.reportError("UITour: can't add a widget without a widgetName property: " + data.target); + return; + } + + CustomizableUI.addWidgetToArea(aTarget.widgetName, CustomizableUI.AREA_NAVBAR); + this.sendPageCallback(aContentDocument, aCallbackID); + }, + _addAnnotationPanelMutationObserver: function(aPanelEl) { #ifdef XP_LINUX let observer = this._annotationPanelMutationObservers.get(aPanelEl); if (observer) { return; } let win = aPanelEl.ownerDocument.defaultView; observer = new win.MutationObserver(this._annotationMutationCallback);
--- a/browser/modules/test/browser_UITour.js +++ b/browser/modules/test/browser_UITour.js @@ -272,15 +272,40 @@ let tests = [ ok(typeof(result[property]) !== undefined, "Check " + property + " isn't undefined."); is(result[property], Services.appinfo[property], "Should have the same " + property + " property."); } done(); } gContentAPI.getConfiguration("appinfo", callback); }, + function test_addToolbarButton(done) { + let placement = CustomizableUI.getPlacementOfWidget("panic-button"); + is(placement, null, "default UI has panic button in the palette"); + + gContentAPI.getConfiguration("availableTargets", (data) => { + let available = (data.targets.indexOf("forget") != -1); + ok(!available, "Forget button should not be available by default"); + + gContentAPI.addNavBarWidget("forget", () => { + info("addNavBarWidget callback successfully called"); + + let placement = CustomizableUI.getPlacementOfWidget("panic-button"); + is(placement.area, CustomizableUI.AREA_NAVBAR); + + gContentAPI.getConfiguration("availableTargets", (data) => { + let available = (data.targets.indexOf("forget") != -1); + ok(available, "Forget button should now be available"); + + // Cleanup + CustomizableUI.removeWidgetFromArea("panic-button"); + done(); + }); + }); + }); + }, // Make sure this test is last in the file so the appMenu gets left open and done will confirm it got tore down. function cleanupMenus(done) { gContentAPI.showMenu("appMenu"); done(); }, ];
--- a/browser/modules/test/uitour.js +++ b/browser/modules/test/uitour.js @@ -178,9 +178,16 @@ if (typeof Mozilla == 'undefined') { Mozilla.UITour.showFirefoxAccounts = function() { _sendEvent('showFirefoxAccounts'); }; Mozilla.UITour.resetFirefox = function() { _sendEvent('resetFirefox'); }; + Mozilla.UITour.addNavBarWidget= function(name, callback) { + _sendEvent('addNavBarWidget', { + name: name, + callbackID: _waitForCallback(callback), + }); + }; + })();
--- a/build/automationutils.py +++ b/build/automationutils.py @@ -304,41 +304,51 @@ def processSingleLeakFile(leakLogFileNam if logAsWarning: log.warning("%s | leakcheck | %s %d bytes leaked (%s)" % (prefix, processString, totalBytesLeaked, leakedObjectSummary)) else: log.info("%s | leakcheck | %s %d bytes leaked (%s)" % (prefix, processString, totalBytesLeaked, leakedObjectSummary)) -def processLeakLog(leakLogFile, leakThresholds, ignoreMissingLeaks): +def processLeakLog(leakLogFile, options): """Process the leak log, including separate leak logs created by child processes. Use this function if you want an additional PASS/FAIL summary. It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable. The base of leakLogFile for a non-default process needs to end with _proctype_pid12345.log "proctype" is a string denoting the type of the process, which should be the result of calling XRE_ChildProcessTypeToString(). 12345 is a series of digits that is the pid for the process. The .log is optional. All other file names are treated as being for default processes. + The options argument is checked for two optional attributes, + leakThresholds and ignoreMissingLeaks. + leakThresholds should be a dict mapping process types to leak thresholds, in bytes. If a process type is not present in the dict the threshold will be 0. + + ignoreMissingLeaks should be a list of process types. If a process + creates a leak log without a TOTAL, then we report an error if it isn't + in the list ignoreMissingLeaks. """ if not os.path.exists(leakLogFile): log.info("WARNING | leakcheck | refcount logging is off, so leaks can't be detected!") return + leakThresholds = getattr(options, 'leakThresholds', {}) + ignoreMissingLeaks = getattr(options, 'ignoreMissingLeaks', []) + # This list is based on kGeckoProcessTypeString. ipdlunittest processes likely # are not going to produce leak logs we will ever see. knownProcessTypes = ["default", "plugin", "tab", "geckomediaplugin"] for processType in knownProcessTypes: log.info("TEST-INFO | leakcheck | %s process: leak threshold set at %d bytes" % (processType, leakThresholds.get(processType, 0)))
--- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -1723,17 +1723,17 @@ public: if (!cc) { return true; } ClonedMessageData data; if (!BuildClonedMessageDataForChild(cc, aData, data)) { return false; } InfallibleTArray<mozilla::jsipc::CpowEntry> cpows; - if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { return false; } if (aIsSync) { return cc->SendSyncMessage(PromiseFlatString(aMessage), data, cpows, IPC::Principal(aPrincipal), aJSONRetVal); } return cc->CallRpcMessage(PromiseFlatString(aMessage), data, cpows, IPC::Principal(aPrincipal), aJSONRetVal); @@ -1750,17 +1750,17 @@ public: if (!cc) { return true; } ClonedMessageData data; if (!BuildClonedMessageDataForChild(cc, aData, data)) { return false; } InfallibleTArray<mozilla::jsipc::CpowEntry> cpows; - if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { return false; } return cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, IPC::Principal(aPrincipal)); } };
--- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -55,8 +55,10 @@ MSG_DEF(MSG_INVALID_URL, 1, "{0} is not MSG_DEF(MSG_METADATA_NOT_CONFIGURED, 0, "Either size or lastModified should be true.") MSG_DEF(MSG_INVALID_READ_SIZE, 0, "0 (Zero) is not a valid read size.") MSG_DEF(MSG_HEADERS_IMMUTABLE, 0, "Headers are immutable and cannot be modified.") MSG_DEF(MSG_INVALID_HEADER_NAME, 1, "{0} is an invalid header name.") MSG_DEF(MSG_INVALID_HEADER_VALUE, 1, "{0} is an invalid header value.") MSG_DEF(MSG_INVALID_HEADER_SEQUENCE, 0, "Headers require name/value tuples when being initialized by a sequence.") MSG_DEF(MSG_PERMISSION_DENIED_TO_PASS_ARG, 1, "Permission denied to pass cross-origin object as {0}.") MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.") +MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, "Invalid request method {0}.") +MSG_DEF(MSG_REQUEST_BODY_CONSUMED_ERROR, 0, "Request body has already been consumed.")
new file mode 100644 --- /dev/null +++ b/dom/fetch/Fetch.cpp @@ -0,0 +1,100 @@ +/* -*- Mode: C++; 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/. */ + +#include "Fetch.h" + +#include "nsIStringStream.h" +#include "nsIUnicodeEncoder.h" + +#include "nsStringStream.h" + +#include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/URLSearchParams.h" + +namespace mozilla { +namespace dom { + +nsresult +ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit, + nsIInputStream** aStream, + nsCString& aContentType) +{ + MOZ_ASSERT(aStream); + + nsresult rv; + nsCOMPtr<nsIInputStream> byteStream; + if (aBodyInit.IsArrayBuffer()) { + const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer(); + buf.ComputeLengthAndData(); + //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok. + rv = NS_NewByteInputStream(getter_AddRefs(byteStream), + reinterpret_cast<char*>(buf.Data()), + buf.Length(), NS_ASSIGNMENT_COPY); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else if (aBodyInit.IsArrayBufferView()) { + const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView(); + buf.ComputeLengthAndData(); + //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok. + rv = NS_NewByteInputStream(getter_AddRefs(byteStream), + reinterpret_cast<char*>(buf.Data()), + buf.Length(), NS_ASSIGNMENT_COPY); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else if (aBodyInit.IsScalarValueString()) { + nsString str = aBodyInit.GetAsScalarValueString(); + + nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8"); + if (!encoder) { + return NS_ERROR_OUT_OF_MEMORY; + } + + int32_t destBufferLen; + rv = encoder->GetMaxLength(str.get(), str.Length(), &destBufferLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCString encoded; + if (!encoded.SetCapacity(destBufferLen, fallible_t())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + char* destBuffer = encoded.BeginWriting(); + int32_t srcLen = (int32_t) str.Length(); + int32_t outLen = destBufferLen; + rv = encoder->Convert(str.get(), &srcLen, destBuffer, &outLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(outLen <= destBufferLen); + encoded.SetLength(outLen); + rv = NS_NewCStringInputStream(getter_AddRefs(byteStream), encoded); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8"); + } else if (aBodyInit.IsURLSearchParams()) { + URLSearchParams& params = aBodyInit.GetAsURLSearchParams(); + nsString serialized; + params.Stringify(serialized); + rv = NS_NewStringInputStream(getter_AddRefs(byteStream), serialized); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8"); + } + + MOZ_ASSERT(byteStream); + byteStream.forget(aStream); + return NS_OK; +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/fetch/Fetch.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; 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/. */ + +#ifndef mozilla_dom_Fetch_h +#define mozilla_dom_Fetch_h + +#include "mozilla/dom/UnionTypes.h" + +class nsIInputStream; + +namespace mozilla { +namespace dom { + +/* + * Creates an nsIInputStream based on the fetch specifications 'extract a byte + * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract. + * Stores content type in out param aContentType. + */ +nsresult +ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& aBodyInit, + nsIInputStream** aStream, + nsCString& aContentType); + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_Fetch_h
--- a/dom/fetch/Headers.cpp +++ b/dom/fetch/Headers.cpp @@ -2,17 +2,16 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/Headers.h" #include "mozilla/ErrorResult.h" -#include "mozilla/dom/UnionTypes.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/Preferences.h" #include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsDOMString.h" #include "nsNetUtil.h" #include "nsPIDOMWindow.h" @@ -78,16 +77,49 @@ Headers::Constructor(const GlobalObject& if (aRv.Failed()) { return nullptr; } return headers.forget(); } +// static +already_AddRefed<Headers> +Headers::Constructor(const GlobalObject& aGlobal, + const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit, + ErrorResult& aRv) +{ + nsRefPtr<Headers> headers = new Headers(aGlobal.GetAsSupports()); + + if (aInit.IsHeaders()) { + headers->Fill(aInit.GetAsHeaders(), aRv); + } else if (aInit.IsByteStringSequenceSequence()) { + headers->Fill(aInit.GetAsByteStringSequenceSequence(), aRv); + } else if (aInit.IsByteStringMozMap()) { + headers->Fill(aInit.GetAsByteStringMozMap(), aRv); + } + + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return headers.forget(); +} + +Headers::Headers(const Headers& aOther) + : mOwner(aOther.mOwner) + , mGuard(aOther.mGuard) +{ + SetIsDOMBinding(); + ErrorResult result; + Fill(aOther, result); + MOZ_ASSERT(!result.Failed()); +} + void Headers::Append(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv) { nsAutoCString lowerName; ToLowerCase(aName, lowerName); if (IsInvalidMutableHeader(lowerName, &aValue, aRv)) { @@ -198,16 +230,22 @@ Headers::Set(const nsACString& aName, co entry->mName = lowerName; entry->mValue = aValue; } else { mList.AppendElement(Entry(lowerName, aValue)); } } void +Headers::Clear() +{ + mList.Clear(); +} + +void Headers::SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv) { // Rather than re-validate all current headers, just require code to set // this prior to populating the Headers object. Allow setting immutable // late, though, as that is pretty much required to have a useful, immutable // headers object. if (aGuard != HeadersGuardEnum::Immutable && mList.Length() > 0) { aRv.Throw(NS_ERROR_FAILURE); @@ -323,11 +361,10 @@ void Headers::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv) { nsTArray<nsString> keys; aInit.GetKeys(keys); for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) { Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv); } } - } // namespace dom } // namespace mozilla
--- a/dom/fetch/Headers.h +++ b/dom/fetch/Headers.h @@ -3,16 +3,18 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_Headers_h #define mozilla_dom_Headers_h #include "mozilla/dom/HeadersBinding.h" +#include "mozilla/dom/UnionTypes.h" + #include "nsClassHashtable.h" #include "nsWrapperCache.h" class nsPIDOMWindow; namespace mozilla { class ErrorResult; @@ -37,53 +39,67 @@ private: { } Entry() { } nsCString mName; nsCString mValue; }; - nsRefPtr<nsISupports> mOwner; + nsCOMPtr<nsISupports> mOwner; HeadersGuardEnum mGuard; nsTArray<Entry> mList; public: explicit Headers(nsISupports* aOwner, HeadersGuardEnum aGuard = HeadersGuardEnum::None) : mOwner(aOwner) , mGuard(aGuard) { SetIsDOMBinding(); } + explicit Headers(const Headers& aOther); + static bool PrefEnabled(JSContext* cx, JSObject* obj); static already_AddRefed<Headers> Constructor(const GlobalObject& aGlobal, const Optional<HeadersOrByteStringSequenceSequenceOrByteStringMozMap>& aInit, ErrorResult& aRv); + static already_AddRefed<Headers> + Constructor(const GlobalObject& aGlobal, + const OwningHeadersOrByteStringSequenceSequenceOrByteStringMozMap& aInit, + ErrorResult& aRv); + void Append(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv); void Delete(const nsACString& aName, ErrorResult& aRv); void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const; void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults, ErrorResult& aRv) const; bool Has(const nsACString& aName, ErrorResult& aRv) const; void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv); + void Clear(); + // ChromeOnly HeadersGuardEnum Guard() const { return mGuard; } void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv); virtual JSObject* WrapObject(JSContext* aCx); nsISupports* GetParentObject() const { return mOwner; } + void Fill(const Headers& aInit, ErrorResult& aRv); private: - Headers(const Headers& aOther) MOZ_DELETE; + // Since Headers is also an nsISupports, the above constructor can + // accidentally be invoked as new Headers(Headers*[, implied None guard]) when + // the intention is to use the copy constructor. Explicitly disallow it. + Headers(Headers* aOther) MOZ_DELETE; + virtual ~Headers(); static bool IsSimpleHeader(const nsACString& aName, const nsACString* aValue = nullptr); static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv); static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv); bool IsImmutable(ErrorResult& aRv) const; bool IsForbiddenRequestHeader(const nsACString& aName) const; @@ -98,17 +114,16 @@ private: return IsInvalidName(aName, aRv) || (aValue && IsInvalidValue(*aValue, aRv)) || IsImmutable(aRv) || IsForbiddenRequestHeader(aName) || IsForbiddenRequestNoCorsHeader(aName, aValue) || IsForbiddenResponseHeader(aName); } - void Fill(const Headers& aInit, ErrorResult& aRv); void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv); void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_Headers_h
new file mode 100644 --- /dev/null +++ b/dom/fetch/InternalRequest.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; 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/. */ + +#include "InternalRequest.h" + +#include "nsIContentPolicy.h" +#include "nsIDocument.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/workers/Workers.h" + +#include "WorkerPrivate.h" + +namespace mozilla { +namespace dom { + +// The global is used to extract the principal. +already_AddRefed<InternalRequest> +InternalRequest::GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const +{ + nsRefPtr<InternalRequest> copy = new InternalRequest(); + copy->mURL.Assign(mURL); + copy->SetMethod(mMethod); + copy->mHeaders = new Headers(*mHeaders); + + copy->mBodyStream = mBodyStream; + copy->mPreserveContentCodings = true; + + if (NS_IsMainThread()) { + nsIPrincipal* principal = aGlobal->PrincipalOrNull(); + MOZ_ASSERT(principal); + aRv = nsContentUtils::GetASCIIOrigin(principal, copy->mOrigin); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + } else { + workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + workers::WorkerPrivate::LocationInfo& location = worker->GetLocationInfo(); + copy->mOrigin = NS_ConvertUTF16toUTF8(location.mOrigin); + } + + copy->mMode = mMode; + copy->mCredentialsMode = mCredentialsMode; + // FIXME(nsm): Add ContentType fetch to nsIContentPolicy and friends. + // Then set copy's mContext to that. + return copy.forget(); +} + +InternalRequest::~InternalRequest() +{ +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/fetch/InternalRequest.h @@ -0,0 +1,264 @@ +/* -*- Mode: C++; 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/. */ + +#ifndef mozilla_dom_InternalRequest_h +#define mozilla_dom_InternalRequest_h + +#include "mozilla/dom/Headers.h" +#include "mozilla/dom/RequestBinding.h" +#include "mozilla/dom/UnionTypes.h" + +#include "nsIContentPolicy.h" +#include "nsIInputStream.h" +#include "nsISupportsImpl.h" + +class nsIDocument; +class nsPIDOMWindow; + +namespace mozilla { +namespace dom { + +class FetchBodyStream; +class Request; + +class InternalRequest MOZ_FINAL +{ + friend class Request; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalRequest) + + enum ContextFrameType + { + FRAMETYPE_AUXILIARY = 0, + FRAMETYPE_TOP_LEVEL, + FRAMETYPE_NESTED, + FRAMETYPE_NONE, + }; + + // Since referrer type can be none, client or a URL. + enum ReferrerType + { + REFERRER_NONE = 0, + REFERRER_CLIENT, + REFERRER_URL, + }; + + enum ResponseTainting + { + RESPONSETAINT_BASIC, + RESPONSETAINT_CORS, + RESPONSETAINT_OPAQUE, + }; + + explicit InternalRequest() + : mMethod("GET") + , mHeaders(new Headers(nullptr, HeadersGuardEnum::None)) + , mContextFrameType(FRAMETYPE_NONE) + , mReferrerType(REFERRER_CLIENT) + , mMode(RequestMode::No_cors) + , mCredentialsMode(RequestCredentials::Omit) + , mResponseTainting(RESPONSETAINT_BASIC) + , mRedirectCount(0) + , mAuthenticationFlag(false) + , mForceOriginHeader(false) + , mManualRedirect(false) + , mPreserveContentCodings(false) + , mSameOriginDataURL(false) + , mSkipServiceWorker(false) + , mSynchronous(false) + , mUnsafeRequest(false) + , mUseURLCredentials(false) + { + } + + explicit InternalRequest(const InternalRequest& aOther) + : mMethod(aOther.mMethod) + , mURL(aOther.mURL) + , mHeaders(aOther.mHeaders) + , mBodyStream(aOther.mBodyStream) + , mContext(aOther.mContext) + , mOrigin(aOther.mOrigin) + , mContextFrameType(aOther.mContextFrameType) + , mReferrerType(aOther.mReferrerType) + , mReferrerURL(aOther.mReferrerURL) + , mMode(aOther.mMode) + , mCredentialsMode(aOther.mCredentialsMode) + , mResponseTainting(aOther.mResponseTainting) + , mRedirectCount(aOther.mRedirectCount) + , mAuthenticationFlag(aOther.mAuthenticationFlag) + , mForceOriginHeader(aOther.mForceOriginHeader) + , mManualRedirect(aOther.mManualRedirect) + , mPreserveContentCodings(aOther.mPreserveContentCodings) + , mSameOriginDataURL(aOther.mSameOriginDataURL) + , mSandboxedStorageAreaURLs(aOther.mSandboxedStorageAreaURLs) + , mSkipServiceWorker(aOther.mSkipServiceWorker) + , mSynchronous(aOther.mSynchronous) + , mUnsafeRequest(aOther.mUnsafeRequest) + , mUseURLCredentials(aOther.mUseURLCredentials) + { + } + + void + GetMethod(nsCString& aMethod) const + { + aMethod.Assign(mMethod); + } + + void + SetMethod(const nsACString& aMethod) + { + mMethod.Assign(aMethod); + } + + void + GetURL(nsCString& aURL) const + { + aURL.Assign(mURL); + } + + bool + ReferrerIsNone() const + { + return mReferrerType == REFERRER_NONE; + } + + bool + ReferrerIsURL() const + { + return mReferrerType == REFERRER_URL; + } + + bool + ReferrerIsClient() const + { + return mReferrerType == REFERRER_CLIENT; + } + + nsCString + ReferrerAsURL() const + { + MOZ_ASSERT(ReferrerIsURL()); + return mReferrerURL; + } + + void + SetReferrer(const nsACString& aReferrer) + { + // May be removed later. + MOZ_ASSERT(!ReferrerIsNone()); + mReferrerType = REFERRER_URL; + mReferrerURL.Assign(aReferrer); + } + + bool + IsSynchronous() const + { + return mSynchronous; + } + + void + SetMode(RequestMode aMode) + { + mMode = aMode; + } + + void + SetCredentialsMode(RequestCredentials aCredentialsMode) + { + mCredentialsMode = aCredentialsMode; + } + + nsContentPolicyType + GetContext() const + { + return mContext; + } + + Headers* + Headers_() + { + return mHeaders; + } + + bool + ForceOriginHeader() + { + return mForceOriginHeader; + } + + void + GetOrigin(nsCString& aOrigin) const + { + aOrigin.Assign(mOrigin); + } + + void + SetBody(nsIInputStream* aStream) + { + mBodyStream = aStream; + } + + // Will return the original stream! + // Use a tee or copy if you don't want to erase the original. + void + GetBody(nsIInputStream** aStream) + { + nsCOMPtr<nsIInputStream> s = mBodyStream; + s.forget(aStream); + } + + // The global is used as the client for the new object. + already_AddRefed<InternalRequest> + GetRequestConstructorCopy(nsIGlobalObject* aGlobal, ErrorResult& aRv) const; + +private: + ~InternalRequest(); + + void + SetURL(const nsACString& aURL) + { + mURL.Assign(aURL); + } + + nsCString mMethod; + nsCString mURL; + nsRefPtr<Headers> mHeaders; + nsCOMPtr<nsIInputStream> mBodyStream; + + // nsContentPolicyType does not cover the complete set defined in the spec, + // but it is a good start. + nsContentPolicyType mContext; + + nsCString mOrigin; + + ContextFrameType mContextFrameType; + ReferrerType mReferrerType; + + // When mReferrerType is REFERRER_URL. + nsCString mReferrerURL; + + RequestMode mMode; + RequestCredentials mCredentialsMode; + ResponseTainting mResponseTainting; + + uint32_t mRedirectCount; + + bool mAuthenticationFlag; + bool mForceOriginHeader; + bool mManualRedirect; + bool mPreserveContentCodings; + bool mSameOriginDataURL; + bool mSandboxedStorageAreaURLs; + bool mSkipServiceWorker; + bool mSynchronous; + bool mUnsafeRequest; + bool mUseURLCredentials; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_InternalRequest_h
--- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -1,118 +1,443 @@ /* -*- Mode: C++; 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/. */ #include "Request.h" +#include "nsIUnicodeDecoder.h" +#include "nsIURI.h" + +#include "nsDOMFile.h" #include "nsDOMString.h" -#include "nsISupportsImpl.h" -#include "nsIURI.h" +#include "nsNetUtil.h" #include "nsPIDOMWindow.h" +#include "nsStreamUtils.h" +#include "nsStringStream.h" #include "mozilla/ErrorResult.h" +#include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/Headers.h" +#include "mozilla/dom/Fetch.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/URL.h" +#include "mozilla/dom/workers/bindings/URL.h" + +// dom/workers +#include "File.h" +#include "WorkerPrivate.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTING_ADDREF(Request) NS_IMPL_CYCLE_COLLECTING_RELEASE(Request) -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -Request::Request(nsISupports* aOwner) +Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest) : mOwner(aOwner) - , mHeaders(new Headers(aOwner)) + , mRequest(aRequest) + , mBodyUsed(false) { SetIsDOMBinding(); } Request::~Request() { } +already_AddRefed<InternalRequest> +Request::GetInternalRequest() +{ + nsRefPtr<InternalRequest> r = mRequest; + return r.forget(); +} + /*static*/ already_AddRefed<Request> -Request::Constructor(const GlobalObject& global, +Request::Constructor(const GlobalObject& aGlobal, const RequestOrScalarValueString& aInput, - const RequestInit& aInit, ErrorResult& rv) + const RequestInit& aInit, ErrorResult& aRv) { - nsRefPtr<Request> request = new Request(global.GetAsSupports()); - return request.forget(); + nsRefPtr<InternalRequest> request; + + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + + if (aInput.IsRequest()) { + nsRefPtr<Request> inputReq = &aInput.GetAsRequest(); + if (inputReq->BodyUsed()) { + aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR); + return nullptr; + } + + inputReq->SetBodyUsed(); + request = inputReq->GetInternalRequest(); + } else { + request = new InternalRequest(); + } + + request = request->GetRequestConstructorCopy(global, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RequestMode fallbackMode = RequestMode::EndGuard_; + RequestCredentials fallbackCredentials = RequestCredentials::EndGuard_; + if (aInput.IsScalarValueString()) { + nsString input; + input.Assign(aInput.GetAsScalarValueString()); + + nsString requestURL; + if (NS_IsMainThread()) { + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global); + MOZ_ASSERT(window); + nsCOMPtr<nsIURI> docURI = window->GetDocumentURI(); + nsCString spec; + aRv = docURI->GetSpec(spec); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + nsRefPtr<mozilla::dom::URL> url = + dom::URL::Constructor(aGlobal, input, NS_ConvertUTF8toUTF16(spec), aRv); + if (aRv.Failed()) { + return nullptr; + } + + url->Stringify(requestURL, aRv); + if (aRv.Failed()) { + return nullptr; + } + } else { + workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + nsString baseURL = NS_ConvertUTF8toUTF16(worker->GetLocationInfo().mHref); + nsRefPtr<workers::URL> url = + workers::URL::Constructor(aGlobal, input, baseURL, aRv); + if (aRv.Failed()) { + return nullptr; + } + + url->Stringify(requestURL, aRv); + if (aRv.Failed()) { + return nullptr; + } + } + request->SetURL(NS_ConvertUTF16toUTF8(requestURL)); + fallbackMode = RequestMode::Cors; + fallbackCredentials = RequestCredentials::Omit; + } + + RequestMode mode = aInit.mMode.WasPassed() ? aInit.mMode.Value() : fallbackMode; + RequestCredentials credentials = + aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value() + : fallbackCredentials; + + if (mode != RequestMode::EndGuard_) { + request->SetMode(mode); + } + + if (credentials != RequestCredentials::EndGuard_) { + request->SetCredentialsMode(credentials); + } + + if (aInit.mMethod.WasPassed()) { + nsCString method = aInit.mMethod.Value(); + ToLowerCase(method); + + if (!method.EqualsASCII("options") && + !method.EqualsASCII("get") && + !method.EqualsASCII("head") && + !method.EqualsASCII("post") && + !method.EqualsASCII("put") && + !method.EqualsASCII("delete")) { + NS_ConvertUTF8toUTF16 label(method); + aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label); + return nullptr; + } + + ToUpperCase(method); + request->SetMethod(method); + } + + nsRefPtr<Request> domRequest = new Request(global, request); + nsRefPtr<Headers> domRequestHeaders = domRequest->Headers_(); + + nsRefPtr<Headers> headers; + if (aInit.mHeaders.WasPassed()) { + headers = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv); + if (aRv.Failed()) { + return nullptr; + } + } else { + headers = new Headers(*domRequestHeaders); + } + + domRequestHeaders->Clear(); + + if (domRequest->Mode() == RequestMode::No_cors) { + nsCString method; + domRequest->GetMethod(method); + ToLowerCase(method); + if (!method.EqualsASCII("get") && + !method.EqualsASCII("head") && + !method.EqualsASCII("post")) { + NS_ConvertUTF8toUTF16 label(method); + aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label); + return nullptr; + } + + domRequestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv); + if (aRv.Failed()) { + return nullptr; + } + } + + domRequestHeaders->Fill(*headers, aRv); + if (aRv.Failed()) { + return nullptr; + } + + if (aInit.mBody.WasPassed()) { + const OwningArrayBufferOrArrayBufferViewOrScalarValueStringOrURLSearchParams& bodyInit = aInit.mBody.Value(); + nsCOMPtr<nsIInputStream> stream; + nsCString contentType; + aRv = ExtractByteStreamFromBody(bodyInit, + getter_AddRefs(stream), contentType); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + request->SetBody(stream); + + if (!contentType.IsVoid() && + !domRequestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { + domRequestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"), + contentType, aRv); + } + + if (aRv.Failed()) { + return nullptr; + } + } + + // Extract mime type. + nsTArray<nsCString> contentTypeValues; + domRequestHeaders->GetAll(NS_LITERAL_CSTRING("Content-Type"), + contentTypeValues, aRv); + if (aRv.Failed()) { + return nullptr; + } + + // HTTP ABNF states Content-Type may have only one value. + // This is from the "parse a header value" of the fetch spec. + if (contentTypeValues.Length() == 1) { + domRequest->mMimeType = contentTypeValues[0]; + ToLowerCase(domRequest->mMimeType); + } + + return domRequest.forget(); } already_AddRefed<Request> Request::Clone() const { - nsRefPtr<Request> request = new Request(mOwner); + // FIXME(nsm): Bug 1073231. This is incorrect, but the clone method isn't + // well defined yet. + nsRefPtr<Request> request = new Request(mOwner, + new InternalRequest(*mRequest)); return request.forget(); } +namespace { +nsresult +DecodeUTF8(const nsCString& aBuffer, nsString& aDecoded) +{ + nsCOMPtr<nsIUnicodeDecoder> decoder = + EncodingUtils::DecoderForEncoding("UTF-8"); + if (!decoder) { + return NS_ERROR_FAILURE; + } + + int32_t destBufferLen; + nsresult rv = + decoder->GetMaxLength(aBuffer.get(), aBuffer.Length(), &destBufferLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!aDecoded.SetCapacity(destBufferLen, fallible_t())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + char16_t* destBuffer = aDecoded.BeginWriting(); + int32_t srcLen = (int32_t) aBuffer.Length(); + int32_t outLen = destBufferLen; + rv = decoder->Convert(aBuffer.get(), &srcLen, destBuffer, &outLen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(outLen <= destBufferLen); + aDecoded.SetLength(outLen); + return NS_OK; +} +} + +already_AddRefed<Promise> +Request::ConsumeBody(ConsumeType aType, ErrorResult& aRv) +{ + nsRefPtr<Promise> promise = Promise::Create(mOwner, aRv); + if (aRv.Failed()) { + return nullptr; + } + + if (BodyUsed()) { + aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR); + return nullptr; + } + + SetBodyUsed(); + + // While the spec says to do this asynchronously, all the body constructors + // right now only accept bodies whose streams are backed by an in-memory + // buffer that can be read without blocking. So I think this is fine. + nsCOMPtr<nsIInputStream> stream; + mRequest->GetBody(getter_AddRefs(stream)); + + if (!stream) { + aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0, + NS_ASSIGNMENT_COPY); + if (aRv.Failed()) { + return nullptr; + } + } + + AutoJSAPI api; + api.Init(mOwner); + JSContext* cx = api.cx(); + + // We can make this assertion because for now we only support memory backed + // structures for the body argument for a Request. + MOZ_ASSERT(NS_InputStreamIsBuffered(stream)); + nsCString buffer; + uint64_t len; + aRv = stream->Available(&len); + if (aRv.Failed()) { + return nullptr; + } + + aRv = NS_ReadInputStreamToString(stream, buffer, len); + if (aRv.Failed()) { + return nullptr; + } + + buffer.SetLength(len); + + switch (aType) { + case CONSUME_ARRAYBUFFER: { + JS::Rooted<JSObject*> arrayBuffer(cx); + arrayBuffer = + ArrayBuffer::Create(cx, buffer.Length(), + reinterpret_cast<const uint8_t*>(buffer.get())); + JS::Rooted<JS::Value> val(cx); + val.setObjectOrNull(arrayBuffer); + promise->MaybeResolve(cx, val); + return promise.forget(); + } + case CONSUME_BLOB: { + // XXXnsm it is actually possible to avoid these duplicate allocations + // for the Blob case by having the Blob adopt the stream's memory + // directly, but I've not added a special case for now. + // + // This is similar to nsContentUtils::CreateBlobBuffer, but also deals + // with worker wrapping. + uint32_t blobLen = buffer.Length(); + void* blobData = moz_malloc(blobLen); + nsCOMPtr<nsIDOMBlob> blob; + if (blobData) { + memcpy(blobData, buffer.BeginReading(), blobLen); + blob = DOMFile::CreateMemoryFile(blobData, blobLen, + NS_ConvertUTF8toUTF16(mMimeType)); + } else { + aRv = NS_ERROR_OUT_OF_MEMORY; + return nullptr; + } + + JS::Rooted<JS::Value> jsBlob(cx); + if (NS_IsMainThread()) { + aRv = nsContentUtils::WrapNative(cx, blob, &jsBlob); + if (aRv.Failed()) { + return nullptr; + } + } else { + jsBlob.setObject(*workers::file::CreateBlob(cx, blob)); + } + promise->MaybeResolve(cx, jsBlob); + return promise.forget(); + } + case CONSUME_JSON: { + nsString decoded; + aRv = DecodeUTF8(buffer, decoded); + if (aRv.Failed()) { + return nullptr; + } + + JS::Rooted<JS::Value> json(cx); + if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) { + JS::Rooted<JS::Value> exn(cx); + if (JS_GetPendingException(cx, &exn)) { + JS_ClearPendingException(cx); + promise->MaybeReject(cx, exn); + } + } + promise->MaybeResolve(cx, json); + return promise.forget(); + } + case CONSUME_TEXT: { + nsString decoded; + aRv = DecodeUTF8(buffer, decoded); + if (aRv.Failed()) { + return nullptr; + } + + promise->MaybeResolve(decoded); + return promise.forget(); + } + } + + NS_NOTREACHED("Unexpected consume body type"); + // Silence warnings. + return nullptr; +} + already_AddRefed<Promise> Request::ArrayBuffer(ErrorResult& aRv) { - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); - MOZ_ASSERT(global); - nsRefPtr<Promise> promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); - return promise.forget(); + return ConsumeBody(CONSUME_ARRAYBUFFER, aRv); } already_AddRefed<Promise> Request::Blob(ErrorResult& aRv) { - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); - MOZ_ASSERT(global); - nsRefPtr<Promise> promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); - return promise.forget(); + return ConsumeBody(CONSUME_BLOB, aRv); } already_AddRefed<Promise> Request::Json(ErrorResult& aRv) { - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); - MOZ_ASSERT(global); - nsRefPtr<Promise> promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); - return promise.forget(); + return ConsumeBody(CONSUME_JSON, aRv); } already_AddRefed<Promise> Request::Text(ErrorResult& aRv) { - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); - MOZ_ASSERT(global); - nsRefPtr<Promise> promise = Promise::Create(global, aRv); - if (aRv.Failed()) { - return nullptr; - } - - promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); - return promise.forget(); -} - -bool -Request::BodyUsed() -{ - return false; + return ConsumeBody(CONSUME_TEXT, aRv); } } // namespace dom } // namespace mozilla
--- a/dom/fetch/Request.h +++ b/dom/fetch/Request.h @@ -4,74 +4,82 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_Request_h #define mozilla_dom_Request_h #include "nsISupportsImpl.h" #include "nsWrapperCache.h" +#include "mozilla/dom/InternalRequest.h" +// Required here due to certain WebIDL enums/classes being declared in both +// files. #include "mozilla/dom/RequestBinding.h" #include "mozilla/dom/UnionTypes.h" - class nsPIDOMWindow; namespace mozilla { namespace dom { class Headers; class Promise; class Request MOZ_FINAL : public nsISupports , public nsWrapperCache { NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request) public: - Request(nsISupports* aOwner); + Request(nsIGlobalObject* aOwner, InternalRequest* aRequest); JSObject* WrapObject(JSContext* aCx) { return RequestBinding::Wrap(aCx, this); } void GetUrl(DOMString& aUrl) const { - aUrl.AsAString() = EmptyString(); + aUrl.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mURL); } void GetMethod(nsCString& aMethod) const { - aMethod = EmptyCString(); + aMethod = mRequest->mMethod; } RequestMode Mode() const { - return RequestMode::Same_origin; + return mRequest->mMode; } RequestCredentials Credentials() const { - return RequestCredentials::Omit; + return mRequest->mCredentialsMode; } void GetReferrer(DOMString& aReferrer) const { - aReferrer.AsAString() = EmptyString(); + if (mRequest->ReferrerIsNone()) { + aReferrer.AsAString() = EmptyString(); + return; + } + + // FIXME(nsm): Spec doesn't say what to do if referrer is client. + aReferrer.AsAString() = NS_ConvertUTF8toUTF16(mRequest->mReferrerURL); } - Headers* Headers_() const { return mHeaders; } + Headers* Headers_() const { return mRequest->Headers_(); } static already_AddRefed<Request> Constructor(const GlobalObject& aGlobal, const RequestOrScalarValueString& aInput, const RequestInit& aInit, ErrorResult& rv); nsISupports* GetParentObject() const { return mOwner; @@ -88,20 +96,46 @@ public: already_AddRefed<Promise> Json(ErrorResult& aRv); already_AddRefed<Promise> Text(ErrorResult& aRv); bool - BodyUsed(); + BodyUsed() const + { + return mBodyUsed; + } + + already_AddRefed<InternalRequest> + GetInternalRequest(); private: + enum ConsumeType + { + CONSUME_ARRAYBUFFER, + CONSUME_BLOB, + // FormData not supported right now, + CONSUME_JSON, + CONSUME_TEXT, + }; + ~Request(); - nsCOMPtr<nsISupports> mOwner; - nsRefPtr<Headers> mHeaders; + already_AddRefed<Promise> + ConsumeBody(ConsumeType aType, ErrorResult& aRv); + + void + SetBodyUsed() + { + mBodyUsed = true; + } + + nsCOMPtr<nsIGlobalObject> mOwner; + nsRefPtr<InternalRequest> mRequest; + bool mBodyUsed; + nsCString mMimeType; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_Request_h
--- a/dom/fetch/moz.build +++ b/dom/fetch/moz.build @@ -1,22 +1,26 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ + 'Fetch.h', 'Headers.h', + 'InternalRequest.h', 'Request.h', 'Response.h', ] UNIFIED_SOURCES += [ + 'Fetch.cpp', 'Headers.cpp', + 'InternalRequest.cpp', 'Request.cpp', 'Response.cpp', ] LOCAL_INCLUDES += [ '../workers', ]
new file mode 100644 --- /dev/null +++ b/dom/ipc/CPOWManagerGetter.h @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_CPOWManagerGetter_h +#define mozilla_dom_CPOWManagerGetter_h + +namespace mozilla { + +namespace jsipc { +class JavaScriptShared; +} /* namespace jsipc */ + +namespace dom { +class CPOWManagerGetter +{ +public: + virtual mozilla::jsipc::JavaScriptShared* GetCPOWManager() = 0; +}; +} /* namespace dom */ + +} /* namespace mozilla */ + +#endif /* mozilla_dom_CPOWManagerGetter_h */
--- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -93,17 +93,17 @@ ContentBridgeChild::SendPBrowserConstruc aID, aIsForApp, aIsForBrowser); } // This implementation is identical to ContentChild::GetCPOWManager but we can't // move it to nsIContentChild because it calls ManagedPJavaScriptChild() which // only exists in PContentChild and PContentBridgeChild. -jsipc::JavaScriptChild * +jsipc::JavaScriptShared* ContentBridgeChild::GetCPOWManager() { if (ManagedPJavaScriptChild().Length()) { return static_cast<JavaScriptChild*>(ManagedPJavaScriptChild()[0]); } JavaScriptChild* actor = static_cast<JavaScriptChild*>(SendPJavaScriptConstructor()); return actor; }
--- a/dom/ipc/ContentBridgeChild.h +++ b/dom/ipc/ContentBridgeChild.h @@ -31,17 +31,17 @@ public: const ClonedMessageData& aData, const InfallibleTArray<jsipc::CpowEntry>& aCpows, const IPC::Principal& aPrincipal) MOZ_OVERRIDE; virtual PBlobChild* SendPBlobConstructor(PBlobChild* actor, const BlobConstructorParams& params); - jsipc::JavaScriptChild* GetCPOWManager(); + jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; virtual bool SendPBrowserConstructor(PBrowserChild* aActor, const IPCTabContext& aContext, const uint32_t& aChromeFlags, const uint64_t& aID, const bool& aIsForApp, const bool& aIsForBrowser) MOZ_OVERRIDE;
--- a/dom/ipc/ContentBridgeParent.cpp +++ b/dom/ipc/ContentBridgeParent.cpp @@ -143,17 +143,17 @@ bool ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent) { return nsIContentParent::DeallocPBrowserParent(aParent); } // This implementation is identical to ContentParent::GetCPOWManager but we can't // move it to nsIContentParent because it calls ManagedPJavaScriptParent() which // only exists in PContentParent and PContentBridgeParent. -jsipc::JavaScriptParent* +jsipc::JavaScriptShared* ContentBridgeParent::GetCPOWManager() { if (ManagedPJavaScriptParent().Length()) { return static_cast<JavaScriptParent*>(ManagedPJavaScriptParent()[0]); } JavaScriptParent* actor = static_cast<JavaScriptParent*>(SendPJavaScriptConstructor()); return actor; }
--- a/dom/ipc/ContentBridgeParent.h +++ b/dom/ipc/ContentBridgeParent.h @@ -34,17 +34,17 @@ public: virtual PBrowserParent* SendPBrowserConstructor(PBrowserParent* aActor, const IPCTabContext& aContext, const uint32_t& aChromeFlags, const uint64_t& aID, const bool& aIsForApp, const bool& aIsForBrowser) MOZ_OVERRIDE; - jsipc::JavaScriptParent* GetCPOWManager(); + jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; virtual uint64_t ChildID() MOZ_OVERRIDE { return mChildID; } virtual bool IsForApp() MOZ_OVERRIDE { return mIsForApp;
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1241,17 +1241,17 @@ ContentChild::AllocPTestShellChild() bool ContentChild::DeallocPTestShellChild(PTestShellChild* shell) { delete shell; return true; } -jsipc::JavaScriptChild * +jsipc::JavaScriptShared* ContentChild::GetCPOWManager() { if (ManagedPJavaScriptChild().Length()) { return static_cast<JavaScriptChild*>(ManagedPJavaScriptChild()[0]); } JavaScriptChild* actor = static_cast<JavaScriptChild*>(SendPJavaScriptConstructor()); return actor; } @@ -1726,17 +1726,17 @@ bool ContentChild::RecvAsyncMessage(const nsString& aMsg, const ClonedMessageData& aData, const InfallibleTArray<CpowEntry>& aCpows, const IPC::Principal& aPrincipal) { nsRefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::sChildProcessManager; if (cpm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForChild(aData); - CpowIdHolder cpows(GetCPOWManager(), aCpows); + CpowIdHolder cpows(this, aCpows); cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); } return true; } bool ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -29,17 +29,17 @@ class RemoteSpellcheckEngineChild; namespace ipc { class OptionalURIParams; class PFileDescriptorSetChild; class URIParams; }// namespace ipc namespace jsipc { -class JavaScriptChild; +class JavaScriptShared; } namespace layers { class PCompositorChild; } // namespace layers namespace dom { @@ -189,17 +189,17 @@ public: virtual bool RecvDataStoreNotify(const uint32_t& aAppId, const nsString& aName, const nsString& aManifestURL) MOZ_OVERRIDE; virtual PTestShellChild* AllocPTestShellChild() MOZ_OVERRIDE; virtual bool DeallocPTestShellChild(PTestShellChild*) MOZ_OVERRIDE; virtual bool RecvPTestShellConstructor(PTestShellChild*) MOZ_OVERRIDE; - jsipc::JavaScriptChild *GetCPOWManager(); + jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; PMobileConnectionChild* SendPMobileConnectionConstructor(PMobileConnectionChild* aActor, const uint32_t& aClientId); virtual PMobileConnectionChild* AllocPMobileConnectionChild(const uint32_t& aClientId) MOZ_OVERRIDE; virtual bool DeallocPMobileConnectionChild(PMobileConnectionChild* aActor) MOZ_OVERRIDE;
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1772,17 +1772,17 @@ ContentParent::NotifyTabDestroyed(PBrows if (ManagedPBrowserParent().Length() == 1) { MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &ContentParent::ShutDownProcess, /* force */ false)); } } -jsipc::JavaScriptParent* +jsipc::JavaScriptShared* ContentParent::GetCPOWManager() { if (ManagedPJavaScriptParent().Length()) { return static_cast<JavaScriptParent*>(ManagedPJavaScriptParent()[0]); } return nullptr; }
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -40,17 +40,17 @@ class PRemoteSpellcheckEngineParent; namespace ipc { class OptionalURIParams; class PFileDescriptorSetParent; class URIParams; class TestShellParent; } // namespace ipc namespace jsipc { -class JavaScriptParent; +class JavaScriptShared; class PJavaScriptParent; } namespace layers { class PCompositorParent; class PSharedBufferManagerParent; } // namespace layers @@ -172,17 +172,17 @@ public: void NotifyTabDestroying(PBrowserParent* aTab); /** Notify that a tab was destroyed during normal operation. */ void NotifyTabDestroyed(PBrowserParent* aTab, bool aNotifiedDestroying); TestShellParent* CreateTestShell(); bool DestroyTestShell(TestShellParent* aTestShell); TestShellParent* GetTestShellSingleton(); - jsipc::JavaScriptParent *GetCPOWManager(); + jsipc::JavaScriptShared* GetCPOWManager() MOZ_OVERRIDE; void ReportChildAlreadyBlocked(); bool RequestRunToCompletion(); bool IsAlive(); virtual bool IsForApp() MOZ_OVERRIDE; virtual bool IsForBrowser() MOZ_OVERRIDE {
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -2569,17 +2569,17 @@ TabChild::RecvAsyncMessage(const nsStrin const InfallibleTArray<CpowEntry>& aCpows, const IPC::Principal& aPrincipal) { if (mTabChildGlobal) { nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal()); StructuredCloneData cloneData = UnpackClonedMessageDataForChild(aData); nsRefPtr<nsFrameMessageManager> mm = static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get()); - CpowIdHolder cpows(Manager()->GetCPOWManager(), aCpows); + CpowIdHolder cpows(Manager(), aCpows); mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), aMessage, false, &cloneData, &cpows, aPrincipal, nullptr); } return true; } class UnloadScriptEvent : public nsRunnable { @@ -2905,17 +2905,17 @@ TabChild::DoSendBlockingMessage(JSContex InfallibleTArray<nsString>* aJSONRetVal, bool aIsSync) { ClonedMessageData data; if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { return false; } InfallibleTArray<CpowEntry> cpows; - if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { return false; } if (aIsSync) { return SendSyncMessage(PromiseFlatString(aMessage), data, cpows, Principal(aPrincipal), aJSONRetVal); } return CallRpcMessage(PromiseFlatString(aMessage), data, cpows, @@ -2929,17 +2929,17 @@ TabChild::DoSendAsyncMessage(JSContext* JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal) { ClonedMessageData data; if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { return false; } InfallibleTArray<CpowEntry> cpows; - if (!Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { return false; } return SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, Principal(aPrincipal)); } TabChild* TabChild::GetFrom(nsIPresShell* aPresShell)
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1109,17 +1109,17 @@ TabParent::RecvSyncMessage(const nsStrin ContentParent* parent = Manager()->AsContentParent(); if (!ContentParent::IgnoreIPCPrincipal() && parent && principal && !AssertAppPrincipal(parent, principal)) { return false; } } StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(Manager()->GetCPOWManager(), aCpows); + CpowIdHolder cpows(Manager(), aCpows); return ReceiveMessage(aMessage, true, &cloneData, &cpows, aPrincipal, aJSONRetVal); } bool TabParent::AnswerRpcMessage(const nsString& aMessage, const ClonedMessageData& aData, const InfallibleTArray<CpowEntry>& aCpows, const IPC::Principal& aPrincipal, @@ -1131,17 +1131,17 @@ TabParent::AnswerRpcMessage(const nsStri ContentParent* parent = Manager()->AsContentParent(); if (!ContentParent::IgnoreIPCPrincipal() && parent && principal && !AssertAppPrincipal(parent, principal)) { return false; } } StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(Manager()->GetCPOWManager(), aCpows); + CpowIdHolder cpows(Manager(), aCpows); return ReceiveMessage(aMessage, true, &cloneData, &cpows, aPrincipal, aJSONRetVal); } bool TabParent::RecvAsyncMessage(const nsString& aMessage, const ClonedMessageData& aData, const InfallibleTArray<CpowEntry>& aCpows, const IPC::Principal& aPrincipal) @@ -1152,17 +1152,17 @@ TabParent::RecvAsyncMessage(const nsStri ContentParent* parent = Manager()->AsContentParent(); if (!ContentParent::IgnoreIPCPrincipal() && parent && principal && !AssertAppPrincipal(parent, principal)) { return false; } } StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(Manager()->GetCPOWManager(), aCpows); + CpowIdHolder cpows(Manager(), aCpows); return ReceiveMessage(aMessage, false, &cloneData, &cpows, aPrincipal, nullptr); } bool TabParent::RecvSetCursor(const uint32_t& aCursor, const bool& aForce) { nsCOMPtr<nsIWidget> widget = GetWidget(); if (widget) {
--- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -15,16 +15,17 @@ EXPORTS.mozilla.dom.ipc += [ ] EXPORTS.mozilla.dom += [ 'ContentBridgeChild.h', 'ContentBridgeParent.h', 'ContentChild.h', 'ContentParent.h', 'ContentProcess.h', + 'CPOWManagerGetter.h', 'CrashReporterChild.h', 'CrashReporterParent.h', 'FilePickerParent.h', 'nsIContentChild.h', 'nsIContentParent.h', 'PermissionMessageUtils.h', 'StructuredCloneUtils.h', 'TabChild.h',
--- a/dom/ipc/nsIContentChild.cpp +++ b/dom/ipc/nsIContentChild.cpp @@ -115,17 +115,17 @@ bool nsIContentChild::RecvAsyncMessage(const nsString& aMsg, const ClonedMessageData& aData, const InfallibleTArray<CpowEntry>& aCpows, const IPC::Principal& aPrincipal) { nsRefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::sChildProcessManager; if (cpm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForChild(aData); - CpowIdHolder cpows(GetCPOWManager(), aCpows); + CpowIdHolder cpows(this, aCpows); cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); } return true; } } // dom } // mozilla
--- a/dom/ipc/nsIContentChild.h +++ b/dom/ipc/nsIContentChild.h @@ -4,46 +4,47 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_nsIContentChild_h #define mozilla_dom_nsIContentChild_h #include "nsISupports.h" #include "nsTArrayForwardDeclare.h" +#include "mozilla/dom/CPOWManagerGetter.h" #define NS_ICONTENTCHILD_IID \ { 0x4eed2e73, 0x94ba, 0x48a8, \ { 0xa2, 0xd1, 0xa5, 0xed, 0x86, 0xd7, 0xbb, 0xe4 } } class nsIDOMBlob; class nsString; namespace IPC { class Principal; } // IPC namespace mozilla { namespace jsipc { class PJavaScriptChild; -class JavaScriptChild; class CpowEntry; } // jsipc namespace dom { class BlobChild; class BlobConstructorParams; class ClonedMessageData; class IPCTabContext; class PBlobChild; class PBrowserChild; class nsIContentChild : public nsISupports + , public CPOWManagerGetter { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTCHILD_IID) BlobChild* GetOrCreateActorForBlob(nsIDOMBlob* aBlob); virtual PBlobChild* SendPBlobConstructor( PBlobChild* aActor, @@ -51,17 +52,16 @@ public: virtual bool SendPBrowserConstructor(PBrowserChild* aActor, const IPCTabContext& aContext, const uint32_t& aChromeFlags, const uint64_t& aID, const bool& aIsForApp, const bool& aIsForBrowser) = 0; - virtual jsipc::JavaScriptChild* GetCPOWManager() = 0; protected: virtual jsipc::PJavaScriptChild* AllocPJavaScriptChild(); virtual bool DeallocPJavaScriptChild(jsipc::PJavaScriptChild*); virtual PBrowserChild* AllocPBrowserChild(const IPCTabContext& aContext, const uint32_t& aChromeFlags, const uint64_t& aID, const bool& aIsForApp,
--- a/dom/ipc/nsIContentParent.cpp +++ b/dom/ipc/nsIContentParent.cpp @@ -180,18 +180,17 @@ nsIContentParent::RecvSyncMessage(const parent && principal && !AssertAppPrincipal(parent, principal)) { return false; } } nsRefPtr<nsFrameMessageManager> ppm = mMessageManager; if (ppm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(GetCPOWManager(), aCpows); - + CpowIdHolder cpows(this, aCpows); ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMsg, true, &cloneData, &cpows, aPrincipal, aRetvals); } return true; } bool nsIContentParent::AnswerRpcMessage(const nsString& aMsg, @@ -208,17 +207,17 @@ nsIContentParent::AnswerRpcMessage(const parent && principal && !AssertAppPrincipal(parent, principal)) { return false; } } nsRefPtr<nsFrameMessageManager> ppm = mMessageManager; if (ppm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(GetCPOWManager(), aCpows); + CpowIdHolder cpows(this, aCpows); ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMsg, true, &cloneData, &cpows, aPrincipal, aRetvals); } return true; } bool nsIContentParent::RecvAsyncMessage(const nsString& aMsg, @@ -234,17 +233,17 @@ nsIContentParent::RecvAsyncMessage(const parent && principal && !AssertAppPrincipal(parent, principal)) { return false; } } nsRefPtr<nsFrameMessageManager> ppm = mMessageManager; if (ppm) { StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); - CpowIdHolder cpows(GetCPOWManager(), aCpows); + CpowIdHolder cpows(this, aCpows); ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); } return true; } } // namespace dom } // namespace mozilla
--- a/dom/ipc/nsIContentParent.h +++ b/dom/ipc/nsIContentParent.h @@ -4,47 +4,48 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_nsIContentParent_h #define mozilla_dom_nsIContentParent_h #include "nsFrameMessageManager.h" #include "nsISupports.h" +#include "mozilla/dom/CPOWManagerGetter.h" #define NS_ICONTENTPARENT_IID \ { 0xeeec9ebf, 0x8ecf, 0x4e38, \ { 0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 } } class nsFrameMessageManager; class nsIDOMBlob; namespace IPC { class Principal; } // namespace IPC namespace mozilla { namespace jsipc { class PJavaScriptParent; -class JavaScriptParent; class CpowEntry; } // namespace jsipc namespace dom { class BlobConstructorParams; class BlobParent; class ContentParent; class IPCTabContext; class PBlobParent; class PBrowserParent; class nsIContentParent : public nsISupports , public mozilla::dom::ipc::MessageManagerCallback + , public CPOWManagerGetter { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTPARENT_IID) nsIContentParent(); BlobParent* GetOrCreateActorForBlob(nsIDOMBlob* aBlob); @@ -59,18 +60,16 @@ public: virtual PBrowserParent* SendPBrowserConstructor( PBrowserParent* actor, const IPCTabContext& context, const uint32_t& chromeFlags, const uint64_t& aId, const bool& aIsForApp, const bool& aIsForBrowser) NS_WARN_UNUSED_RESULT = 0; - virtual jsipc::JavaScriptParent *GetCPOWManager() = 0; - virtual bool IsContentParent() { return false; } ContentParent* AsContentParent(); protected: // methods bool CanOpenBrowser(const IPCTabContext& aContext); protected: // IPDL methods virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent();
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -17,17 +17,17 @@ #include "mozilla/dom/BindingDeclarations.h" #include "nsCycleCollectionParticipant.h" #include "nsDataHashtable.h" #include "nsHashKeys.h" #include "nsRefPtrHashtable.h" #include "nsString.h" #include "nsTArray.h" #include "nsThreadUtils.h" -#include "StructuredCloneTags.h" +#include "mozilla/dom/StructuredCloneTags.h" #include "Queue.h" #include "WorkerFeature.h" class JSAutoStructuredCloneBuffer; class nsIChannel; class nsIDocument; class nsIEventTarget;
--- a/dom/workers/test/fetch/mochitest.ini +++ b/dom/workers/test/fetch/mochitest.ini @@ -1,5 +1,7 @@ [DEFAULT] support-files = worker_interfaces.js + worker_test_request.js [test_interfaces.html] +[test_request.html]
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fetch/test_request.html @@ -0,0 +1,48 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE HTML> +<html> +<head> + <title>Bug XXXXXX - Test Request object in worker</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script class="testbody" type="text/javascript"> + + function checkEnabled() { + var worker = new Worker("worker_test_request.js"); + worker.onmessage = function(event) { + + if (event.data.type == 'finish') { + SimpleTest.finish(); + } else if (event.data.type == 'status') { + ok(event.data.status, event.data.msg); + } + } + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.message + " at " + event.lineno); + SimpleTest.finish(); + }; + + worker.postMessage(true); + } + + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({"set": [ + ["dom.fetch.enabled", true] + ]}, function() { + checkEnabled(); + }); +</script> +</pre> +</body> +</html> +
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fetch/worker_test_request.js @@ -0,0 +1,215 @@ +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + " " + msg + "\n"); + postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +function testDefaultCtor() { + var req = new Request(""); + is(req.method, "GET", "Default Request method is GET"); + ok(req.headers instanceof Headers, "Request should have non-null Headers object"); + is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL"); + is(req.referrer, "", "Default referrer is `client` which serializes to empty string."); + is(req.mode, "cors", "Request mode for string input is cors"); + is(req.credentials, "omit", "Default Request credentials is omit"); + + var req = new Request(req); + is(req.method, "GET", "Default Request method is GET"); + ok(req.headers instanceof Headers, "Request should have non-null Headers object"); + is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL"); + is(req.referrer, "", "Default referrer is `client` which serializes to empty string."); + is(req.mode, "cors", "Request mode string input is cors"); + is(req.credentials, "omit", "Default Request credentials is omit"); +} + +function testClone() { + var req = (new Request("./cloned_request.txt", { + method: 'POST', + headers: { "Content-Length": 5 }, + body: "Sample body", + mode: "same-origin", + credentials: "same-origin", + })).clone(); + ok(req.method === "POST", "Request method is POST"); + ok(req.headers instanceof Headers, "Request should have non-null Headers object"); + is(req.headers.get('content-length'), "5", "Request content-length should be 5."); + ok(req.url === (new URL("./cloned_request.txt", self.location.href)).href, + "URL should be resolved with entry settings object's API base URL"); + ok(req.referrer === "", "Default referrer is `client` which serializes to empty string."); + ok(req.mode === "same-origin", "Request mode is same-origin"); + ok(req.credentials === "same-origin", "Default credentials is same-origin"); +} + +function testUsedRequest() { + // Passing a used request should fail. + var req = new Request("", { body: "This is foo" }); + var p1 = req.text().then(function(v) { + try { + var req2 = new Request(req); + ok(false, "Used Request cannot be passed to new Request"); + } catch(e) { + ok(true, "Used Request cannot be passed to new Request"); + } + }); + + // Passing a request should set the request as used. + var reqA = new Request("", { body: "This is foo" }); + var reqB = new Request(reqA); + is(reqA.bodyUsed, true, "Passing a Request to another Request should set the former as used"); + return p1; +} + +function testSimpleUrlParse() { + // Just checks that the URL parser is actually being used. + var req = new Request("/file.html"); + is(req.url, (new URL("/file.html", self.location.href)).href, "URL parser should be used to resolve Request URL"); +} + +function testMethod() { + var allowed = ["delete", "get", "head", "options", "post", "put"]; + for (var i = 0; i < allowed.length; ++i) { + try { + var r = new Request("", { method: allowed[i] }); + ok(true, "Method " + allowed[i] + " should be allowed"); + } catch(e) { + ok(false, "Method " + allowed[i] + " should be allowed"); + } + } + + var forbidden = ["aardvark", "connect", "trace", "track"]; + for (var i = 0; i < forbidden.length; ++i) { + try { + var r = new Request("", { method: forbidden[i] }); + ok(false, "Method " + forbidden[i] + " should be forbidden"); + } catch(e) { + ok(true, "Method " + forbidden[i] + " should be forbidden"); + } + } + + var allowedNoCors = ["get", "head", "post"]; + for (var i = 0; i < allowedNoCors.length; ++i) { + try { + var r = new Request("", { method: allowedNoCors[i], mode: "no-cors" }); + ok(true, "Method " + allowedNoCors[i] + " should be allowed in no-cors mode"); + } catch(e) { + ok(false, "Method " + allowedNoCors[i] + " should be allowed in no-cors mode"); + } + } + + var forbiddenNoCors = ["aardvark", "delete", "options", "put"]; + for (var i = 0; i < forbiddenNoCors.length; ++i) { + try { + var r = new Request("", { method: forbiddenNoCors[i], mode: "no-cors" }); + ok(false, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode"); + } catch(e) { + ok(true, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode"); + } + } +} + +function testUrlFragment() { + var req = new Request("./request#withfragment"); + ok(req.url, (new URL("./request", self.location.href)).href, "request.url should be serialized with exclude fragment flag set"); +} + +function testBodyUsed() { + var req = new Request("./bodyused", { body: "Sample body" }); + is(req.bodyUsed, false, "bodyUsed is initially false."); + return req.text().then((v) => { + is(v, "Sample body", "Body should match"); + is(req.bodyUsed, true, "After reading body, bodyUsed should be true."); + }).then((v) => { + return req.blob().then((v) => { + ok(false, "Attempting to read body again should fail."); + }, (e) => { + ok(true, "Attempting to read body again should fail."); + }) + }); +} + +// FIXME(nsm): Bug 1071290: We can't use Blobs as the body yet. +function testBodyCreation() { + var text = "κόσμε"; + var req1 = new Request("", { body: text }); + var p1 = req1.text().then(function(v) { + ok(typeof v === "string", "Should resolve to string"); + is(text, v, "Extracted string should match"); + }); + + var req2 = new Request("", { body: new Uint8Array([72, 101, 108, 108, 111]) }); + var p2 = req2.text().then(function(v) { + is("Hello", v, "Extracted string should match"); + }); + + var req2b = new Request("", { body: (new Uint8Array([72, 101, 108, 108, 111])).buffer }); + var p2b = req2b.text().then(function(v) { + is("Hello", v, "Extracted string should match"); + }); + + var params = new URLSearchParams(); + params.append("item", "Geckos"); + params.append("feature", "stickyfeet"); + params.append("quantity", "700"); + var req3 = new Request("", { body: params }); + var p3 = req3.text().then(function(v) { + var extracted = new URLSearchParams(v); + is(extracted.get("item"), "Geckos", "Param should match"); + is(extracted.get("feature"), "stickyfeet", "Param should match"); + is(extracted.get("quantity"), "700", "Param should match"); + }); + + return Promise.all([p1, p2, p2b, p3]); +} + +function testBodyExtraction() { + var text = "κόσμε"; + var newReq = function() { return new Request("", { body: text }); } + return newReq().text().then(function(v) { + ok(typeof v === "string", "Should resolve to string"); + is(text, v, "Extracted string should match"); + }).then(function() { + return newReq().blob().then(function(v) { + ok(v instanceof Blob, "Should resolve to Blob"); + var fs = new FileReaderSync(); + is(fs.readAsText(v), text, "Decoded Blob should match original"); + }); + }).then(function() { + return newReq().json().then(function(v) { + ok(false, "Invalid json should reject"); + }, function(e) { + ok(true, "Invalid json should reject"); + }) + }).then(function() { + return newReq().arrayBuffer().then(function(v) { + ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer"); + var dec = new TextDecoder(); + is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original"); + }); + }) +} + +onmessage = function() { + var done = function() { postMessage({ type: 'finish' }) } + + testDefaultCtor(); + testClone(); + testSimpleUrlParse(); + testUrlFragment(); + testMethod(); + + Promise.resolve() + .then(testBodyCreation) + .then(testBodyUsed) + .then(testBodyExtraction) + .then(testUsedRequest) + // Put more promise based tests here. + .then(done) + .catch(function(e) { + ok(false, "Some Request tests failed " + e); + done(); + }) +}
--- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -8,16 +8,17 @@ #include "Units.h" // for ScreenPoint #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/RefPtr.h" // for TemporaryRef, RefCounted #include "mozilla/gfx/Point.h" // for IntSize, Point #include "mozilla/gfx/Rect.h" // for Rect, IntRect #include "mozilla/gfx/Types.h" // for Float #include "mozilla/layers/CompositorTypes.h" // for DiagnosticTypes, etc +#include "mozilla/layers/FenceUtils.h" // for FenceHandle #include "mozilla/layers/LayersTypes.h" // for LayersBackend #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc #include "nsRegion.h" #include <vector> #include "mozilla/WidgetUtils.h" /** * Different elements of a web pages are rendered into separate "layers" before @@ -348,16 +349,21 @@ public: /** * Flush the current frame to the screen and tidy up. */ virtual void EndFrame() = 0; virtual void SetFBAcquireFence(Layer* aLayer) {} + virtual FenceHandle GetReleaseFence() + { + return FenceHandle(); + } + /** * Post-rendering stuff if the rendering is done outside of this Compositor * e.g., by Composer2D. * aTransform is the transform from user space to window space. */ virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) = 0; /**
--- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -353,17 +353,17 @@ public: * If the texture flags contain TextureFlags::DEALLOCATE_CLIENT, the destruction * will be synchronously coordinated with the compositor side, otherwise it * will be done asynchronously. */ void ForceRemove(); virtual void SetReleaseFenceHandle(FenceHandle aReleaseFenceHandle) { - mReleaseFenceHandle = aReleaseFenceHandle; + mReleaseFenceHandle.Merge(aReleaseFenceHandle); } const FenceHandle& GetReleaseFenceHandle() const { return mReleaseFenceHandle; } virtual void SetAcquireFenceHandle(FenceHandle aAcquireFenceHandle)
--- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -75,18 +75,16 @@ public: ~TextureParent(); bool Init(const SurfaceDescriptor& aSharedData, const TextureFlags& aFlags); void CompositorRecycle(); - void SendFenceHandleIfPresent(); - virtual bool RecvClientRecycle() MOZ_OVERRIDE; virtual bool RecvClearTextureHostSync() MOZ_OVERRIDE; virtual bool RecvRemoveTexture() MOZ_OVERRIDE; TextureHost* GetTextureHost() { return mTextureHost; } @@ -142,24 +140,16 @@ TextureHost::AsTextureHost(PTextureParen } PTextureParent* TextureHost::GetIPDLActor() { return mActor; } -// static -void -TextureHost::SendFenceHandleIfPresent(PTextureParent* actor) -{ - TextureParent* parent = static_cast<TextureParent*>(actor); - parent->SendFenceHandleIfPresent(); -} - FenceHandle TextureHost::GetAndResetReleaseFenceHandle() { #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 TextureHostOGL* hostOGL = this->AsHostOGL(); if (!hostOGL) { return FenceHandle(); } @@ -709,48 +699,25 @@ static void RecycleCallback(TextureHost* TextureParent* tp = reinterpret_cast<TextureParent*>(aClosure); tp->CompositorRecycle(); } void TextureParent::CompositorRecycle() { mTextureHost->ClearRecycleCallback(); - SendFenceHandleIfPresent(); if (mTextureHost->GetFlags() & TextureFlags::RECYCLE) { mozilla::unused << SendCompositorRecycle(); // Don't forget to prepare for the next reycle // if TextureClient request it. mWaitForClientRecycle = mTextureHost; } } -void -TextureParent::SendFenceHandleIfPresent() -{ -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 - if (mTextureHost) { - TextureHostOGL* hostOGL = mTextureHost->AsHostOGL(); - if (!hostOGL) { - return; - } - android::sp<android::Fence> fence = hostOGL->GetAndResetReleaseFence(); - if (fence.get() && fence->isValid()) { - // HWC might not provide Fence. - // In this case, HWC implicitly handles buffer's fence. - - FenceHandle handle = FenceHandle(fence); - RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(handle); - mCompositableManager->SendFenceHandle(tracker, this, handle); - } - } -#endif -} - bool TextureParent::RecvClientRecycle() { // This will allow the RecycleCallback to be called once the compositor // releases any external references to TextureHost. mTextureHost->SetRecycleCallback(RecycleCallback, this); if (!mWaitForClientRecycle) { RECYCLE_LOG("Not a recycable tile");
--- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -416,18 +416,16 @@ public: /** * Return a pointer to the IPDLActor. * * This is to be used with IPDL messages only. Do not store the returned * pointer. */ PTextureParent* GetIPDLActor(); - static void SendFenceHandleIfPresent(PTextureParent* actor); - FenceHandle GetAndResetReleaseFenceHandle(); /** * Specific to B2G's Composer2D * XXX - more doc here */ virtual LayerRenderState GetRenderState() {
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp +++ b/gfx/layers/ipc/CompositableTransactionParent.cpp @@ -157,45 +157,46 @@ CompositableParentManager::ReceiveCompos case CompositableOperation::TOpRemoveTexture: { const OpRemoveTexture& op = aEdit.get_OpRemoveTexture(); CompositableHost* compositable = AsCompositable(op); RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent()); MOZ_ASSERT(tex.get()); compositable->RemoveTextureHost(tex); // send FenceHandle if present. - TextureHost::SendFenceHandleIfPresent(op.textureParent()); + SendFenceHandleIfPresent(op.textureParent(), compositable); break; } case CompositableOperation::TOpRemoveTextureAsync: { const OpRemoveTextureAsync& op = aEdit.get_OpRemoveTextureAsync(); CompositableHost* compositable = AsCompositable(op); RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent()); MOZ_ASSERT(tex.get()); compositable->RemoveTextureHost(tex); if (!IsAsync() && GetChildProcessId()) { // send FenceHandle if present via ImageBridge. ImageBridgeParent::SendFenceHandleToTrackerIfPresent( GetChildProcessId(), op.holderId(), op.transactionId(), - op.textureParent()); + op.textureParent(), + compositable); // If the message is recievied via PLayerTransaction, // Send message back via PImageBridge. ImageBridgeParent::ReplyRemoveTexture( GetChildProcessId(), OpReplyRemoveTexture(true, // isMain op.holderId(), op.transactionId())); } else { // send FenceHandle if present. - TextureHost::SendFenceHandleIfPresent(op.textureParent()); + SendFenceHandleIfPresent(op.textureParent(), compositable); ReplyRemoveTexture(OpReplyRemoveTexture(false, // isMain op.holderId(), op.transactionId())); } break; } case CompositableOperation::TOpUseTexture: { @@ -253,11 +254,47 @@ CompositableParentManager::ReceiveCompos default: { MOZ_ASSERT(false, "bad type"); } } return true; } +void +CompositableParentManager::SendPendingAsyncMessges() +{ + if (mPendingAsyncMessage.empty()) { + return; + } + + // Some type of AsyncParentMessageData message could have + // one file descriptor (e.g. OpDeliverFence). + // A number of file descriptors per gecko ipc message have a limitation + // on OS_POSIX (MACOSX or LINUX). +#if defined(OS_POSIX) + static const uint32_t kMaxMessageNumber = FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; +#else + // default number that works everywhere else + static const uint32_t kMaxMessageNumber = 250; +#endif + + InfallibleTArray<AsyncParentMessageData> messages; + messages.SetCapacity(mPendingAsyncMessage.size()); + for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) { + messages.AppendElement(mPendingAsyncMessage[i]); + // Limit maximum number of messages. + if (messages.Length() >= kMaxMessageNumber) { + SendAsyncMessage(messages); + // Initialize Messages. + messages.Clear(); + } + } + + if (messages.Length() > 0) { + SendAsyncMessage(messages); + } + mPendingAsyncMessage.clear(); +} + } // namespace } // namespace
--- a/gfx/layers/ipc/CompositableTransactionParent.h +++ b/gfx/layers/ipc/CompositableTransactionParent.h @@ -25,22 +25,27 @@ typedef std::vector<mozilla::layers::Edi // Since PCompositble has two potential manager protocols, we can't just call // the Manager() method usually generated when there's one manager protocol, // so both manager protocols implement this and we keep a reference to them // through this interface. class CompositableParentManager : public ISurfaceAllocator , public AsyncTransactionTrackersHolder { public: + virtual void SendFenceHandleIfPresent(PTextureParent* aTexture, + CompositableHost* aCompositableHost) = 0; + virtual void SendFenceHandle(AsyncTransactionTracker* aTracker, PTextureParent* aTexture, const FenceHandle& aFence) = 0; virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) = 0; + void SendPendingAsyncMessges(); + /** * Get child side's process Id. */ virtual base::ProcessId GetChildProcessId() = 0; protected: /** * Handle the IPDL messages that affect PCompositable actors. @@ -52,14 +57,15 @@ protected: /** * Return true if this protocol is asynchronous with respect to the content * thread (ImageBridge for instance). */ virtual bool IsAsync() const { return false; } virtual void ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) {} + std::vector<AsyncParentMessageData> mPendingAsyncMessage; }; } // namespace } // namespace #endif
--- a/gfx/layers/ipc/FenceUtils.h +++ b/gfx/layers/ipc/FenceUtils.h @@ -21,16 +21,17 @@ namespace layers { struct FenceHandleFromChild; struct FenceHandle { FenceHandle() {} explicit FenceHandle(const FenceHandleFromChild& aFenceHandle) {} bool operator==(const FenceHandle&) const { return false; } bool IsValid() const { return false; } + void Merge(const FenceHandle& aFenceHandle) {} }; struct FenceHandleFromChild { FenceHandleFromChild() {} explicit FenceHandleFromChild(const FenceHandle& aFence) {} bool operator==(const FenceHandle&) const { return false; } bool operator==(const FenceHandleFromChild&) const { return false; } bool IsValid() const { return false; }
--- a/gfx/layers/ipc/FenceUtilsGonk.cpp +++ b/gfx/layers/ipc/FenceUtilsGonk.cpp @@ -44,38 +44,45 @@ ParamTraits<FenceHandle>::Write(Message* // which returned by getFlattenedSize() and getFdCount() are not changed. // So we change nbytes and nfds back by call corresponding calls. nbytes = flattenable->getFlattenedSize(); nfds = flattenable->getFdCount(); #else flattenable->flatten(data, nbytes, fds, nfds); #endif aMsg->WriteSize(nbytes); + aMsg->WriteSize(nfds); aMsg->WriteBytes(data, nbytes); for (size_t n = 0; n < nfds; ++n) { // These buffers can't die in transit because they're created // synchonously and the parent-side buffer can only be dropped if // there's a crash. aMsg->WriteFileDescriptor(FileDescriptor(fds[n], false)); } } bool ParamTraits<FenceHandle>::Read(const Message* aMsg, void** aIter, paramType* aResult) { size_t nbytes; + size_t nfds; const char* data; if (!aMsg->ReadSize(aIter, &nbytes) || + !aMsg->ReadSize(aIter, &nfds) || !aMsg->ReadBytes(aIter, &data, nbytes)) { return false; } - size_t nfds = aMsg->num_fds(); + // Check if nfds is correct. + // aMsg->num_fds() could include fds of another ParamTraits<>s. + if (nfds > aMsg->num_fds()) { + return false; + } int fds[nfds]; for (size_t n = 0; n < nfds; ++n) { FileDescriptor fd; if (!aMsg->ReadFileDescriptor(aIter, &fd)) { return false; } // If the GraphicBuffer was shared cross-process, SCM_RIGHTS does @@ -133,16 +140,17 @@ ParamTraits<FenceHandleFromChild>::Write // which returned by getFlattenedSize() and getFdCount() are not changed. // So we change nbytes and nfds back by call corresponding calls. nbytes = flattenable->getFlattenedSize(); nfds = flattenable->getFdCount(); #else flattenable->flatten(data, nbytes, fds, nfds); #endif aMsg->WriteSize(nbytes); + aMsg->WriteSize(nfds); aMsg->WriteBytes(data, nbytes); for (size_t n = 0; n < nfds; ++n) { // If the Fence was shared cross-process, SCM_RIGHTS does // the right thing and dup's the fd. If it's shared cross-thread, // SCM_RIGHTS doesn't dup the fd. That's surprising, but we just // deal with it here. NB: only the "default" (master) process can // alloc gralloc buffers. bool sameProcess = (XRE_GetProcessType() == GeckoProcessType_Default); @@ -156,24 +164,30 @@ ParamTraits<FenceHandleFromChild>::Write } } bool ParamTraits<FenceHandleFromChild>::Read(const Message* aMsg, void** aIter, paramType* aResult) { size_t nbytes; + size_t nfds; const char* data; if (!aMsg->ReadSize(aIter, &nbytes) || + !aMsg->ReadSize(aIter, &nfds) || !aMsg->ReadBytes(aIter, &data, nbytes)) { return false; } - size_t nfds = aMsg->num_fds(); + // Check if nfds is correct. + // aMsg->num_fds() could include fds of another ParamTraits<>s. + if (nfds > aMsg->num_fds()) { + return false; + } int fds[nfds]; for (size_t n = 0; n < nfds; ++n) { FileDescriptor fd; if (!aMsg->ReadFileDescriptor(aIter, &fd)) { return false; } fds[n] = fd.fd; @@ -206,15 +220,39 @@ FenceHandle::FenceHandle(const sp<Fence> : mFence(aFence) { } FenceHandle::FenceHandle(const FenceHandleFromChild& aFenceHandle) { mFence = aFenceHandle.mFence; } +void +FenceHandle::Merge(const FenceHandle& aFenceHandle) +{ + if (!aFenceHandle.IsValid()) { + return; + } + + if (!IsValid()) { + mFence = aFenceHandle.mFence; + } else { + android::sp<android::Fence> mergedFence = android::Fence::merge( + android::String8::format("FenceHandle"), + mFence, aFenceHandle.mFence); + if (!mergedFence.get()) { + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union. + // This error handling is same as android::ConsumerBase does. + mFence = aFenceHandle.mFence; + return; + } + mFence = mergedFence; + } +} + FenceHandleFromChild::FenceHandleFromChild(const sp<Fence>& aFence) : mFence(aFence) { } } // namespace layers } // namespace mozilla
--- a/gfx/layers/ipc/FenceUtilsGonk.h +++ b/gfx/layers/ipc/FenceUtilsGonk.h @@ -18,40 +18,42 @@ namespace layers { struct FenceHandleFromChild; struct FenceHandle { typedef android::Fence Fence; FenceHandle() { } - FenceHandle(const android::sp<Fence>& aFence); + explicit FenceHandle(const android::sp<Fence>& aFence); - FenceHandle(const FenceHandleFromChild& aFenceHandle); + explicit FenceHandle(const FenceHandleFromChild& aFenceHandle); bool operator==(const FenceHandle& aOther) const { return mFence.get() == aOther.mFence.get(); } bool IsValid() const { return mFence.get() && mFence->isValid(); } + void Merge(const FenceHandle& aFenceHandle); + android::sp<Fence> mFence; }; struct FenceHandleFromChild { typedef android::Fence Fence; FenceHandleFromChild() { } - FenceHandleFromChild(const android::sp<Fence>& aFence); + explicit FenceHandleFromChild(const android::sp<Fence>& aFence); - FenceHandleFromChild(const FenceHandle& aFence) { + explicit FenceHandleFromChild(const FenceHandle& aFence) { mFence = aFence.mFence; } bool operator==(const FenceHandle& aOther) const { return mFence.get() == aOther.mFence.get(); } bool operator==(const FenceHandleFromChild& aOther) const {
--- a/gfx/layers/ipc/ImageBridgeParent.cpp +++ b/gfx/layers/ipc/ImageBridgeParent.cpp @@ -91,19 +91,35 @@ ImageBridgeParent::GetCompositorBackendT void ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy)); } +class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender +{ +public: + explicit AutoImageBridgeParentAsyncMessageSender(ImageBridgeParent* aImageBridge) + : mImageBridge(aImageBridge) {} + + ~AutoImageBridgeParentAsyncMessageSender() + { + mImageBridge->SendPendingAsyncMessges(); + } +private: + ImageBridgeParent* mImageBridge; +}; + bool ImageBridgeParent::RecvUpdate(const EditArray& aEdits, EditReplyArray* aReply) { + AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this); + // If we don't actually have a compositor, then don't bother // creating any textures. if (Compositor::GetBackend() == LayersBackend::LAYERS_NONE) { return true; } EditReplyVector replyv; for (EditArray::index_type i = 0; i < aEdits.Length(); ++i) { @@ -331,66 +347,121 @@ ImageBridgeParent::CloneToplevel(const I bool ImageBridgeParent::IsSameProcess() const { return OtherProcess() == ipc::kInvalidProcessHandle; } void ImageBridgeParent::ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) { - InfallibleTArray<AsyncParentMessageData> messages; - messages.AppendElement(aReply); - mozilla::unused << SendParentAsyncMessages(messages); + mPendingAsyncMessage.push_back(aReply); } /*static*/ void ImageBridgeParent::ReplyRemoveTexture(base::ProcessId aChildProcessId, const OpReplyRemoveTexture& aReply) { ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(aChildProcessId); if (!imageBridge) { return; } imageBridge->ReplyRemoveTexture(aReply); } -/*static*/ void -ImageBridgeParent::SendFenceHandleToTrackerIfPresent(uint64_t aDestHolderId, - uint64_t aTransactionId, - PTextureParent* aTexture) +void +ImageBridgeParent::SendFenceHandleIfPresent(PTextureParent* aTexture, + CompositableHost* aCompositableHost) { RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); if (!texture) { return; } + + // Send a ReleaseFence of CompositorOGL. + if (aCompositableHost && aCompositableHost->GetCompositor()) { + FenceHandle fence = aCompositableHost->GetCompositor()->GetReleaseFence(); + if (fence.IsValid()) { + RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); + HoldUntilComplete(tracker); + mPendingAsyncMessage.push_back(OpDeliverFence(tracker->GetId(), + aTexture, nullptr, + fence)); + } + } + + // Send a ReleaseFence that is set by HwcComposer2D. FenceHandle fence = texture->GetAndResetReleaseFenceHandle(); - if (!fence.IsValid()) { + if (fence.IsValid()) { + RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); + HoldUntilComplete(tracker); + mPendingAsyncMessage.push_back(OpDeliverFence(tracker->GetId(), + aTexture, nullptr, + fence)); + } +} + +void +ImageBridgeParent::SendFenceHandleToTrackerIfPresent(uint64_t aDestHolderId, + uint64_t aTransactionId, + PTextureParent* aTexture, + CompositableHost* aCompositableHost) +{ + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { return; } - RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); - HoldUntilComplete(tracker); - InfallibleTArray<AsyncParentMessageData> messages; - messages.AppendElement(OpDeliverFenceToTracker(tracker->GetId(), - aDestHolderId, - aTransactionId, - fence)); - mozilla::unused << SendParentAsyncMessages(messages); + // Send a ReleaseFence of CompositorOGL. + if (aCompositableHost && aCompositableHost->GetCompositor()) { + FenceHandle fence = aCompositableHost->GetCompositor()->GetReleaseFence(); + if (fence.IsValid()) { + RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); + HoldUntilComplete(tracker); + mPendingAsyncMessage.push_back(OpDeliverFenceToTracker(tracker->GetId(), + aDestHolderId, + aTransactionId, + fence)); + } + } + + // Send a ReleaseFence that is set by HwcComposer2D. + FenceHandle fence = texture->GetAndResetReleaseFenceHandle(); + if (fence.IsValid()) { + RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); + HoldUntilComplete(tracker); + mPendingAsyncMessage.push_back(OpDeliverFenceToTracker(tracker->GetId(), + aDestHolderId, + aTransactionId, + fence)); + } } /*static*/ void ImageBridgeParent::SendFenceHandleToTrackerIfPresent(base::ProcessId aChildProcessId, uint64_t aDestHolderId, uint64_t aTransactionId, - PTextureParent* aTexture) + PTextureParent* aTexture, + CompositableHost* aCompositableHost) { ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(aChildProcessId); if (!imageBridge) { return; } imageBridge->SendFenceHandleToTrackerIfPresent(aDestHolderId, aTransactionId, - aTexture); + aTexture, + aCompositableHost); } +/*static*/ void +ImageBridgeParent::SendPendingAsyncMessges(base::ProcessId aChildProcessId) +{ +#ifdef MOZ_WIDGET_GONK + ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(aChildProcessId); + if (!imageBridge) { + return; + } + imageBridge->SendPendingAsyncMessges(); +#endif +} } // layers } // mozilla
--- a/gfx/layers/ipc/ImageBridgeParent.h +++ b/gfx/layers/ipc/ImageBridgeParent.h @@ -51,16 +51,19 @@ public: virtual LayersBackend GetCompositorBackendType() const MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; static PImageBridgeParent* Create(Transport* aTransport, ProcessId aChildProcessId); // CompositableParentManager + virtual void SendFenceHandleIfPresent(PTextureParent* aTexture, + CompositableHost* aCompositableHost) MOZ_OVERRIDE; + virtual void SendFenceHandle(AsyncTransactionTracker* aTracker, PTextureParent* aTexture, const FenceHandle& aFence) MOZ_OVERRIDE; virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) MOZ_OVERRIDE; virtual base::ProcessId GetChildProcessId() MOZ_OVERRIDE { @@ -117,22 +120,27 @@ public: virtual void ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) MOZ_OVERRIDE; static void ReplyRemoveTexture(base::ProcessId aChildProcessId, const OpReplyRemoveTexture& aReply); void SendFenceHandleToTrackerIfPresent(uint64_t aDestHolderId, uint64_t aTransactionId, - PTextureParent* aTexture); + PTextureParent* aTexture, + CompositableHost* aCompositableHost); static void SendFenceHandleToTrackerIfPresent(base::ProcessId aChildProcessId, uint64_t aDestHolderId, uint64_t aTransactionId, - PTextureParent* aTexture); + PTextureParent* aTexture, + CompositableHost* aCompositableHost); + + using CompositableParentManager::SendPendingAsyncMessges; + static void SendPendingAsyncMessges(base::ProcessId aChildProcessId); static ImageBridgeParent* GetInstance(ProcessId aId); // Overriden from IToplevelProtocol IToplevelProtocol* CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds, base::ProcessHandle aPeerProcess, mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
--- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -13,16 +13,17 @@ #include "ShadowLayerParent.h" // for ShadowLayerParent #include "CompositableTransactionParent.h" // for EditReplyVector #include "ShadowLayersManager.h" // for ShadowLayersManager #include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D #include "mozilla/layers/CanvasLayerComposite.h" #include "mozilla/layers/ColorLayerComposite.h" #include "mozilla/layers/Compositor.h" // for Compositor #include "mozilla/layers/ContainerLayerComposite.h" +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent #include "mozilla/layers/ImageLayerComposite.h" #include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/LayersMessages.h" // for EditReply, etc #include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent #include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG #include "mozilla/layers/PCompositableParent.h" #include "mozilla/layers/PLayerParent.h" // for PLayerParent #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL @@ -187,16 +188,31 @@ LayerTransactionParent::RecvUpdateNoSwap const bool& isRepeatTransaction, const mozilla::TimeStamp& aTransactionStart) { return RecvUpdate(cset, aTransactionId, targetConfig, isFirstPaint, scheduleComposite, paintSequenceNumber, isRepeatTransaction, aTransactionStart, nullptr); } +class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender +{ +public: + explicit AutoLayerTransactionParentAsyncMessageSender(LayerTransactionParent* aLayerTransaction) + : mLayerTransaction(aLayerTransaction) {} + + ~AutoLayerTransactionParentAsyncMessageSender() + { + mLayerTransaction->SendPendingAsyncMessges(); + ImageBridgeParent::SendPendingAsyncMessges(mLayerTransaction->GetChildProcessId()); + } +private: + LayerTransactionParent* mLayerTransaction; +}; + bool LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset, const uint64_t& aTransactionId, const TargetConfig& targetConfig, const bool& isFirstPaint, const bool& scheduleComposite, const uint32_t& paintSequenceNumber, const bool& isRepeatTransaction, @@ -218,16 +234,17 @@ LayerTransactionParent::RecvUpdate(const } if (mLayerManager && mLayerManager->GetCompositor() && !targetConfig.naturalBounds().IsEmpty()) { mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation()); } EditReplyVector replyv; + AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(this); { AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this)); layer_manager()->BeginTransaction(); } for (EditArray::index_type i = 0; i < cset.Length(); ++i) { const Edit& edit = cset[i]; @@ -886,16 +903,48 @@ LayerTransactionParent::ActorDestroy(Act } bool LayerTransactionParent::IsSameProcess() const { return OtherProcess() == ipc::kInvalidProcessHandle; } void +LayerTransactionParent::SendFenceHandleIfPresent(PTextureParent* aTexture, + CompositableHost* aCompositableHost) +{ + RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture); + if (!texture) { + return; + } + + // Send a ReleaseFence of CompositorOGL. + if (aCompositableHost && aCompositableHost->GetCompositor()) { + FenceHandle fence = aCompositableHost->GetCompositor()->GetReleaseFence(); + if (fence.IsValid()) { + RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); + HoldUntilComplete(tracker); + mPendingAsyncMessage.push_back(OpDeliverFence(tracker->GetId(), + aTexture, nullptr, + fence)); + } + } + + // Send a ReleaseFence that is set by HwcComposer2D. + FenceHandle fence = texture->GetAndResetReleaseFenceHandle(); + if (fence.IsValid()) { + RefPtr<FenceDeliveryTracker> tracker = new FenceDeliveryTracker(fence); + HoldUntilComplete(tracker); + mPendingAsyncMessage.push_back(OpDeliverFence(tracker->GetId(), + aTexture, nullptr, + fence)); + } +} + +void LayerTransactionParent::SendFenceHandle(AsyncTransactionTracker* aTracker, PTextureParent* aTexture, const FenceHandle& aFence) { HoldUntilComplete(aTracker); InfallibleTArray<AsyncParentMessageData> messages; messages.AppendElement(OpDeliverFence(aTracker->GetId(), aTexture, nullptr,
--- a/gfx/layers/ipc/LayerTransactionParent.h +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -81,16 +81,19 @@ public: virtual LayersBackend GetCompositorBackendType() const MOZ_OVERRIDE; virtual bool IsSameProcess() const MOZ_OVERRIDE; const uint64_t& GetPendingTransactionId() { return mPendingTransaction; } void SetPendingTransactionId(uint64_t aId) { mPendingTransaction = aId; } // CompositableParentManager + virtual void SendFenceHandleIfPresent(PTextureParent* aTexture, + CompositableHost* aCompositableHost) MOZ_OVERRIDE; + virtual void SendFenceHandle(AsyncTransactionTracker* aTracker, PTextureParent* aTexture, const FenceHandle& aFence) MOZ_OVERRIDE; virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) MOZ_OVERRIDE; virtual base::ProcessId GetChildProcessId() MOZ_OVERRIDE {
--- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -1393,59 +1393,43 @@ CompositorOGL::SetFBAcquireFence(Layer* // FBAcquireFence is FramebufferSurface's AcquireFence. // AcquireFence will be signaled when a buffer's content is available. // See Bug 974152. if (!aLayer) { return; } - const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion(); - if (visibleRegion.IsEmpty()) { - return; + android::sp<android::Fence> fence = new android::Fence(GetGonkDisplay()->GetPrevFBAcquireFd()); + if (fence.get() && fence->isValid()) { + FenceHandle handle = FenceHandle(fence); + mReleaseFenceHandle.Merge(handle); } - - // Set FBAcquireFence on ContainerLayer's childs - ContainerLayer* container = aLayer->AsContainerLayer(); - if (container) { - for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { - SetFBAcquireFence(child); - } - return; - } +} - // Set FBAcquireFence as tiles' ReleaseFence on TiledLayerComposer. - TiledLayerComposer* composer = nullptr; - LayerComposite* shadow = aLayer->AsLayerComposite(); - // Only ask for the composer if we have a compositable host. Timing - // may make it so that we don't - see bug 1000634. - if (shadow && shadow->GetCompositableHost()) { - composer = shadow->GetTiledLayerComposer(); - if (composer) { - composer->SetReleaseFence(new android::Fence(GetGonkDisplay()->GetPrevFBAcquireFd())); - return; - } +FenceHandle +CompositorOGL::GetReleaseFence() +{ + if (!mReleaseFenceHandle.IsValid()) { + return FenceHandle(); } + return FenceHandle(new android::Fence(mReleaseFenceHandle.mFence->dup())); +} - // Set FBAcquireFence as layer buffer's ReleaseFence - LayerRenderState state = aLayer->GetRenderState(); - if (!state.mTexture) { - return; - } - TextureHostOGL* texture = state.mTexture->AsHostOGL(); - if (!texture) { - return; - } - texture->SetReleaseFence(new android::Fence(GetGonkDisplay()->GetPrevFBAcquireFd())); -} #else void CompositorOGL::SetFBAcquireFence(Layer* aLayer) { } + +FenceHandle +CompositorOGL::GetReleaseFence() +{ + return FenceHandle(); +} #endif void CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform) { // This lets us reftest and screenshot content rendered externally if (mTarget) { MakeCurrent();
--- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -204,16 +204,17 @@ public: virtual void DrawQuad(const gfx::Rect& aRect, const gfx::Rect& aClipRect, const EffectChain &aEffectChain, gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE; virtual void EndFrame() MOZ_OVERRIDE; virtual void SetFBAcquireFence(Layer* aLayer) MOZ_OVERRIDE; + virtual FenceHandle GetReleaseFence() MOZ_OVERRIDE; virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) MOZ_OVERRIDE; virtual void AbortFrame() MOZ_OVERRIDE; virtual bool SupportsPartialTextureUpdate() MOZ_OVERRIDE; virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) MOZ_OVERRIDE { if (!mGLContext) @@ -388,14 +389,16 @@ private: bool mDestroyed; /** * Height of the OpenGL context's primary framebuffer in pixels. Used by * FlipY for the y-flipping calculation. */ GLint mHeight; + + FenceHandle mReleaseFenceHandle; }; } } #endif /* MOZILLA_GFX_COMPOSITOROGL_H */
--- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -2,16 +2,17 @@ * vim: set ts=4 sw=4 et tw=80: * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "JavaScriptShared.h" #include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/CPOWManagerGetter.h" #include "mozilla/dom/TabChild.h" #include "jsfriendapi.h" #include "xpcprivate.h" #include "WrapperFactory.h" #include "mozilla/Preferences.h" using namespace js; using namespace JS; @@ -510,19 +511,31 @@ JavaScriptShared::toDescriptor(JSContext out.setSetter(JS_StrictPropertyStub); else out.setSetter(UnknownStrictPropertyStub); } return true; } +CpowIdHolder::CpowIdHolder(dom::CPOWManagerGetter *managerGetter, const InfallibleTArray<CpowEntry> &cpows) + : js_(nullptr), + cpows_(cpows) +{ + // Only instantiate the CPOW manager if we might need it later. + if (cpows.Length()) + js_ = managerGetter->GetCPOWManager(); +} + bool CpowIdHolder::ToObject(JSContext *cx, JS::MutableHandleObject objp) { + if (!cpows_.Length()) + return true; + return js_->Unwrap(cx, cpows_, objp); } bool JavaScriptShared::Unwrap(JSContext *cx, const InfallibleTArray<CpowEntry> &aCpows, JS::MutableHandleObject objp) { objp.set(nullptr);
--- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -9,16 +9,21 @@ #define mozilla_jsipc_JavaScriptShared_h__ #include "mozilla/dom/DOMTypes.h" #include "mozilla/jsipc/PJavaScript.h" #include "nsJSUtils.h" #include "nsFrameMessageManager.h" namespace mozilla { + +namespace dom { +class CPOWManagerGetter; +} + namespace jsipc { class ObjectId { public: // Use 47 bits at most, to be safe, since jsval privates are encoded as // doubles. See bug 1065811 comment 12 for an explanation. static const size_t SERIAL_NUMBER_BITS = 47; static const size_t FLAG_BITS = 1; @@ -58,21 +63,17 @@ class ObjectId { bool hasXrayWaiver_ : 1; }; class JavaScriptShared; class CpowIdHolder : public CpowHolder { public: - CpowIdHolder(JavaScriptShared *js, const InfallibleTArray<CpowEntry> &cpows) - : js_(js), - cpows_(cpows) - { - } + CpowIdHolder(dom::CPOWManagerGetter *managerGetter, const InfallibleTArray<CpowEntry> &cpows); bool ToObject(JSContext *cx, JS::MutableHandleObject objp); private: JavaScriptShared *js_; const InfallibleTArray<CpowEntry> &cpows_; };
--- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -157,20 +157,16 @@ class RemoteOptions(ReftestOptions): f.close() # httpd-path is specified by standard makefile targets and may be specified # on the command line to select a particular version of httpd.js. If not # specified, try to select the one from hostutils.zip, as required in bug 882932. if not options.httpdPath: options.httpdPath = os.path.join(options.utilityPath, "components") - # Android does not run leak tests, but set some reasonable defaults to avoid errors. - options.leakThresholds = {} - options.ignoreMissingLeaks = [] - # TODO: Copied from main, but I think these are no longer used in a post xulrunner world #options.xrePath = options.remoteTestRoot + self.automation._product + '/xulrunner' #options.utilityPath = options.testRoot + self.automation._product + '/bin' return options class ReftestServer: """ Web server used to serve Reftests, for closer fidelity to the real web. It is virtually identical to the server used in mochitest and will only
--- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -339,17 +339,17 @@ class RefTest(object): cmdlineArgs, utilityPath = options.utilityPath, xrePath=options.xrePath, debuggerInfo=debuggerInfo, symbolsPath=options.symbolsPath, # give the JS harness 30 seconds to deal # with its own timeouts timeout=options.timeout + 30.0) - processLeakLog(self.leakLogFile, options.leakThresholds, options.ignoreMissingLeaks) + processLeakLog(self.leakLogFile, options) self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.") finally: self.cleanup(profileDir) return status def copyExtraFilesToProfile(self, options, profile): "Copy extra files or dirs specified on the command line to the testing profile." profileDir = profile.profile @@ -507,17 +507,16 @@ class ReftestOptions(OptionParser): if options.totalChunks is not None and options.thisChunk is None: self.error("cannot specify thisChunk or totalChunks with parallel tests") if options.focusFilterMode != "all": self.error("cannot specify focusFilterMode with parallel tests") if options.debugger is not None: self.error("cannot specify a debugger with parallel tests") options.leakThresholds = {"default": options.defaultLeakThreshold} - options.ignoreMissingLeaks = [] return options def main(): automation = Automation() parser = ReftestOptions(automation) reftest = RefTest(automation)
--- a/layout/tools/reftest/runreftestb2g.py +++ b/layout/tools/reftest/runreftestb2g.py @@ -198,20 +198,16 @@ class B2GOptions(ReftestOptions): f.close() # httpd-path is specified by standard makefile targets and may be specified # on the command line to select a particular version of httpd.js. If not # specified, try to select the one from from the xre bundle, as required in bug 882932. if not options.httpdPath: options.httpdPath = os.path.join(options.xrePath, "components") - # B2G reftests do not do leak checking, but set some reasonable defaults to avoid errors. - options.leakThresholds = {} - options.ignoreMissingLeaks = [] - return options class ProfileConfigParser(ConfigParser.RawConfigParser): """Subclass of RawConfigParser that outputs .ini files in the exact format expected for profiles.ini, which is slightly different than the default format. """
--- a/mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_action_bar_button.xml +++ b/mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_action_bar_button.xml @@ -8,32 +8,32 @@ <item android:state_pressed="true" android:state_enabled="true"> <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical" android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical" android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal" android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"> <shape android:shape="rectangle"> - <solid android:color="#D7D7DC"/> + <solid android:color="@color/new_tablet_highlight"/> <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/> </shape> </inset> </item> <item android:state_focused="true" android:state_pressed="false"> <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical" android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical" android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal" android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"> <shape android:shape="rectangle"> - <solid android:color="#C0C9D0"/> + <solid android:color="@color/new_tablet_highlight_focused"/> <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/> </shape> </inset> </item> <item> <shape android:shape="rectangle">
new file mode 100644 --- /dev/null +++ b/mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_url_bar_nav_button.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:gecko="http://schemas.android.com/apk/res-auto"> + + <!-- private pressed state --> + <item gecko:state_private="true" + android:state_pressed="true" + android:drawable="@color/new_tablet_highlight_pb"/> + + <!-- focused state --> + <item gecko:state_private="true" + android:state_focused="true" + android:state_pressed="false" + android:drawable="@color/new_tablet_highlight_focused_pb"/> + + <!-- pressed state --> + <item android:state_pressed="true" + android:drawable="@color/new_tablet_highlight"/> + + <!-- focused state --> + <item android:state_focused="true" + android:state_pressed="false" + android:drawable="@color/new_tablet_highlight_focused"/> + + <!-- private browsing mode --> + <item gecko:state_private="true" + android:drawable="@color/background_private"/> + + <!-- normal mode --> + <item android:drawable="@color/background_normal"/> + +</selector>
--- a/mobile/android/base/newtablet/res/layout-large-v11/new_tablet_browser_toolbar.xml +++ b/mobile/android/base/newtablet/res/layout-large-v11/new_tablet_browser_toolbar.xml @@ -27,17 +27,17 @@ style="@style/UrlBar.ImageButton.NewTablet" android:layout_width="@dimen/back_button_width" android:layout_height="@dimen/back_button_width" android:layout_centerVertical="true" android:layout_marginLeft="12dp" android:layout_alignParentLeft="true" android:src="@drawable/new_tablet_ic_menu_back" android:contentDescription="@string/back" - android:background="@drawable/url_bar_nav_button"/> + android:background="@drawable/new_tablet_url_bar_nav_button"/> <org.mozilla.gecko.toolbar.ToolbarEditLayout android:id="@+id/edit_layout" style="@style/UrlBar.Button" android:paddingLeft="12dp" android:paddingRight="12dp" android:visibility="gone" android:orientation="horizontal" android:layout_toRightOf="@id/back"
--- a/mobile/android/base/resources/values-large-v11/styles.xml +++ b/mobile/android/base/resources/values-large-v11/styles.xml @@ -28,16 +28,17 @@ <style name="UrlBar.ImageButton.Forward.NewTablet"> <item name="android:layout_marginLeft">@dimen/new_tablet_forward_default_offset</item> <item name="android:layout_height">match_parent</item> <item name="android:paddingTop">0dp</item> <item name="android:paddingBottom">0dp</item> <item name="android:layout_marginTop">11.5dp</item> <item name="android:layout_marginBottom">11.5dp</item> <item name="android:src">@drawable/new_tablet_ic_menu_forward</item> + <item name="android:background">@drawable/new_tablet_url_bar_nav_button</item> </style> <style name="UrlBar.ImageButton.TabCount.NewTablet"> <item name="android:background">@drawable/new_tablet_tabs_count</item> <!-- From UrlBar.ImageButton.NewTablet because we can't inherit directly. --> <item name="android:layout_width">@dimen/new_tablet_browser_toolbar_menu_item_width</item> </style>
--- a/mobile/android/base/resources/values/colors.xml +++ b/mobile/android/base/resources/values/colors.xml @@ -8,16 +8,22 @@ <color name="background_normal">#FFEBEBF0</color> <color name="background_private">#FF292C29</color> <color name="background_tabs">#FF363B40</color> <color name="highlight">#33000000</color> <color name="highlight_focused">#1A000000</color> <color name="highlight_dark">#33FFFFFF</color> <color name="highlight_dark_focused">#1AFFFFFF</color> + <!-- (bug 1077195) Focused state values are temporary. --> + <color name="new_tablet_highlight">#D7D7DC</color> + <color name="new_tablet_highlight_focused">#C0C9D0</color> + <color name="new_tablet_highlight_pb">#222222</color> + <color name="new_tablet_highlight_focused_pb">#363B40</color> + <!-- highlight on shaped button: 20% white over background_tabs --> <color name="highlight_shaped">#FF696D71</color> <!-- highlight-focused on shaped button: 10% white over background_tabs --> <color name="highlight_shaped_focused">#FF565B60</color> <!-- highlight on nav button: 20% black over background_normal --> <color name="highlight_nav">#FFA5ACB2</color>
new file mode 100644 --- /dev/null +++ b/mozglue/linker/BaseElf.cpp @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BaseElf.h" +#include "Elfxx.h" +#include "Logging.h" + +using namespace Elf; + +unsigned long +BaseElf::Hash(const char *symbol) +{ + const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol); + unsigned long h = 0, g; + while (*sym) { + h = (h << 4) + *sym++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; +} + +void * +BaseElf::GetSymbolPtr(const char *symbol, unsigned long hash) const +{ + const Sym *sym = GetSymbol(symbol, hash); + void *ptr = nullptr; + if (sym && sym->st_shndx != SHN_UNDEF) + ptr = GetPtr(sym->st_value); + DEBUG_LOG("BaseElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p", + reinterpret_cast<const void *>(this), GetPath(), symbol, ptr); + return ptr; +} + +const Sym * +BaseElf::GetSymbol(const char *symbol, unsigned long hash) const +{ + /* Search symbol with the buckets and chains tables. + * The hash computed from the symbol name gives an index in the buckets + * table. The corresponding value in the bucket table is an index in the + * symbols table and in the chains table. + * If the corresponding symbol in the symbols table matches, we're done. + * Otherwise, the corresponding value in the chains table is a new index + * in both tables, which corresponding symbol is tested and so on and so + * forth */ + size_t bucket = hash % buckets.numElements(); + for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) { + if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name))) + continue; + return &symtab[y]; + } + return nullptr; +}
new file mode 100644 --- /dev/null +++ b/mozglue/linker/BaseElf.h @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BaseElf_h +#define BaseElf_h + +#include "ElfLoader.h" +#include "Elfxx.h" + + +/** + * Base class for ELF libraries. This class includes things that will be + * common between SystemElfs and CustomElfs. + */ +class BaseElf: public LibHandle +{ +public: + /** + * Hash function for symbol lookup, as defined in ELF standard for System V. + */ + static unsigned long Hash(const char *symbol); + + /** + * Returns the address corresponding to the given symbol name (with a + * pre-computed hash). + */ + void *GetSymbolPtr(const char *symbol, unsigned long hash) const; + + /** + * Returns a pointer to the Elf Symbol in the Dynamic Symbol table + * corresponding to the given symbol name (with a pre-computed hash). + */ + const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const; + + BaseElf(const char *path) + : LibHandle(path) { } + +protected: + /** + * Inherited from LibHandle. Those are temporary and are not supposed to + * be used. + */ + virtual void *GetSymbolPtr(const char *symbol) const { return NULL; }; + virtual bool Contains(void *addr) const { return false; }; + virtual void *GetBase() const { return GetPtr(0); } + +#ifdef __ARM_EABI__ + virtual const void *FindExidx(int *pcount) const { return NULL; }; +#endif + + virtual Mappable *GetMappable() const { return NULL; }; + +public: +/* private: */ + /** + * Returns a pointer relative to the base address where the library is + * loaded. + */ + void *GetPtr(const Elf::Addr offset) const + { + if (reinterpret_cast<void *>(offset) > base) + return reinterpret_cast<void *>(offset); + return base + offset; + } + + /** + * Like the above, but returns a typed (const) pointer + */ + template <typename T> + const T *GetPtr(const Elf::Addr offset) const + { + if (reinterpret_cast<void *>(offset) > base) + return reinterpret_cast<const T *>(offset); + return reinterpret_cast<const T *>(base + offset); + } + + /* Base address where the library is loaded */ + MappedPtr base; + + /* Buckets and chains for the System V symbol hash table */ + Array<Elf::Word> buckets; + UnsizedArray<Elf::Word> chains; + +/* protected: */ + /* String table */ + Elf::Strtab strtab; + + /* Symbol table */ + UnsizedArray<Elf::Sym> symtab; +}; + +#endif /* BaseElf_h */
--- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -2,17 +2,19 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <cstring> #include <sys/mman.h> #include <vector> #include <dlfcn.h> #include <signal.h> +#include <string.h> #include "CustomElf.h" +#include "BaseElf.h" #include "Mappable.h" #include "Logging.h" using namespace Elf; using namespace mozilla; /* TODO: Fill ElfLoader::Singleton.lastError on errors. */ @@ -274,53 +276,20 @@ CustomElf::~CustomElf() CallFini(); /* Normally, __cxa_finalize is called by the .fini function. However, * Android NDK before r6b doesn't do that. Our wrapped cxa_finalize only * calls destructors once, so call it in all cases. */ ElfLoader::__wrap_cxa_finalize(this); ElfLoader::Singleton.Forget(this); } -namespace { - -/** - * Hash function for symbol lookup, as defined in ELF standard for System V - */ -unsigned long -ElfHash(const char *symbol) -{ - const unsigned char *sym = reinterpret_cast<const unsigned char *>(symbol); - unsigned long h = 0, g; - while (*sym) { - h = (h << 4) + *sym++; - if ((g = h & 0xf0000000)) - h ^= g >> 24; - h &= ~g; - } - return h; -} - -} /* anonymous namespace */ - void * CustomElf::GetSymbolPtr(const char *symbol) const { - return GetSymbolPtr(symbol, ElfHash(symbol)); -} - -void * -CustomElf::GetSymbolPtr(const char *symbol, unsigned long hash) const -{ - const Sym *sym = GetSymbol(symbol, hash); - void *ptr = nullptr; - if (sym && sym->st_shndx != SHN_UNDEF) - ptr = GetPtr(sym->st_value); - DEBUG_LOG("CustomElf::GetSymbolPtr(%p [\"%s\"], \"%s\") = %p", - reinterpret_cast<const void *>(this), GetPath(), symbol, ptr); - return ptr; + return BaseElf::GetSymbolPtr(symbol, Hash(symbol)); } void * CustomElf::GetSymbolPtrInDeps(const char *symbol) const { /* Resolve dlopen and related functions to point to ours */ if (symbol[0] == 'd' && symbol[1] == 'l') { if (strcmp(symbol + 2, "open") == 0) @@ -367,69 +336,48 @@ CustomElf::GetSymbolPtrInDeps(const char // so stub those out here if (strncmp(symbol, MISSING_FLASH_SYMNAME_START, sizeof(MISSING_FLASH_SYMNAME_START) - 1) == 0) { return FunctionPtr(__void_stub); } void *sym; - /* Search the symbol in the main program. Note this also tries all libraries - * the system linker will have loaded RTLD_GLOBAL. Unfortunately, that doesn't - * work with bionic, but its linker doesn't normally search the main binary - * anyways. Moreover, on android, the main binary is dalvik. */ -#ifdef __GLIBC__ - sym = dlsym(RTLD_DEFAULT, symbol); - DEBUG_LOG("dlsym(RTLD_DEFAULT, \"%s\") = %p", symbol, sym); - if (sym) - return sym; -#endif + + unsigned long hash = Hash(symbol); + + /* self_elf should never be NULL, but better safe than sorry. */ + if (ElfLoader::Singleton.self_elf) { + /* We consider the library containing this code a permanent LD_PRELOAD, + * so, check if the symbol exists here first. */ + sym = ElfLoader::Singleton.self_elf->GetSymbolPtr(symbol, hash); + if (sym) + return sym; + } /* Then search the symbol in our dependencies. Since we already searched in * libraries the system linker loaded, skip those (on glibc systems). We * also assume the symbol is to be found in one of the dependent libraries * directly, not in their own dependent libraries. Building libraries with * --no-allow-shlib-undefined ensures such indirect symbol dependency don't * happen. */ - unsigned long hash = ElfHash(symbol); for (std::vector<RefPtr<LibHandle> >::const_iterator it = dependencies.begin(); it < dependencies.end(); ++it) { if (!(*it)->IsSystemElf()) { - sym = reinterpret_cast<CustomElf *>((*it).get())->GetSymbolPtr(symbol, hash); -#ifndef __GLIBC__ + sym = static_cast<BaseElf *>( + static_cast<CustomElf *>((*it).get()))->GetSymbolPtr(symbol, hash); } else { sym = (*it)->GetSymbolPtr(symbol); -#endif } if (sym) return sym; } return nullptr; } -const Sym * -CustomElf::GetSymbol(const char *symbol, unsigned long hash) const -{ - /* Search symbol with the buckets and chains tables. - * The hash computed from the symbol name gives an index in the buckets - * table. The corresponding value in the bucket table is an index in the - * symbols table and in the chains table. - * If the corresponding symbol in the symbols table matches, we're done. - * Otherwise, the corresponding value in the chains table is a new index - * in both tables, which corresponding symbol is tested and so on and so - * forth */ - size_t bucket = hash % buckets.numElements(); - for (size_t y = buckets[bucket]; y != STN_UNDEF; y = chains[y]) { - if (strcmp(symbol, strtab.GetStringAt(symtab[y].st_name))) - continue; - return &symtab[y]; - } - return nullptr; -} - bool CustomElf::Contains(void *addr) const { return base.Contains(addr); } #ifdef __ARM_EABI__ const void *
--- a/mozglue/linker/CustomElf.h +++ b/mozglue/linker/CustomElf.h @@ -1,24 +1,25 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef CustomElf_h #define CustomElf_h #include "ElfLoader.h" +#include "BaseElf.h" #include "Logging.h" #include "Elfxx.h" /** * Library Handle class for ELF libraries we don't let the system linker * handle. */ -class CustomElf: public LibHandle, private ElfLoader::link_map +class CustomElf: public BaseElf, private ElfLoader::link_map { friend class ElfLoader; friend class SEGVHandler; public: /** * Returns a new CustomElf using the given file descriptor to map ELF * content. The file descriptor ownership is stolen, and it will be closed * in CustomElf's destructor if an instance is created, or by the Load @@ -26,17 +27,17 @@ public: * are the same kind of flags that would be given to dlopen(), though * currently, none are supported and the behaviour is more or less that of * RTLD_GLOBAL | RTLD_BIND_NOW. */ static mozilla::TemporaryRef<LibHandle> Load(Mappable *mappable, const char *path, int flags); /** - * Inherited from LibHandle + * Inherited from LibHandle/BaseElf */ virtual ~CustomElf(); virtual void *GetSymbolPtr(const char *symbol) const; virtual bool Contains(void *addr) const; virtual void *GetBase() const { return GetPtr(0); } #ifdef __ARM_EABI__ virtual const void *FindExidx(int *pcount) const; @@ -50,65 +51,35 @@ public: * Shows some stats about the Mappable instance. The when argument is to be * used by the caller to give an identifier of the when the stats call is * made. */ void stats(const char *when) const; private: /** - * Returns a pointer to the Elf Symbol in the Dynamic Symbol table - * corresponding to the given symbol name (with a pre-computed hash). - */ - const Elf::Sym *GetSymbol(const char *symbol, unsigned long hash) const; - - /** - * Returns the address corresponding to the given symbol name (with a - * pre-computed hash). - */ - void *GetSymbolPtr(const char *symbol, unsigned long hash) const; - - /** * Scan dependent libraries to find the address corresponding to the * given symbol name. This is used to find symbols that are undefined * in the Elf object. */ void *GetSymbolPtrInDeps(const char *symbol) const; /** * Private constructor */ CustomElf(Mappable *mappable, const char *path) - : LibHandle(path) + : BaseElf(path) , mappable(mappable) , init(0) , fini(0) , initialized(false) , has_text_relocs(false) { } /** - * Returns a pointer relative to the base address where the library is - * loaded. - */ - void *GetPtr(const Elf::Addr offset) const - { - return base + offset; - } - - /** - * Like the above, but returns a typed (const) pointer - */ - template <typename T> - const T *GetPtr(const Elf::Addr offset) const - { - return reinterpret_cast<const T *>(base + offset); - } - - /** * Loads an Elf segment defined by the given PT_LOAD header. * Returns whether this succeeded or failed. */ bool LoadSegment(const Elf::Phdr *pt_load) const; /** * Initializes the library according to information found in the given * PT_DYNAMIC header. @@ -162,29 +133,16 @@ private: void CallFunction(Elf::Addr addr) const { return CallFunction(GetPtr(addr)); } /* Appropriated Mappable */ mozilla::RefPtr<Mappable> mappable; - /* Base address where the library is loaded */ - MappedPtr base; - - /* String table */ - Elf::Strtab strtab; - - /* Symbol table */ - UnsizedArray<Elf::Sym> symtab; - - /* Buckets and chains for the System V symbol hash table */ - Array<Elf::Word> buckets; - UnsizedArray<Elf::Word> chains; - /* List of dependent libraries */ std::vector<mozilla::RefPtr<LibHandle> > dependencies; /* List of .rel.dyn/.rela.dyn relocations */ Array<Elf::Reloc> relocations; /* List of .rel.plt/.rela.plt relocation */ Array<Elf::Reloc> jumprels;
--- a/mozglue/linker/ElfLoader.cpp +++ b/mozglue/linker/ElfLoader.cpp @@ -6,16 +6,17 @@ #include <cstring> #include <cstdlib> #include <cstdio> #include <dlfcn.h> #include <unistd.h> #include <algorithm> #include <fcntl.h> #include "ElfLoader.h" +#include "BaseElf.h" #include "CustomElf.h" #include "Mappable.h" #include "Logging.h" #include <inttypes.h> #if defined(ANDROID) #include <sys/syscall.h> @@ -33,16 +34,20 @@ inline int sigaltstack(const stack_t *ss #endif /* __ANDROID_API__ */ #endif /* ANDROID */ #ifdef __ARM_EABI__ extern "C" MOZ_EXPORT const void * __gnu_Unwind_Find_exidx(void *pc, int *pcount) __attribute__((weak)); #endif +/* Pointer to the PT_DYNAMIC section of the executable or library + * containing this code. */ +extern "C" Elf::Dyn _DYNAMIC[]; + using namespace mozilla; /** * dlfcn.h replacements functions */ void * __wrap_dlopen(const char *path, int flags) @@ -326,16 +331,20 @@ SystemElf::FindExidx(int *pcount) const ElfLoader ElfLoader::Singleton; TemporaryRef<LibHandle> ElfLoader::Load(const char *path, int flags, LibHandle *parent) { /* Ensure logging is initialized or refresh if environment changed. */ Logging::Init(); + /* Ensure self_elf initialization. */ + if (!self_elf) + Init(); + RefPtr<LibHandle> handle; /* Handle dlopen(nullptr) directly. */ if (!path) { handle = SystemElf::Load(nullptr, flags); return handle; } @@ -461,16 +470,64 @@ ElfLoader::Forget(LibHandle *handle) dbg.Remove(static_cast<CustomElf *>(handle)); handles.erase(it); } else { DEBUG_LOG("ElfLoader::Forget(%p [\"%s\"]): Handle not found", reinterpret_cast<void *>(handle), handle->GetPath()); } } +void +ElfLoader::Init() +{ + Dl_info info; + /* On Android < 4.1 can't reenter dl* functions. So when the library + * containing this code is dlopen()ed, it can't call dladdr from a + * static initializer. */ + if (dladdr(_DYNAMIC, &info) != 0) { + /* Ideally, we wouldn't be initializing self_elf this way, but until + * SystemElf actually inherits from BaseElf, we'll just do it this + * (gross) way. */ + UniquePtr<BaseElf> elf = mozilla::MakeUnique<BaseElf>(info.dli_fname); + elf->base.Assign(info.dli_fbase, -1); + size_t symnum = 0; + for (const Elf::Dyn *dyn = _DYNAMIC; dyn->d_tag; dyn++) { + switch (dyn->d_tag) { + case DT_HASH: + { + DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_HASH", dyn->d_un.d_val); + const Elf::Word *hash_table_header = \ + elf->GetPtr<Elf::Word>(dyn->d_un.d_ptr); + symnum = hash_table_header[1]; + elf->buckets.Init(&hash_table_header[2], hash_table_header[0]); + elf->chains.Init(&*elf->buckets.end()); + } + break; + case DT_STRTAB: + DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_STRTAB", dyn->d_un.d_val); + elf->strtab.Init(elf->GetPtr(dyn->d_un.d_ptr)); + break; + case DT_SYMTAB: + DEBUG_LOG("%s 0x%08" PRIxAddr, "DT_SYMTAB", dyn->d_un.d_val); + elf->symtab.Init(elf->GetPtr(dyn->d_un.d_ptr)); + break; + } + } + if (!elf->buckets || !symnum) { + ERROR("%s: Missing or broken DT_HASH", info.dli_fname); + } else if (!elf->strtab) { + ERROR("%s: Missing DT_STRTAB", info.dli_fname); + } else if (!elf->symtab) { + ERROR("%s: Missing DT_SYMTAB", info.dli_fname); + } else { + self_elf = Move(elf); + } + } +} + ElfLoader::~ElfLoader() { LibHandleList list; /* Build up a list of all library handles with direct (external) references. * We actually skip system library handles because we want to keep at least * some of these open. Most notably, Mozilla codebase keeps a few libgnome * libraries deliberately open because of the mess that libORBit destruction * is. dlclose()ing these libraries actually leads to problems. */ @@ -502,16 +559,20 @@ ElfLoader::~ElfLoader() "[%d direct refs, %d refs total]", (*it)->GetPath(), (*it)->DirectRefCount(), (*it)->refCount()); /* Not removing, since it could have references to other libraries, * destroying them as a side effect, and possibly leaving dangling * pointers in the handle list we're scanning */ } } } + /* Avoid self_elf->base destructor unmapping something that doesn't actually + * belong to it. */ + if (self_elf) + self_elf->base.release(); } void ElfLoader::stats(const char *when) { for (LibHandleList::iterator it = Singleton.handles.begin(); it < Singleton.handles.end(); ++it) if (!(*it)->IsSystemElf())
--- a/mozglue/linker/ElfLoader.h +++ b/mozglue/linker/ElfLoader.h @@ -4,16 +4,17 @@ #ifndef ElfLoader_h #define ElfLoader_h #include <vector> #include <dlfcn.h> #include <signal.h> #include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" #include "Zip.h" #include "Elfxx.h" #include "Mappable.h" /** * dlfcn.h replacement functions */ extern "C" { @@ -58,16 +59,19 @@ MFBT_API void * MFBT_API void __dl_munmap(void *handle, void *addr, size_t length); MFBT_API bool IsSignalHandlingBroken(); } +/* Forward declaration because BaseElf.h includes ElfLoader.h */ +class BaseElf; + /** * Specialize RefCounted template for LibHandle. We may get references to * LibHandles during the execution of their destructor, so we need * RefCounted<LibHandle>::Release to support some reentrancy. See further * below. */ class LibHandle; @@ -434,16 +438,23 @@ protected: friend const char *__wrap_dlerror(void); friend void *__wrap_dlsym(void *handle, const char *symbol); friend int __wrap_dlclose(void *handle); const char *lastError; private: ~ElfLoader(); + /* Initialization code that can't run during static initialization. */ + void Init(); + + /* System loader handle for the library/program containing our code. This + * is used to resolve wrapped functions. */ + mozilla::UniquePtr<BaseElf> self_elf; + /* Bookkeeping */ typedef std::vector<LibHandle *> LibHandleList; LibHandleList handles; protected: friend class CustomElf; /** * Show some stats about Mappables in CustomElfs. The when argument is to @@ -552,23 +563,31 @@ private: * is in when the function above is called. */ enum { RT_CONSISTENT, /* Changes are complete */ RT_ADD, /* Beginning to add a new object */ RT_DELETE /* Beginning to remove an object */ } r_state; }; + /* Memory representation of ELF Auxiliary Vectors */ + struct AuxVector { + Elf::Addr type; + Elf::Addr value; + }; + /* Helper class used to integrate libraries loaded by this linker in * r_debug */ class DebuggerHelper { public: DebuggerHelper(); + void Init(AuxVector *auvx); + operator bool() { return dbg; } /* Make the debugger aware of a new loaded object */ void Add(link_map *map);
--- a/mozglue/linker/Utils.h +++ b/mozglue/linker/Utils.h @@ -285,16 +285,20 @@ public: } ~GenericMappedPtr() { if (get() != MAP_FAILED) static_cast<T *>(this)->munmap(get(), GetLength()); } + void release() + { + MemoryRange::Assign(MAP_FAILED, 0); + } }; struct MappedPtr: public GenericMappedPtr<MappedPtr> { MappedPtr(void *buf, size_t length) : GenericMappedPtr<MappedPtr>(buf, length) { } MappedPtr(const MemoryRange& other) : GenericMappedPtr<MappedPtr>(other) { }
--- a/mozglue/linker/moz.build +++ b/mozglue/linker/moz.build @@ -1,15 +1,16 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ + 'BaseElf.cpp', 'CustomElf.cpp', 'ElfLoader.cpp', 'Mappable.cpp', 'SeekableZStream.cpp', 'Zip.cpp', ] Library('linker')
--- a/python/mach/mach/base.py +++ b/python/mach/mach/base.py @@ -72,39 +72,34 @@ class MethodHandler(object): 'name', # String category this command belongs to. 'category', # Description of the purpose of this command. 'description', - # Whether to allow all arguments from the parser. - 'allow_all_arguments', - # Functions used to 'skip' commands if they don't meet the conditions # in a given context. 'conditions', # argparse.ArgumentParser instance to use as the basis for command # arguments. 'parser', # Arguments added to this command's parser. This is a 2-tuple of # positional and named arguments, respectively. 'arguments', ) def __init__(self, cls, method, name, category=None, description=None, - allow_all_arguments=False, conditions=None, parser=None, arguments=None, - pass_context=False): + conditions=None, parser=None, arguments=None, pass_context=False): self.cls = cls self.method = method self.name = name self.category = category self.description = description - self.allow_all_arguments = allow_all_arguments self.conditions = conditions or [] self.parser = parser self.arguments = arguments or [] self.pass_context = pass_context
--- a/python/mach/mach/decorators.py +++ b/python/mach/mach/decorators.py @@ -1,14 +1,15 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import unicode_literals +import argparse import collections import inspect import types from .base import ( MachError, MethodHandler ) @@ -51,18 +52,18 @@ def CommandProvider(cls): # define commands multiple times. We also sort keys so commands defined in # the same class are grouped in a sane order. for attr in sorted(cls.__dict__.keys()): value = cls.__dict__[attr] if not isinstance(value, types.FunctionType): continue - command_name, category, description, allow_all, conditions, parser = getattr( - value, '_mach_command', (None, None, None, None, None, None)) + command_name, category, description, conditions, parser = getattr( + value, '_mach_command', (None, None, None, None, None)) if command_name is None: continue if conditions is None and Registrar.require_conditions: continue msg = 'Mach command \'%s\' implemented incorrectly. ' + \ @@ -77,19 +78,18 @@ def CommandProvider(cls): for c in conditions: if not hasattr(c, '__call__'): msg = msg % (command_name, type(c)) raise MachError(msg) arguments = getattr(value, '_mach_command_args', None) handler = MethodHandler(cls, attr, command_name, category=category, - description=description, allow_all_arguments=allow_all, - conditions=conditions, parser=parser, arguments=arguments, - pass_context=pass_context) + description=description, conditions=conditions, parser=parser, + arguments=arguments, pass_context=pass_context) Registrar.register_command_handler(handler) return cls class Command(object): """Decorator for functions or methods that provide a mach subcommand. @@ -97,40 +97,36 @@ class Command(object): The decorator accepts arguments that define basic attributes of the command. The following arguments are recognized: category -- The string category to which this command belongs. Mach's help will group commands by category. description -- A brief description of what the command does. - allow_all_args -- Bool indicating whether to allow unknown arguments - through to the command. - parser -- an optional argparse.ArgumentParser instance to use as the basis for the command arguments. For example: @Command('foo', category='misc', description='Run the foo action') def foo(self): pass """ - def __init__(self, name, category=None, description=None, - allow_all_args=False, conditions=None, parser=None): + def __init__(self, name, category=None, description=None, conditions=None, + parser=None): self._name = name self._category = category self._description = description - self._allow_all_args = allow_all_args self._conditions = conditions self._parser = parser def __call__(self, func): func._mach_command = (self._name, self._category, self._description, - self._allow_all_args, self._conditions, self._parser) + self._conditions, self._parser) return func class CommandArgument(object): """Decorator for additional arguments to mach subcommands. This decorator should be used to add arguments to mach commands. Arguments @@ -140,16 +136,21 @@ class CommandArgument(object): @Command('foo', help='Run the foo action') @CommandArgument('-b', '--bar', action='store_true', default=False, help='Enable bar mode.') def foo(self): pass """ def __init__(self, *args, **kwargs): + if kwargs.get('nargs') == argparse.REMAINDER: + # These are the assertions we make in dispatcher.py about + # those types of CommandArguments. + assert len(args) == 1 + assert all(k in ('default', 'nargs', 'help') for k in kwargs) self._command_args = (args, kwargs) def __call__(self, func): command_args = getattr(func, '_mach_command_args', []) command_args.insert(0, self._command_args) func._mach_command_args = command_args
--- a/python/mach/mach/dispatcher.py +++ b/python/mach/mach/dispatcher.py @@ -130,38 +130,71 @@ class CommandAction(argparse.Action): # We create a new parser, populate it with the command's arguments, # then feed all remaining arguments to it, merging the results # with ourselves. This is essentially what argparse subparsers # do. parser_args = { 'add_help': False, 'usage': '%(prog)s [global arguments] ' + command + - ' command arguments]', + ' [command arguments]', } - if handler.allow_all_arguments: - parser_args['prefix_chars'] = '+' - if handler.parser: subparser = handler.parser else: subparser = argparse.ArgumentParser(**parser_args) + remainder = None + for arg in handler.arguments: - subparser.add_argument(*arg[0], **arg[1]) + if arg[1].get('nargs') == argparse.REMAINDER: + # parse_known_args expects all argparse.REMAINDER ('...') + # arguments to be all stuck together. Instead, we want them to + # pick any extra argument, wherever they are. + # Assume a limited CommandArgument for those arguments. + assert len(arg[0]) == 1 + assert all(k in ('default', 'nargs', 'help') for k in arg[1]) + remainder = arg + else: + subparser.add_argument(*arg[0], **arg[1]) # We define the command information on the main parser result so as to # not interfere with arguments passed to the command. setattr(namespace, 'mach_handler', handler) setattr(namespace, 'command', command) command_namespace, extra = subparser.parse_known_args(args) setattr(namespace, 'command_args', command_namespace) - if extra: + if remainder: + (name,), options = remainder + # parse_known_args usefully puts all arguments after '--' in + # extra, but also puts '--' there. We don't want to pass it down + # to the command handler. Note that if multiple '--' are on the + # command line, only the first one is removed, so that subsequent + # ones are passed down. + if '--' in extra: + extra.remove('--') + + # Commands with argparse.REMAINDER arguments used to force the + # other arguments to be '+' prefixed. If a user now passes such + # an argument, if will silently end up in extra. So, check if any + # of the allowed arguments appear in a '+' prefixed form, and error + # out if that's the case. + for args, _ in handler.arguments: + for arg in args: + arg = arg.replace('-', '+', 1) + if arg in extra: + raise UnrecognizedArgumentError(command, [arg]) + + if extra: + setattr(command_namespace, name, extra) + else: + setattr(command_namespace, name, options.get('default', [])) + elif extra: raise UnrecognizedArgumentError(command, extra) def _handle_main_help(self, parser, verbose): # Since we don't need full sub-parser support for the main help output, # we create groups in the ArgumentParser and populate each group with # arguments corresponding to command names. This has the side-effect # that argparse renders it nicely. r = self._mach_registrar @@ -229,19 +262,16 @@ class CommandAction(argparse.Action): # just the command data then supplement the main help's output with # this 2nd parser's. We use a custom formatter class to ignore some of # the help output. parser_args = { 'formatter_class': CommandFormatter, 'add_help': False, } - if handler.allow_all_arguments: - parser_args['prefix_chars'] = '+' - if handler.parser: c_parser = handler.parser c_parser.formatter_class = NoUsageFormatter # Accessing _action_groups is a bit shady. We are highly dependent # on the argparse implementation not changing. We fail fast to # detect upstream changes so we can intelligently react to them. group = c_parser._action_groups[1]
--- a/python/mach_commands.py +++ b/python/mach_commands.py @@ -20,17 +20,16 @@ from mach.decorators import ( CommandProvider, Command, ) @CommandProvider class MachCommands(MachCommandBase): @Command('python', category='devenv', - allow_all_args=True, description='Run Python.') @CommandArgument('args', nargs=argparse.REMAINDER) def python(self, args): # Avoid logging the command self.log_manager.terminal_handler.setLevel(logging.CRITICAL) self._activate_virtualenv()
--- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -805,75 +805,72 @@ def get_run_args(mach_command, params, r if not os.path.isdir(path): os.makedirs(path) args.append('-profile') args.append(path) if params: args.extend(params) - if '--' in args: - args.remove('--') - return args @CommandProvider class RunProgram(MachCommandBase): """Launch the compiled binary""" - @Command('run', category='post-build', allow_all_args=True, + @Command('run', category='post-build', description='Run the compiled program.') - @CommandArgument('params', default=None, nargs='...', + @CommandArgument('params', nargs='...', help='Command-line arguments to be passed through to the program. Not specifying a -profile or -P option will result in a temporary profile being used.') - @CommandArgument('+remote', '+r', action='store_true', + @CommandArgument('-remote', '-r', action='store_true', help='Do not pass the -no-remote argument by default.') - @CommandArgument('+background', '+b', action='store_true', + @CommandArgument('-background', '-b', action='store_true', help='Do not pass the -foreground argument by default on Mac') - @CommandArgument('+noprofile', '+n', action='store_true', + @CommandArgument('-noprofile', '-n', action='store_true', help='Do not pass the -profile argument by default.') def run(self, params, remote, background, noprofile): args = get_run_args(self, params, remote, background, noprofile) if not args: return 1 return self.run_process(args=args, ensure_exit_code=False, pass_thru=True) @CommandProvider class DebugProgram(MachCommandBase): """Debug the compiled binary""" - @Command('debug', category='post-build', allow_all_args=True, + @Command('debug', category='post-build', description='Debug the compiled program.') - @CommandArgument('params', default=None, nargs='...', + @CommandArgument('params', nargs='...', help='Command-line arguments to be passed through to the program. Not specifying a -profile or -P option will result in a temporary profile being used.') - @CommandArgument('+remote', '+r', action='store_true', + @CommandArgument('-remote', '-r', action='store_true', help='Do not pass the -no-remote argument by default') - @CommandArgument('+background', '+b', action='store_true', + @CommandArgument('-background', '-b', action='store_true', help='Do not pass the -foreground argument by default on Mac') - @CommandArgument('+debugger', default=None, type=str, + @CommandArgument('-debugger', default=None, type=str, help='Name of debugger to launch') - @CommandArgument('+debugparams', default=None, metavar='params', type=str, + @CommandArgument('-debugparams', default=None, metavar='params', type=str, help='Command-line arguments to pass to the debugger itself; split as the Bourne shell would.') # Bug 933807 introduced JS_DISABLE_SLOW_SCRIPT_SIGNALS to avoid clever # segfaults induced by the slow-script-detecting logic for Ion/Odin JITted # code. If we don't pass this, the user will need to periodically type # "continue" to (safely) resume execution. There are ways to implement # automatic resuming; see the bug. - @CommandArgument('+slowscript', action='store_true', + @CommandArgument('-slowscript', action='store_true', help='Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; when not set, recoverable but misleading SIGSEGV instances may occur in Ion/Odin JIT code') - @CommandArgument('+noprofile', '+n', action='store_true', + @CommandArgument('-noprofile', '-n', action='store_true', help='Do not pass the -profile argument by default.') def debug(self, params, remote, background, debugger, debugparams, slowscript, noprofile): # Parameters come from the CLI. We need to convert them before their use. if debugparams: import pymake.process argv, badchar = pymake.process.clinetoargv(debugparams, os.getcwd()) if badchar: - print("The +debugparams you passed require a real shell to parse them.") + print("The -debugparams you passed require a real shell to parse them.") print("(We can't handle the %r character.)" % (badchar,)) return 1 debugparams = argv; import mozdebug if not debugger: # No debugger name was provided. Look for the default ones on current OS. @@ -920,17 +917,17 @@ class DebugProgram(MachCommandBase): ensure_exit_code=False, pass_thru=True) @CommandProvider class RunDmd(MachCommandBase): """Launch the compiled binary with DMD enabled""" @Command('dmd', category='post-build', description='Run the compiled program with DMD enabled.') - @CommandArgument('params', default=None, nargs='...', + @CommandArgument('params', nargs='...', help=('Command-line arguments to be passed through to the program. ' 'Not specifying a -profile or -P option will result in a ' 'temporary profile being used. If passing -params use a "--" to ' 'indicate the start of params to pass to firefox.')) @CommandArgument('--remote', '-r', action='store_true', help='Do not pass the -no-remote argument by default.') @CommandArgument('--background', '-b', action='store_true', help='Do not pass the -foreground argument by default on Mac')
--- a/testing/marionette/client/marionette/tests/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit-tests.ini @@ -10,29 +10,13 @@ b2g = true ; true if the test should be skipped skip = false ; marionette unit tests [include:unit/unit-tests.ini] test_container = true -; webapi tests -[include:../../../../../dom/bluetooth/tests/marionette/manifest.ini] -[include:../../../../../dom/telephony/test/marionette/manifest.ini] -[include:../../../../../dom/voicemail/test/marionette/manifest.ini] -[include:../../../../../dom/battery/test/marionette/manifest.ini] -[include:../../../../../dom/mobilemessage/tests/marionette/manifest.ini] -[include:../../../../../dom/mobileconnection/tests/marionette/manifest.ini] -[include:../../../../../dom/system/gonk/tests/marionette/manifest.ini] -[include:../../../../../dom/icc/tests/marionette/manifest.ini] -[include:../../../../../dom/system/tests/marionette/manifest.ini] -[include:../../../../../dom/nfc/tests/marionette/manifest.ini] -[include:../../../../../dom/events/test/marionette/manifest.ini] -[include:../../../../../dom/wifi/test/marionette/manifest.ini] -[include:../../../../../dom/cellbroadcast/tests/marionette/manifest.ini] -[include:../../../../../dom/tethering/tests/marionette/manifest.ini] - ; layout tests [include:../../../../../layout/base/tests/marionette/manifest.ini] ; loop tests [include:../../../../../browser/components/loop/manifest.ini]
--- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1840,17 +1840,17 @@ class Mochitest(MochitestUtilsMixin): self.log.error("Automation Error: Received unexpected exception while running application\n") status = 1 finally: if options.vmwareRecording: self.stopVMwareRecording(); self.stopServers() - processLeakLog(self.leak_report_file, options.leakThresholds, options.ignoreMissingLeaks) + processLeakLog(self.leak_report_file, options) if self.nsprLogs: with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip: for logfile in glob.glob("%s/nspr*.log*" % tempfile.gettempdir()): logzip.write(logfile) os.remove(logfile) self.log.info("runtests.py | Running tests: end.")
--- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -197,17 +197,17 @@ class B2GMochitest(MochitestUtilsMixin): if status is None: # the runner has timed out status = 124 local_leak_file = tempfile.NamedTemporaryFile() self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) - processLeakLog(local_leak_file.name, options.leakThresholds, options.ignoreMissingLeaks) + processLeakLog(local_leak_file.name, options) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n"); status = -1 except: traceback.print_exc() self.log.error("Automation Error: Received unexpected exception while running application\n") if hasattr(self, 'runner'): self.runner.check_for_crashes()
--- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -545,16 +545,21 @@ stage-marionette: make-stage-dir $(NSINSTALL) -D $(MARIONETTE_DIR)/transport @(cd $(topsrcdir)/testing/marionette/client && tar --exclude marionette/tests $(TAR_CREATE_FLAGS) - *) | (cd $(MARIONETTE_DIR)/ && tar -xf -) @(cd $(topsrcdir)/testing/marionette/transport && tar $(TAR_CREATE_FLAGS) - *) | (cd $(MARIONETTE_DIR)/transport && tar -xf -) $(PYTHON) $(topsrcdir)/testing/marionette/client/marionette/tests/print-manifest-dirs.py \ $(topsrcdir) \ $(topsrcdir)/testing/marionette/client/marionette/tests/unit-tests.ini \ | (cd $(topsrcdir) && xargs tar $(TAR_CREATE_FLAGS) -) \ | (cd $(MARIONETTE_DIR)/tests && tar -xf -) + $(PYTHON) $(topsrcdir)/testing/marionette/client/marionette/tests/print-manifest-dirs.py \ + $(topsrcdir) \ + $(topsrcdir)/testing/marionette/client/marionette/tests/webapi-tests.ini \ + | (cd $(topsrcdir) && xargs tar $(TAR_CREATE_FLAGS) -) \ + | (cd $(MARIONETTE_DIR)/tests && tar -xf -) stage-mozbase: make-stage-dir $(MAKE) -C $(DEPTH)/testing/mozbase stage-package stage-web-platform-tests: make-stage-dir $(MAKE) -C $(DEPTH)/testing/web-platform stage-package stage-instrumentation-tests: make-stage-dir
--- a/tools/mach_commands.py +++ b/tools/mach_commands.py @@ -289,17 +289,17 @@ class PastebinProvider(object): except urllib2.URLError: print('ERROR. Could not connect to pastebin.mozilla.org.') return 1 return 0 @CommandProvider class ReviewboardToolsProvider(MachCommandBase): - @Command('rbt', category='devenv', allow_all_args=True, + @Command('rbt', category='devenv', description='Run Reviewboard Tools') @CommandArgument('args', nargs='...', help='Arguments to rbt tool') def rbt(self, args): if not args: args = ['help'] self._activate_virtualenv() self.virtualenv_manager.install_pip_package('RBTools==0.6')