author | arthur.iakab <aiakab@mozilla.com> |
Wed, 26 Sep 2018 15:57:57 +0300 | |
changeset 438335 | 7e8d28888963098d54ab622d5dde31fe8d797bf1 |
parent 438334 | 74f5bf70f7dc9d26d0017bd1ab535b1edaf5619e (current diff) |
parent 438288 | 32fb340597628c8eb3cea9b507a073a63fb6b81e (diff) |
child 438336 | aebcd9c08c51b24eec808c1190a05f253341cb33 |
push id | 34717 |
push user | shindli@mozilla.com |
push date | Wed, 26 Sep 2018 21:52:33 +0000 |
treeherder | mozilla-central@7ac88abc3c57 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 64.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/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1493,16 +1493,21 @@ pref("toolkit.telemetry.hybridContent.en pref("browser.ping-centre.telemetry", true); pref("browser.ping-centre.log", false); pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre"); pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre"); // Enable GMP support in the addon manager. pref("media.gmp-provider.enabled", true); +// Enable blocking access to storage from tracking resources by default on Nightly +#ifdef NIGHTLY_BUILD +pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */); +#endif + pref("browser.contentblocking.allowlist.storage.enabled", true); pref("browser.contentblocking.global-toggle.enabled", true); // Define a set of default features for the Content Blocking UI pref("browser.contentblocking.fastblock.ui.enabled", true); pref("browser.contentblocking.fastblock.control-center.ui.enabled", true); pref("browser.contentblocking.trackingprotection.ui.enabled", true);
--- a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm +++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm @@ -31,16 +31,25 @@ Object.defineProperty(details, "blockLis get() { let trackingTable = Services.prefs.getCharPref("urlclassifier.trackingTable"); // If content-track-digest256 is in the tracking table, // the user has enabled the strict list. return trackingTable.includes("content") ? "strict" : "basic"; }, }); +Object.defineProperty(details, "hasTouchScreen", { + enumerable: true, + get() { + // return a boolean to indicate there's a touch screen detected or not + let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); + return gfxInfo.getInfo().ApzTouchInput == 1; + }, +}); + if (AppConstants.platform == "linux") { XPCOMUtils.defineLazyPreferenceGetter(details, "layers.acceleration.force-enabled", "layers.acceleration.force-enabled", false); } let WebCompatReporter = { get endpoint() { return Services.urlFormatter.formatURLPref( "extensions.webcompat-reporter.newIssueEndpoint");
--- a/build/moz.configure/node.configure +++ b/build/moz.configure/node.configure @@ -1,102 +1,60 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. option('--disable-nodejs', help='Require Node.js to build') +option(env='NODEJS', nargs=1, help='Path to nodejs') -@depends(host) -@imports('os') -@imports(_from='os', _import='environ') -def node_toolchain_search_path(host): - # XXX partly copied from tooltool.py; should be hoisted somewhere central - - # Also add in the location to which `mach bootstrap` or - # `mach artifact toolchain` installs clang. - mozbuild_state_dir = environ.get('MOZBUILD_STATE_PATH', os.path.expanduser( - os.path.join('~', '.mozbuild'))) - - if host.kernel == "WINNT": - mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node') - else: - mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node', 'bin') - - # We still fallback to the PATH, since on OSes that don't have toolchain - # artifacts available to download, Node may be coming from $PATH. - path = [environ.get('PATH')] - updated_path = [mozbuild_node_path] + path - - return updated_path +@depends('--enable-nodejs', 'NODEJS') +@checking('for nodejs', + callback=lambda x: '%s (%s)' % (x.path, x.str_version) if x else 'no') +@imports(_from='mozbuild.nodeutil', _import='find_node_executable') +@imports(_from='mozbuild.nodeutil', _import='NODE_MIN_VERSION') +def nodejs(require, env_node): + node_exe = env_node[0] if env_node else None -# "nodejs" is first in the tuple on the assumption that it's only likely to -# exist on systems (probably linux distros) where there is a program in the path -# called "node" that does something else. -nodejs = check_prog('NODEJS', ('nodejs', 'node',), - allow_missing=True, paths=node_toolchain_search_path, - paths_have_priority=True) - - -@depends_if(nodejs) -@checking('for node.js version') -@imports('os') -@imports('re') -@imports('subprocess') -def nodejs_version(node): - env = dict(os.environ) - env[b'NODE_DISABLE_COLORS'] = b'1' - - out = check_cmd_output(node, '--version', env=env) - - match = re.search(r'v(.+)$', out) - - if not match: - return None - - return Version(match.group(1)) - - -@depends('--enable-nodejs', nodejs, nodejs_version) -def nodejs_suitability(require, node, version): - MIN_NODE_VERSION = Version('8.11') + nodejs, version = find_node_executable(node_exe) MAYBE_FILE_A_BUG = ''' -If you believe this is a bug, <https://mzl.la/2vLbXAv> is a good way -to file. Executing `mach bootstrap --no-system-changes` should -install a compatible version in ~/.mozbuild on most platforms. -More details: <https://bit.ly/2BbyD1E> -''' + Executing `mach bootstrap --no-system-changes` should + install a compatible version in ~/.mozbuild on most platforms. + If you believe this is a bug, <https://mzl.la/2vLbXAv> is a good way + to file. More details: <https://bit.ly/2BbyD1E> + ''' - if not node: - msg = ('could not find Node.js executable; ensure `node` or `nodejs` ' - 'is in PATH or set NODEJS in environment to point to an ' - 'executable.%s' % (MAYBE_FILE_A_BUG) + if not nodejs: + msg = ('could not find Node.js executable later than %s; ensure ' + '`node` or `nodejs` is in PATH or set NODEJS in environment ' + 'to point to an executable.%s' % (NODE_MIN_VERSION, MAYBE_FILE_A_BUG) ) if require: raise FatalCheckError(msg) else: log.warning(msg) log.warning('(This will become an error in the near future.)') return if not version: - msg = 'could not resolve Node.js version.%s' % (MAYBE_FILE_A_BUG) - if require: - raise FatalCheckError(msg) - else: - log.warning(msg) - log.warning('(This will become an error in the near future.)') - return - - if version < MIN_NODE_VERSION: - msg = 'NODEJS must point to node %s or newer; %s found. %s' % ( - MIN_NODE_VERSION, version, MAYBE_FILE_A_BUG) + msg = 'NODEJS must point to node %s or newer; found node location: %s. %s' % ( + NODE_MIN_VERSION, nodejs, MAYBE_FILE_A_BUG) if require: raise FatalCheckError(msg) else: log.warning(msg) + return + + return namespace( + path=nodejs, + version=version, + str_version='.'.join(str(v) for v in version), + ) + + +set_config('NODEJS', depends_if(nodejs)(lambda p: p.path))
--- a/config/rules.mk +++ b/config/rules.mk @@ -26,17 +26,17 @@ endif USE_AUTOTARGETS_MK = 1 include $(MOZILLA_DIR)/config/makefiles/makeutils.mk ifdef REBUILD_CHECK REPORT_BUILD = $(info $(shell $(PYTHON) $(MOZILLA_DIR)/config/rebuild_check.py $@ $^)) REPORT_BUILD_VERBOSE = $(REPORT_BUILD) else -REPORT_BUILD = $(info $(notdir $@)) +REPORT_BUILD = $(info $(relativesrcdir)/$(notdir $@)) ifdef BUILD_VERBOSE_LOG REPORT_BUILD_VERBOSE = $(REPORT_BUILD) else REPORT_BUILD_VERBOSE = $(call BUILDSTATUS,BUILD_VERBOSE $(relativesrcdir)) endif endif
--- a/devtools/server/tests/mochitest/inspector-helpers.js +++ b/devtools/server/tests/mochitest/inspector-helpers.js @@ -4,18 +4,16 @@ runNextTest */ "use strict"; const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const {DebuggerClient} = require("devtools/shared/client/debugger-client"); const {DebuggerServer} = require("devtools/server/main"); const Services = require("Services"); -// promise is still used in tests using this helper -const defer = require("devtools/shared/defer"); const {DocumentWalker: _documentWalker} = require("devtools/server/actors/inspector/document-walker"); // Always log packets when running tests. Services.prefs.setBoolPref("devtools.debugger.log", true); SimpleTest.registerCleanupFunction(function() { Services.prefs.clearUserPref("devtools.debugger.log"); }); @@ -82,25 +80,25 @@ function attachURL(url, callback) { }); } }); return cleanup; } function promiseOnce(target, event) { - const deferred = defer(); - target.on(event, (...args) => { - if (args.length === 1) { - deferred.resolve(args[0]); - } else { - deferred.resolve(args); - } + return new Promise(resolve => { + target.on(event, (...args) => { + if (args.length === 1) { + resolve(args[0]); + } else { + resolve(args); + } + }); }); - return deferred.promise; } function sortOwnershipChildren(children) { return children.sort((a, b) => a.name.localeCompare(b.name)); } function serverOwnershipSubtree(walker, node) { const actor = walker._refMap.get(node); @@ -166,47 +164,46 @@ function assertOwnershipTrees(walker) { is(JSON.stringify(clientTree, null, " "), JSON.stringify(serverTree, null, " "), "Server and client ownership trees should match."); return ownershipTreeSize(clientTree.root); } // Verify that an actorID is inaccessible both from the client library and the server. function checkMissing(client, actorID) { - let deferred = defer(); - const front = client.getActor(actorID); - ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID); + return new Promise(resolve => { + const front = client.getActor(actorID); + ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID); - deferred = defer(); - client.request({ - to: actorID, - type: "request", - }, response => { - is(response.error, "noSuchActor", "node list actor should no longer be contactable."); - deferred.resolve(undefined); + client.request({ + to: actorID, + type: "request", + }, response => { + is(response.error, "noSuchActor", + "node list actor should no longer be contactable."); + resolve(undefined); + }); }); - return deferred.promise; } // Verify that an actorID is accessible both from the client library and the server. function checkAvailable(client, actorID) { - let deferred = defer(); - const front = client.getActor(actorID); - ok(front, "Front should be accessible from the client for actorID: " + actorID); + return new Promise(resolve => { + const front = client.getActor(actorID); + ok(front, "Front should be accessible from the client for actorID: " + actorID); - deferred = defer(); - client.request({ - to: actorID, - type: "garbageAvailableTest", - }, response => { - is(response.error, "unrecognizedPacketType", - "node list actor should be contactable."); - deferred.resolve(undefined); + client.request({ + to: actorID, + type: "garbageAvailableTest", + }, response => { + is(response.error, "unrecognizedPacketType", + "node list actor should be contactable."); + resolve(undefined); + }); }); - return deferred.promise; } function promiseDone(currentPromise) { currentPromise.catch(err => { ok(false, "Promise failed: " + err); if (err.stack) { dump(err.stack); } @@ -270,30 +267,30 @@ function assertFrameLoad(mutations) { // that mutation out of the list function assertChildList(mutations) { return assertAndStrip(mutations, "Should have had a frame load change.", isChildList); } // Load mutations aren't predictable, so keep accumulating mutations until // the one we're looking for shows up. function waitForMutation(walker, test, mutations = []) { - const deferred = defer(); - for (const change of mutations) { - if (test(change)) { - deferred.resolve(mutations); + return new Promise(resolve => { + for (const change of mutations) { + if (test(change)) { + resolve(mutations); + } } - } - walker.once("mutations", newMutations => { - waitForMutation(walker, test, mutations.concat(newMutations)).then(finalMutations => { - deferred.resolve(finalMutations); + walker.once("mutations", newMutations => { + waitForMutation(walker, test, mutations.concat(newMutations)) + .then(finalMutations => { + resolve(finalMutations); + }); }); }); - - return deferred.promise; } var _tests = []; function addTest(test) { _tests.push(test); } function addAsyncTest(generator) {
--- a/devtools/server/tests/mochitest/test_inspector-dead-nodes.html +++ b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html @@ -19,21 +19,22 @@ window.onload = function() { }; let gWalker = null; let gDoc = null; addAsyncTest(async function() { const url = document.getElementById("inspectorContent").href; - const def = defer(); - attachURL(url, function(err, client, tab, doc) { - def.resolve({client, tab, doc}); + const attachURLPromise = new Promise(resolve => { + attachURL(url, function(err, client, tab, doc) { + resolve({client, tab, doc}); + }); }); - const {client, tab, doc} = await def.promise; + const {client, tab, doc} = await attachURLPromise; gDoc = doc; const {InspectorFront} = require("devtools/shared/fronts/inspector"); const inspector = InspectorFront(client, tab); gWalker = await inspector.getWalker(); runNextTest(); });
--- a/devtools/shared/css/generated/properties-db.js +++ b/devtools/shared/css/generated/properties-db.js @@ -8209,16 +8209,53 @@ exports.CSS_PROPERTIES = { "inherit", "initial", "mandatory", "none", "proximity", "unset" ] }, + "scrollbar-color": { + "isInherited": true, + "subproperties": [ + "scrollbar-color" + ], + "supports": [ + 2 + ], + "values": [ + "COLOR", + "auto", + "currentColor", + "hsl", + "hsla", + "inherit", + "initial", + "rgb", + "rgba", + "transparent", + "unset" + ] + }, + "scrollbar-width": { + "isInherited": false, + "subproperties": [ + "scrollbar-width" + ], + "supports": [], + "values": [ + "auto", + "inherit", + "initial", + "none", + "thin", + "unset" + ] + }, "shape-image-threshold": { "isInherited": false, "subproperties": [ "shape-image-threshold" ], "supports": [], "values": [ "inherit",
--- a/dom/canvas/WebGLContextExtensions.cpp +++ b/dom/canvas/WebGLContextExtensions.cpp @@ -179,17 +179,17 @@ WebGLContext::IsExtensionSupported(WebGL // EXT_ case WebGLExtensionID::EXT_blend_minmax: return WebGLExtensionBlendMinMax::IsSupported(this); case WebGLExtensionID::EXT_color_buffer_half_float: return WebGLExtensionColorBufferHalfFloat::IsSupported(this); case WebGLExtensionID::EXT_frag_depth: return WebGLExtensionFragDepth::IsSupported(this); case WebGLExtensionID::EXT_shader_texture_lod: - return gl->IsSupported(gl::GLFeature::shader_texture_lod); + return WebGLExtensionShaderTextureLod::IsSupported(this); case WebGLExtensionID::EXT_sRGB: return WebGLExtensionSRGB::IsSupported(this); // OES_ case WebGLExtensionID::OES_element_index_uint: return gl->IsSupported(gl::GLFeature::element_index_uint); case WebGLExtensionID::OES_standard_derivatives: return gl->IsSupported(gl::GLFeature::standard_derivatives);
--- a/dom/canvas/WebGLExtensionShaderTextureLod.cpp +++ b/dom/canvas/WebGLExtensionShaderTextureLod.cpp @@ -9,17 +9,30 @@ #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" namespace mozilla { WebGLExtensionShaderTextureLod::WebGLExtensionShaderTextureLod(WebGLContext* webgl) : WebGLExtensionBase(webgl) { + MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); } WebGLExtensionShaderTextureLod::~WebGLExtensionShaderTextureLod() { } +bool +WebGLExtensionShaderTextureLod::IsSupported(const WebGLContext* webgl) +{ + gl::GLContext* gl = webgl->GL(); + if (gl->IsGLES() && gl->Version() >= 300) { + // ANGLE's shader translator doesn't yet translate + // WebGL1+EXT_shader_texture_lod to ES3. (Bug 1491221) + return false; + } + return gl->IsSupported(gl::GLFeature::shader_texture_lod); +} + IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionShaderTextureLod, EXT_shader_texture_lod) } // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h +++ b/dom/canvas/WebGLExtensions.h @@ -246,16 +246,18 @@ public: class WebGLExtensionShaderTextureLod : public WebGLExtensionBase { public: explicit WebGLExtensionShaderTextureLod(WebGLContext*); virtual ~WebGLExtensionShaderTextureLod(); + static bool IsSupported(const WebGLContext* context); + DECL_WEBGL_EXTENSION_GOOP }; class WebGLExtensionTextureFilterAnisotropic : public WebGLExtensionBase { public: explicit WebGLExtensionTextureFilterAnisotropic(WebGLContext*);
--- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -117,17 +117,16 @@ Key::ToLocaleBasedKey(Key& aTarget, cons } if (IsFloat() || IsDate() || IsBinary()) { aTarget.mBuffer = mBuffer; return NS_OK; } aTarget.mBuffer.Truncate(); - aTarget.mBuffer.SetCapacity(mBuffer.Length()); auto* it = reinterpret_cast<const unsigned char*>(mBuffer.BeginReading()); auto* end = reinterpret_cast<const unsigned char*>(mBuffer.EndReading()); // First we do a pass and see if there are any strings in this key. We only // want to copy/decode when necessary. bool canShareBuffers = true; while (it < end) { @@ -145,16 +144,18 @@ Key::ToLocaleBasedKey(Key& aTarget, cons } if (canShareBuffers) { MOZ_ASSERT(it == end); aTarget.mBuffer = mBuffer; return NS_OK; } + aTarget.mBuffer.SetCapacity(mBuffer.Length()); + // A string was found, so we need to copy the data we've read so far auto* start = reinterpret_cast<const unsigned char*>(mBuffer.BeginReading()); if (it > start) { char* buffer; if (!aTarget.mBuffer.GetMutableData(&buffer, it-start)) { return NS_ERROR_OUT_OF_MEMORY; }
--- a/dom/media/AutoplayPolicy.cpp +++ b/dom/media/AutoplayPolicy.cpp @@ -22,31 +22,16 @@ #include "nsIDocShellTreeItem.h" #include "nsPIDOMWindow.h" mozilla::LazyLogModule gAutoplayPermissionLog("Autoplay"); #define AUTOPLAY_LOG(msg, ...) \ MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) -static const char* -AllowAutoplayToStr(const uint32_t state) -{ - switch (state) { - case nsIAutoplay::ALLOWED: - return "allowed"; - case nsIAutoplay::BLOCKED: - return "blocked"; - case nsIAutoplay::PROMPT: - return "prompt"; - default: - return "unknown"; - } -} - namespace mozilla { namespace dom { static nsIDocument* ApproverDocOf(const nsIDocument& aDocument) { nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell(); if (!ds) { @@ -198,17 +183,17 @@ AutoplayPolicy::IsAllowedToPlay(const HT if (IsMediaElementAllowedToPlay(aElement)) { return true; } const bool result = IsMediaElementAllowedToPlay(aElement) || autoplayDefault == nsIAutoplay::ALLOWED; AUTOPLAY_LOG("IsAllowedToPlay, mediaElement=%p, isAllowToPlay=%s", - &aElement, AllowAutoplayToStr(result)); + &aElement, result ? "allowed" : "blocked"); return result; } /* static */ bool AutoplayPolicy::IsAllowedToPlay(const AudioContext& aContext) { if (!Preferences::GetBool("media.autoplay.block-webaudio", false)) {
--- a/dom/media/MediaData.cpp +++ b/dom/media/MediaData.cpp @@ -250,17 +250,17 @@ ConstructPlanarYCbCrData(const VideoInfo data.mCbCrStride = Cb.mStride; data.mCbSkip = Cb.mSkip; data.mCrSkip = Cr.mSkip; data.mPicX = aPicture.x; data.mPicY = aPicture.y; data.mPicSize = aPicture.Size(); data.mStereoMode = aInfo.mStereoMode; data.mYUVColorSpace = aBuffer.mYUVColorSpace; - data.mBitDepth = aBuffer.mBitDepth; + data.mColorDepth = aBuffer.mColorDepth; return data; } /* static */ bool VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage, const VideoInfo& aInfo, const YCbCrBuffer &aBuffer, const IntRect& aPicture,
--- a/dom/media/MediaData.h +++ b/dom/media/MediaData.h @@ -451,16 +451,17 @@ class PlanarYCbCrImage; class VideoInfo; // Holds a decoded video frame, in YCbCr format. These are queued in the reader. class VideoData : public MediaData { public: typedef gfx::IntRect IntRect; typedef gfx::IntSize IntSize; + typedef gfx::ColorDepth ColorDepth; typedef layers::ImageContainer ImageContainer; typedef layers::Image Image; typedef layers::PlanarYCbCrImage PlanarYCbCrImage; static const Type sType = VIDEO_DATA; static const char* sTypeName; // YCbCr data obtained from decoding the video. The index's are: @@ -476,17 +477,17 @@ public: uint32_t mHeight; uint32_t mStride; uint32_t mOffset; uint32_t mSkip; }; Plane mPlanes[3]; YUVColorSpace mYUVColorSpace = YUVColorSpace::BT601; - uint32_t mBitDepth = 8; + ColorDepth mColorDepth = ColorDepth::COLOR_8; }; class Listener { public: virtual void OnSentToCompositor() = 0; virtual ~Listener() { } };
--- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -13,16 +13,17 @@ #include "nsTArray.h" #include "AudioConfig.h" #include "ImageTypes.h" #include "MediaData.h" #include "TrackID.h" // for TrackID #include "TimeUnits.h" #include "mozilla/gfx/Point.h" // for gfx::IntSize #include "mozilla/gfx/Rect.h" // for gfx::IntRect +#include "mozilla/gfx/Types.h" // for gfx::ColorDepth namespace mozilla { class AudioInfo; class VideoInfo; class TextInfo; class MetadataTag @@ -222,17 +223,17 @@ public: VideoInfo(const VideoInfo& aOther) : TrackInfo(aOther) , mDisplay(aOther.mDisplay) , mStereoMode(aOther.mStereoMode) , mImage(aOther.mImage) , mCodecSpecificConfig(aOther.mCodecSpecificConfig) , mExtraData(aOther.mExtraData) , mRotation(aOther.mRotation) - , mBitDepth(aOther.mBitDepth) + , mColorDepth(aOther.mColorDepth) , mImageRect(aOther.mImageRect) , mAlphaPresent(aOther.mAlphaPresent) { } bool IsValid() const override { return mDisplay.width > 0 && mDisplay.height > 0; @@ -331,17 +332,17 @@ public: RefPtr<MediaByteBuffer> mCodecSpecificConfig; RefPtr<MediaByteBuffer> mExtraData; // Describing how many degrees video frames should be rotated in clock-wise to // get correct view. Rotation mRotation; // Should be 8, 10 or 12. Default value is 8. - uint8_t mBitDepth = 8; + gfx::ColorDepth mColorDepth = gfx::ColorDepth::COLOR_8; private: // mImage may be cropped; currently only used with the WebM container. // A negative width or height indicate that no cropping is to occur. gfx::IntRect mImageRect; // Indicates whether or not frames may contain alpha information. bool mAlphaPresent = false;
--- a/dom/media/mp4/MP4Decoder.cpp +++ b/dom/media/mp4/MP4Decoder.cpp @@ -6,16 +6,17 @@ #include "MP4Decoder.h" #include "H264.h" #include "MP4Demuxer.h" #include "MediaContainerType.h" #include "PDMFactory.h" #include "VideoUtils.h" #include "mozilla/StaticPrefs.h" +#include "mozilla/gfx/Tools.h" #include "nsMimeTypes.h" namespace mozilla { static bool IsWhitelistedH264Codec(const nsAString& aCodec) { uint8_t profile = 0, constraint = 0, level = 0; @@ -107,17 +108,18 @@ MP4Decoder::GetTracksInfo(const MediaCon if (IsVP9CodecString(codec)) { auto trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/vp9"), aType); uint8_t profile = 0; uint8_t level = 0; uint8_t bitDepth = 0; if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) { - trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth; + trackInfo->GetAsVideoInfo()->mColorDepth = + gfx::ColorDepthForBitDepth(bitDepth); } tracks.AppendElement(std::move(trackInfo)); continue; } #ifdef MOZ_AV1 if (IsAV1CodecString(codec)) { tracks.AppendElement( CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters(
--- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -189,34 +189,34 @@ public: virtual bool Supports(const TrackInfo& aTrackInfo, DecoderDoctorDiagnostics* aDiagnostics) const { if (!SupportsMimeType(aTrackInfo.mMimeType, aDiagnostics)) { return false; } const auto videoInfo = aTrackInfo.GetAsVideoInfo(); - return !videoInfo || SupportsBitDepth(videoInfo->mBitDepth, aDiagnostics); + return !videoInfo || SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics); } protected: PlatformDecoderModule() { } virtual ~PlatformDecoderModule() { } friend class H264Converter; friend class PDMFactory; friend class dom::RemoteDecoderModule; friend class EMEDecoderModule; - // Indicates if the PlatformDecoderModule supports decoding of aBitDepth. - // Should override this method when the platform can support bitDepth != 8. - virtual bool SupportsBitDepth(const uint8_t aBitDepth, - DecoderDoctorDiagnostics* aDiagnostics) const + // Indicates if the PlatformDecoderModule supports decoding of aColorDepth. + // Should override this method when the platform can support color depth != 8. + virtual bool SupportsColorDepth(gfx::ColorDepth aColorDepth, + DecoderDoctorDiagnostics* aDiagnostics) const { - return aBitDepth == 8; + return aColorDepth == gfx::ColorDepth::COLOR_8; } // Creates a Video decoder. The layers backend is passed in so that // decoders can determine whether hardware accelerated decoding can be used. // Asynchronous decoding of video should be done in runnables dispatched // to aVideoTaskQueue. If the task queue isn't needed, the decoder should // not hold a reference to it. // On Windows the task queue's threads in have MSCOM initialized with
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h +++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h @@ -73,28 +73,28 @@ public: if (audioCodec == AV_CODEC_ID_NONE && videoCodec == AV_CODEC_ID_NONE) { return false; } AVCodecID codec = audioCodec != AV_CODEC_ID_NONE ? audioCodec : videoCodec; return !!FFmpegDataDecoder<V>::FindAVCodec(mLib, codec); } protected: - bool SupportsBitDepth(const uint8_t aBitDepth, - DecoderDoctorDiagnostics* aDiagnostics) const override + bool SupportsColorDepth(gfx::ColorDepth aColorDepth, + DecoderDoctorDiagnostics* aDiagnostics) const override { // We don't support bitDepth > 8 when compositor backend is D3D11. // But we don't have KnowsCompositor or any object // that we can ask for the layersbackend type. // We should remove this restriction until // we solve the D3D11 compositor backend issue. #if defined(XP_LINUX) || defined(XP_MACOSX) return true; #endif - return aBitDepth == 8; + return aColorDepth == gfx::ColorDepth::COLOR_8; } private: FFmpegLibWrapper* mLib; }; } // namespace mozilla
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp @@ -293,31 +293,31 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode( #if LIBAVCODEC_VERSION_MAJOR >= 57 || mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE #endif ) { b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = mFrame->width; b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height; if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P10LE) { - b.mBitDepth = 10; + b.mColorDepth = gfx::ColorDepth::COLOR_10; } #if LIBAVCODEC_VERSION_MAJOR >= 57 else if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE) { - b.mBitDepth = 12; + b.mColorDepth = gfx::ColorDepth::COLOR_12; } #endif } else if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV422P) { b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1; b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height; } else { b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1; b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1; if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV420P10LE) { - b.mBitDepth = 10; + b.mColorDepth = gfx::ColorDepth::COLOR_10; } } if (mLib->av_frame_get_colorspace) { switch (mLib->av_frame_get_colorspace(mFrame)) { case AVCOL_SPC_BT709: b.mYUVColorSpace = YUVColorSpace::BT709; break; case AVCOL_SPC_SMPTE170M:
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp +++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp @@ -194,17 +194,17 @@ WMFDecoderModule::SupportsMimeType( return Supports(*trackInfo, aDiagnostics); } bool WMFDecoderModule::Supports(const TrackInfo& aTrackInfo, DecoderDoctorDiagnostics* aDiagnostics) const { const auto videoInfo = aTrackInfo.GetAsVideoInfo(); - if (videoInfo && !SupportsBitDepth(videoInfo->mBitDepth, aDiagnostics)) { + if (videoInfo && !SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics)) { return false; } if ((aTrackInfo.mMimeType.EqualsLiteral("audio/mp4a-latm") || aTrackInfo.mMimeType.EqualsLiteral("audio/mp4")) && WMFDecoderModule::HasAAC()) { const auto audioInfo = aTrackInfo.GetAsAudioInfo(); if (audioInfo && audioInfo->mRate > 0) {
--- a/dom/media/webm/WebMDecoder.cpp +++ b/dom/media/webm/WebMDecoder.cpp @@ -53,17 +53,18 @@ WebMDecoder::GetTracksInfo(const MediaCo trackInfo = CreateTrackInfoWithMIMETypeAndContainerTypeExtraParameters( NS_LITERAL_CSTRING("video/vp8"), aType); } if (trackInfo) { uint8_t profile = 0; uint8_t level = 0; uint8_t bitDepth = 0; if (ExtractVPXCodecDetails(codec, profile, level, bitDepth)) { - trackInfo->GetAsVideoInfo()->mBitDepth = bitDepth; + trackInfo->GetAsVideoInfo()->mColorDepth = + gfx::ColorDepthForBitDepth(bitDepth); } tracks.AppendElement(std::move(trackInfo)); continue; } } #ifdef MOZ_AV1 if (StaticPrefs::MediaAv1Enabled() && IsAV1CodecString(codec)) { tracks.AppendElement( @@ -97,17 +98,17 @@ WebMDecoder::IsSupportedType(const Media } if (tracks.IsEmpty()) { // WebM guarantees that the only codecs it contained are vp8, vp9, opus or vorbis. return true; } // Verify that we have a PDM that supports the whitelisted types, include - // bitdepth + // color depth RefPtr<PDMFactory> platform = new PDMFactory(); for (const auto& track : tracks) { if (!track || !platform->Supports(*track, nullptr /* diagnostic */)) { return false; } } return true;
--- a/dom/presentation/PresentationSessionInfo.cpp +++ b/dom/presentation/PresentationSessionInfo.cpp @@ -1574,17 +1574,17 @@ PresentationPresentingInfo::ResolvedCall // Use Element to support both HTMLIFrameElement and nsXULElement. Element* frame = nullptr; nsresult rv = UNWRAP_OBJECT(Element, &obj, frame); if (NS_WARN_IF(!frame)) { ReplyError(NS_ERROR_DOM_OPERATION_ERR); return; } - nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface((nsIFrameLoaderOwner*) frame); + nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(frame); if (NS_WARN_IF(!owner)) { ReplyError(NS_ERROR_DOM_OPERATION_ERR); return; } RefPtr<nsFrameLoader> frameLoader = owner->GetFrameLoader(); if (NS_WARN_IF(!frameLoader)) { ReplyError(NS_ERROR_DOM_OPERATION_ERR);
--- a/dom/vr/test/mochitest/mochitest.ini +++ b/dom/vr/test/mochitest/mochitest.ini @@ -3,22 +3,30 @@ support-files = VRSimulationDriver.js requestPresent.js runVRTest.js WebVRHelpers.js [test_vrController_displayId.html] # Enable Linux after Bug 1310655 # TIMED_OUT for Android. -skip-if = (os != "win" && release_or_beta) || (os == "android") +# skip-if = (os != "win" && release_or_beta) || (os == "android") +# Re-enable this once Bug 1466702 has landed +skip-if = true [test_vrDisplay_canvas2d.html] -skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 +# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 +# Re-enable this once Bug 1466702 has landed +skip-if = true [test_vrDisplay_exitPresent.html] -skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 +# skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 +# Re-enable this once Bug 1466702 has landed +skip-if = true [test_vrDisplay_getFrameData.html] # Enable Linux after Bug 1310655, enable Android after Bug 1348246 -skip-if = (os != "win" && release_or_beta) || (os == "android") +# skip-if = (os != "win" && release_or_beta) || (os == "android") +# Re-enable this once Bug 1466702 has landed +skip-if = true [test_vrDisplay_onvrdisplayconnect.html] skip-if = true [test_vrDisplay_onvrdisplaydeactivate_crosscontent.html] skip-if = true [test_vrDisplay_requestPresent.html] skip-if = true
--- a/dom/webidl/CaretStateChangedEvent.webidl +++ b/dom/webidl/CaretStateChangedEvent.webidl @@ -24,16 +24,17 @@ dictionary CaretStateChangedEventInit : boolean selectionEditable = false; DOMString selectedTextContent = ""; }; [Constructor(DOMString type, optional CaretStateChangedEventInit eventInit), ChromeOnly] interface CaretStateChangedEvent : Event { readonly attribute boolean collapsed; + /* The bounding client rect is relative to the visual viewport. */ readonly attribute DOMRectReadOnly? boundingClientRect; readonly attribute CaretChangedReason reason; readonly attribute boolean caretVisible; readonly attribute boolean caretVisuallyVisible; readonly attribute boolean selectionVisible; readonly attribute boolean selectionEditable; readonly attribute DOMString selectedTextContent; };
--- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -48,17 +48,17 @@ typedef OfflineResourceList ApplicationC [Replaceable, Throws] readonly attribute BarProp statusbar; [Replaceable, Throws] readonly attribute BarProp toolbar; [Throws] attribute DOMString status; [Throws, CrossOriginCallable] void close(); [Throws, CrossOriginReadable] readonly attribute boolean closed; [Throws] void stop(); [Throws, CrossOriginCallable] void focus(); [Throws, CrossOriginCallable] void blur(); - [Replaceable] readonly attribute any event; + [Replaceable, Pref="dom.window.event.enabled"] readonly attribute any event; // other browsing contexts [Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames; [Replaceable, CrossOriginReadable] readonly attribute unsigned long length; //[Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy top; [Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy? top; [Throws, CrossOriginReadable] attribute any opener; //[Throws] readonly attribute WindowProxy parent;
--- a/gfx/2d/Tools.h +++ b/gfx/2d/Tools.h @@ -105,51 +105,89 @@ BytesPerPixel(SurfaceFormat aFormat) case SurfaceFormat::Depth: return sizeof(uint16_t); default: return 4; } } static inline SurfaceFormat -SurfaceFormatForAlphaBitDepth(uint32_t aBitDepth) +SurfaceFormatForColorDepth(ColorDepth aColorDepth) { - if (aBitDepth == 8) { - return SurfaceFormat::A8; - } else if (aBitDepth == 10 || - aBitDepth == 12) { - return SurfaceFormat::A16; + SurfaceFormat format = SurfaceFormat::A8; + switch (aColorDepth) { + case ColorDepth::COLOR_8: + break; + case ColorDepth::COLOR_10: + case ColorDepth::COLOR_12: + format = SurfaceFormat::A16; + break; + case ColorDepth::UNKNOWN: + MOZ_ASSERT_UNREACHABLE("invalid color depth value"); } - MOZ_ASSERT_UNREACHABLE("Unsupported alpha bit depth"); - return SurfaceFormat::UNKNOWN; + return format; +} + +static inline uint32_t +BitDepthForColorDepth(ColorDepth aColorDepth) +{ + uint32_t depth = 8; + switch (aColorDepth) { + case ColorDepth::COLOR_8: + break; + case ColorDepth::COLOR_10: + depth = 10; + break; + case ColorDepth::COLOR_12: + depth = 12; + break; + case ColorDepth::UNKNOWN: + MOZ_ASSERT_UNREACHABLE("invalid color depth value"); + } + return depth; } static inline ColorDepth -ColorDepthForAlphaBitDepth(uint32_t aBitDepth) +ColorDepthForBitDepth(uint8_t aBitDepth) { + ColorDepth depth = ColorDepth::COLOR_8; switch (aBitDepth) { - case 8: return ColorDepth::COLOR_8; - case 10: return ColorDepth::COLOR_10; - case 12: return ColorDepth::COLOR_12; - default: - MOZ_ASSERT_UNREACHABLE("Unsupported alpha bit depth"); - return ColorDepth::COLOR_8; + case 8: + break; + case 10: + depth = ColorDepth::COLOR_10; + break; + case 12: + depth = ColorDepth::COLOR_12; + break; + default: + MOZ_ASSERT_UNREACHABLE("invalid color depth value"); } + return depth; } // 10 and 12 bits color depth image are using 16 bits integers for storage -// As such we need to rescale the value from 8,10 or 12 to 16. +// As such we need to rescale the value from 10 or 12 bits to 16. static inline uint32_t -RescalingFactorForAlphaBitDepth(uint32_t aBitDepth) +RescalingFactorForColorDepth(ColorDepth aColorDepth) { - MOZ_ASSERT(aBitDepth == 8 || aBitDepth == 10 || aBitDepth == 12); - uint32_t pixelBits = - 8 * BytesPerPixel(SurfaceFormatForAlphaBitDepth(aBitDepth)); - uint32_t paddingBits = pixelBits - aBitDepth; - return pow(2, paddingBits); + uint32_t factor = 1; + switch (aColorDepth) { + case ColorDepth::COLOR_8: + break; + case ColorDepth::COLOR_10: + factor = 64; + break; + case ColorDepth::COLOR_12: + factor = 16; + break; + case ColorDepth::UNKNOWN: + MOZ_ASSERT_UNREACHABLE("invalid color depth value"); + } + return factor; } static inline bool IsOpaqueFormat(SurfaceFormat aFormat) { switch (aFormat) { case SurfaceFormat::B8G8R8X8: case SurfaceFormat::R8G8B8X8: case SurfaceFormat::X8R8G8B8:
--- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -101,17 +101,17 @@ inline bool IsOpaque(SurfaceFormat aForm return false; } } enum class ColorDepth : uint8_t { COLOR_8, COLOR_10, COLOR_12, - MAX + UNKNOWN }; enum class FilterType : int8_t { BLEND = 0, TRANSFORM, MORPHOLOGY, COLOR_MATRIX, FLOOD,
--- a/gfx/ipc/GPUProcessHost.cpp +++ b/gfx/ipc/GPUProcessHost.cpp @@ -155,24 +155,23 @@ GPUProcessHost::InitAfterConnect(bool aS void GPUProcessHost::Shutdown() { MOZ_ASSERT(!mShutdownRequested); mListener = nullptr; if (mGPUChild) { - mGPUChild->SendShutdownVR(); - // OnChannelClosed uses this to check if the shutdown was expected or // unexpected. mShutdownRequested = true; // The channel might already be closed if we got here unexpectedly. if (!mChannelClosed) { + mGPUChild->SendShutdownVR(); mGPUChild->Close(); } #ifndef NS_FREE_PERMANENT_DATA // No need to communicate shutdown, the GPU process doesn't need to // communicate anything back. KillHard("NormalShutdown"); #endif
--- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -700,16 +700,24 @@ template <> struct ParamTraits<mozilla::gfx::SurfaceFormat> : public ContiguousEnumSerializer< mozilla::gfx::SurfaceFormat, mozilla::gfx::SurfaceFormat::B8G8R8A8, mozilla::gfx::SurfaceFormat::UNKNOWN> {}; template <> +struct ParamTraits<mozilla::gfx::ColorDepth> + : public ContiguousEnumSerializer< + mozilla::gfx::ColorDepth, + mozilla::gfx::ColorDepth::COLOR_8, + mozilla::gfx::ColorDepth::UNKNOWN> +{}; + +template <> struct ParamTraits<mozilla::StereoMode> : public ContiguousEnumSerializer< mozilla::StereoMode, mozilla::StereoMode::MONO, mozilla::StereoMode::MAX> {}; template <>
--- a/gfx/layers/BufferTexture.cpp +++ b/gfx/layers/BufferTexture.cpp @@ -1,22 +1,23 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "BufferTexture.h" -#include "mozilla/layers/ImageDataSerializer.h" -#include "mozilla/layers/ISurfaceAllocator.h" -#include "mozilla/layers/CompositableForwarder.h" +#include "libyuv.h" +#include "mozilla/Move.h" +#include "mozilla/fallible.h" +#include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/fallible.h" -#include "libyuv.h" +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/layers/ImageDataSerializer.h" #ifdef MOZ_WIDGET_GTK #include "gfxPlatformGtk.h" #endif namespace mozilla { namespace layers { @@ -163,18 +164,18 @@ BufferTextureData::CreateInternal(Layers BufferTextureData* BufferTextureData::CreateForYCbCr(KnowsCompositor* aAllocator, gfx::IntSize aYSize, uint32_t aYStride, gfx::IntSize aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace, - uint32_t aBitDepth, TextureFlags aTextureFlags) { uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize( aYSize, aYStride, aCbCrSize, aCbCrStride); if (bufSize == 0) { return nullptr; } @@ -195,18 +196,18 @@ BufferTextureData::CreateForYCbCr(KnowsC aAllocator->GetCompositorBackendType(), supportsTextureDirectMapping) : true; YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aYStride, aCbCrSize, aCbCrStride, yOffset, cbOffset, crOffset, aStereoMode, + aColorDepth, aYUVColorSpace, - aBitDepth, hasIntermediateBuffer); return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor, gfx::BackendType::NONE, bufSize, aTextureFlags); @@ -249,20 +250,20 @@ BufferTextureData::GetCbCrSize() const } Maybe<YUVColorSpace> BufferTextureData::GetYUVColorSpace() const { return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor); } -Maybe<uint32_t> -BufferTextureData::GetBitDepth() const +Maybe<gfx::ColorDepth> +BufferTextureData::GetColorDepth() const { - return ImageDataSerializer::BitDepthFromBufferDescriptor(mDescriptor); + return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor); } Maybe<StereoMode> BufferTextureData::GetStereoMode() const { return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor); } @@ -349,17 +350,18 @@ BufferTextureData::BorrowMappedYCbCrData const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); uint8_t* data = GetBuffer(); auto ySize = desc.ySize(); auto cbCrSize = desc.cbCrSize(); aMap.stereoMode = desc.stereoMode(); aMap.metadata = nullptr; - uint32_t bytesPerPixel = desc.bitDepth() > 8 ? 2 : 1; + uint32_t bytesPerPixel = + BytesPerPixel(SurfaceFormatForColorDepth(desc.colorDepth())); aMap.y.data = data + desc.yOffset(); aMap.y.size = ySize; aMap.y.stride = desc.yStride(); aMap.y.skip = 0; aMap.y.bytesPerPixel = bytesPerPixel; aMap.cb.data = data + desc.cbOffset(); @@ -429,21 +431,21 @@ BufferTextureData::UpdateFromSurface(gfx srcSurf->Unmap(); surface->Unmap(); return true; } void -BufferTextureData::SetDesciptor(const BufferDescriptor& aDescriptor) +BufferTextureData::SetDescriptor(BufferDescriptor&& aDescriptor) { MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor); MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize()); - mDescriptor = aDescriptor; + mDescriptor = std::move(aDescriptor); } bool MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN); if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) { return false;
--- a/gfx/layers/BufferTexture.h +++ b/gfx/layers/BufferTexture.h @@ -31,18 +31,18 @@ public: LayersIPCChannel* aAllocator); static BufferTextureData* CreateForYCbCr(KnowsCompositor* aAllocator, gfx::IntSize aYSize, uint32_t aYStride, gfx::IntSize aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace, - uint32_t aBitDepth, TextureFlags aTextureFlags); virtual bool Lock(OpenMode aMode) override { return true; } virtual void Unlock() override {} virtual void FillInfo(TextureData::Info& aInfo) const override; @@ -53,23 +53,23 @@ public: virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override; // use TextureClient's default implementation virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override; virtual BufferTextureData* AsBufferTextureData() override { return this; } // Don't use this. - void SetDesciptor(const BufferDescriptor& aDesc); + void SetDescriptor(BufferDescriptor&& aDesc); Maybe<gfx::IntSize> GetCbCrSize() const; Maybe<YUVColorSpace> GetYUVColorSpace() const; - Maybe<uint32_t> GetBitDepth() const; + Maybe<gfx::ColorDepth> GetColorDepth() const; Maybe<StereoMode> GetStereoMode() const; protected: gfx::IntSize GetSize() const; gfx::SurfaceFormat GetFormat() const;
--- a/gfx/layers/D3D11YCbCrImage.cpp +++ b/gfx/layers/D3D11YCbCrImage.cpp @@ -30,17 +30,17 @@ bool D3D11YCbCrImage::SetData(KnowsCompositor* aAllocator, ImageContainer* aContainer, const PlanarYCbCrData& aData) { mPictureRect = IntRect( aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height); mYSize = aData.mYSize; mCbCrSize = aData.mCbCrSize; - mBitDepth = aData.mBitDepth; + mColorDepth = aData.mColorDepth; mColorSpace = aData.mYUVColorSpace; D3D11YCbCrRecycleAllocator* allocator = aContainer->GetD3D11YCbCrRecycleAllocator(aAllocator); if (!allocator) { return false; } @@ -245,17 +245,17 @@ D3D11YCbCrImage::GetAsSourceSurface() } MOZ_ASSERT(mapCb.RowPitch == mapCr.RowPitch); data.mPicX = mPictureRect.X(); data.mPicY = mPictureRect.Y(); data.mPicSize = mPictureRect.Size(); data.mStereoMode = StereoMode::MONO; - data.mBitDepth = mBitDepth; + data.mColorDepth = mColorDepth; data.mYUVColorSpace = mColorSpace; data.mYSkip = data.mCbSkip = data.mCrSkip = 0; data.mYSize = mYSize; data.mCbCrSize = mCbCrSize; data.mYChannel = static_cast<uint8_t*>(mapY.pData); data.mYStride = mapY.RowPitch; data.mCbChannel = static_cast<uint8_t*>(mapCb.pData); data.mCrChannel = static_cast<uint8_t*>(mapCr.pData); @@ -350,17 +350,17 @@ DXGIYCbCrTextureAllocationHelper::IsComp { MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV); DXGIYCbCrTextureData* dxgiData = aTextureClient->GetInternalData()->AsDXGIYCbCrTextureData(); if (!dxgiData || aTextureClient->GetSize() != mData.mYSize || dxgiData->GetYSize() != mData.mYSize || dxgiData->GetCbCrSize() != mData.mCbCrSize || - dxgiData->GetBitDepth() != mData.mBitDepth || + dxgiData->GetColorDepth() != mData.mColorDepth || dxgiData->GetYUVColorSpace() != mData.mYUVColorSpace) { return false; } ID3D11Texture2D* textureY = dxgiData->GetD3D11Texture(0); ID3D11Texture2D* textureCb = dxgiData->GetD3D11Texture(1); ID3D11Texture2D* textureCr = dxgiData->GetD3D11Texture(2); @@ -384,22 +384,23 @@ DXGIYCbCrTextureAllocationHelper::IsComp } return true; } already_AddRefed<TextureClient> DXGIYCbCrTextureAllocationHelper::Allocate(KnowsCompositor* aAllocator) { - CD3D11_TEXTURE2D_DESC newDesc( - mData.mBitDepth == 8 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_R16_UNORM, - mData.mYSize.width, - mData.mYSize.height, - 1, - 1); + CD3D11_TEXTURE2D_DESC newDesc(mData.mColorDepth == gfx::ColorDepth::COLOR_8 + ? DXGI_FORMAT_R8_UNORM + : DXGI_FORMAT_R16_UNORM, + mData.mYSize.width, + mData.mYSize.height, + 1, + 1); // WebRender requests keyed mutex if (mDevice == gfx::DeviceManagerDx::Get()->GetCompositorDevice() && !gfxVars::UseWebRender()) { newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; } else { newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; } @@ -439,17 +440,17 @@ DXGIYCbCrTextureAllocationHelper::Alloca return TextureClient::CreateWithData( DXGIYCbCrTextureData::Create( textureY, textureCb, textureCr, mData.mYSize, mData.mYSize, mData.mCbCrSize, - mData.mBitDepth, + mData.mColorDepth, mData.mYUVColorSpace), mTextureFlags, forwarder); } already_AddRefed<TextureClient> D3D11YCbCrRecycleAllocator::Allocate(SurfaceFormat aFormat, IntSize aSize,
--- a/gfx/layers/D3D11YCbCrImage.h +++ b/gfx/layers/D3D11YCbCrImage.h @@ -79,17 +79,17 @@ public: gfx::IntRect GetPictureRect() const override { return mPictureRect; } private: const DXGIYCbCrTextureData* GetData() const; gfx::IntSize mYSize; gfx::IntSize mCbCrSize; gfx::IntRect mPictureRect; - uint32_t mBitDepth; + gfx::ColorDepth mColorDepth; YUVColorSpace mColorSpace; RefPtr<TextureClient> mTextureClient; }; } // namepace layers } // namespace mozilla #endif // GFX_D3D11_YCBCR_IMAGE_H
--- a/gfx/layers/Effects.h +++ b/gfx/layers/Effects.h @@ -157,26 +157,30 @@ struct EffectRGB : public TexturedEffect : TexturedEffect(EffectTypes::RGB, aTexture, aPremultiplied, aSamplingFilter) {} virtual const char* Name() override { return "EffectRGB"; } }; struct EffectYCbCr : public TexturedEffect { - EffectYCbCr(TextureSource *aSource, YUVColorSpace aYUVColorSpace, uint32_t aBitDepth, gfx::SamplingFilter aSamplingFilter) + EffectYCbCr(TextureSource* aSource, + YUVColorSpace aYUVColorSpace, + gfx::ColorDepth aColorDepth, + gfx::SamplingFilter aSamplingFilter) : TexturedEffect(EffectTypes::YCBCR, aSource, false, aSamplingFilter) , mYUVColorSpace(aYUVColorSpace) - , mBitDepth(aBitDepth) - {} + , mColorDepth(aColorDepth) + { + } virtual const char* Name() override { return "EffectYCbCr"; } YUVColorSpace mYUVColorSpace; - uint32_t mBitDepth; + gfx::ColorDepth mColorDepth; }; struct EffectNV12 : public TexturedEffect { EffectNV12(TextureSource *aSource, gfx::SamplingFilter aSamplingFilter) : TexturedEffect(EffectTypes::NV12, aSource, false, aSamplingFilter) {} @@ -268,18 +272,20 @@ CreateTexturedEffect(TextureHost* aHost, bool isAlphaPremultiplied) { MOZ_ASSERT(aHost); MOZ_ASSERT(aSource); RefPtr<TexturedEffect> result; if (aHost->GetReadFormat() == gfx::SurfaceFormat::YUV) { MOZ_ASSERT(aHost->GetYUVColorSpace() != YUVColorSpace::UNKNOWN); - result = new EffectYCbCr( - aSource, aHost->GetYUVColorSpace(), aHost->GetBitDepth(), aSamplingFilter); + result = new EffectYCbCr(aSource, + aHost->GetYUVColorSpace(), + aHost->GetColorDepth(), + aSamplingFilter); } else { result = CreateTexturedEffect(aHost->GetReadFormat(), aSource, aSamplingFilter, isAlphaPremultiplied); } return result.forget(); }
--- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -11,16 +11,17 @@ #include <sys/types.h> // for int32_t #include "gfxTypes.h" #include "ImageTypes.h" // for ImageFormat, etc #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 #include "mozilla/Mutex.h" // for Mutex #include "mozilla/RecursiveMutex.h" // for RecursiveMutex, etc #include "mozilla/TimeStamp.h" // for TimeStamp #include "mozilla/gfx/Point.h" // For IntSize +#include "mozilla/gfx/Types.h" // For ColorDepth #include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc #include "mozilla/layers/CompositorTypes.h" #include "mozilla/mozalloc.h" // for operator delete, etc #include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc #include "nsAutoRef.h" // for nsCountedRef #include "nsCOMPtr.h" // for already_AddRefed #include "nsDebug.h" // for NS_ASSERTION #include "nsISupportsImpl.h" // for Image::Release, etc @@ -760,32 +761,32 @@ struct PlanarYCbCrData int32_t mCbSkip; int32_t mCrSkip; // Picture region uint32_t mPicX; uint32_t mPicY; gfx::IntSize mPicSize; StereoMode mStereoMode; YUVColorSpace mYUVColorSpace; - uint32_t mBitDepth; + gfx::ColorDepth mColorDepth; gfx::IntRect GetPictureRect() const { return gfx::IntRect(mPicX, mPicY, mPicSize.width, mPicSize.height); } PlanarYCbCrData() : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0) , mCbChannel(nullptr), mCrChannel(nullptr) , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0) , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO) , mYUVColorSpace(YUVColorSpace::BT601) - , mBitDepth(8) + , mColorDepth(gfx::ColorDepth::COLOR_8) {} }; /****** Image subtypes for the different formats ******/ /** * We assume that the image data is in the REC 470M color space (see * Theora specification, section 4.3.1).
--- a/gfx/layers/ImageDataSerializer.cpp +++ b/gfx/layers/ImageDataSerializer.cpp @@ -172,25 +172,25 @@ Maybe<YUVColorSpace> YUVColorSpaceFromBu return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace()); default: MOZ_CRASH("GFX: YUVColorSpaceFromBufferDescriptor"); } } -Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor) +Maybe<gfx::ColorDepth> ColorDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: - return Some(aDescriptor.get_YCbCrDescriptor().bitDepth()); + return Some(aDescriptor.get_YCbCrDescriptor().colorDepth()); default: - MOZ_CRASH("GFX: BitDepthFromBufferDescriptor"); + MOZ_CRASH("GFX: ColorDepthFromBufferDescriptor"); } } Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); @@ -249,17 +249,17 @@ DataSourceSurfaceFromYCbCrDescriptor(uin ycbcrData.mYStride = aDescriptor.yStride(); ycbcrData.mYSize = ySize; ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); ycbcrData.mCbCrSize = aDescriptor.cbCrSize(); ycbcrData.mPicSize = ySize; ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); - ycbcrData.mBitDepth = aDescriptor.bitDepth(); + ycbcrData.mColorDepth = aDescriptor.colorDepth(); gfx::ConvertYCbCrToRGB(ycbcrData, gfx::SurfaceFormat::B8G8R8X8, ySize, map.mData, map.mStride); result->Unmap(); @@ -281,16 +281,16 @@ ConvertAndScaleFromYCbCrDescriptor(uint8 ycbcrData.mYStride = aDescriptor.yStride();; ycbcrData.mYSize = aDescriptor.ySize(); ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); ycbcrData.mCbCrSize = aDescriptor.cbCrSize(); ycbcrData.mPicSize = aDescriptor.ySize(); ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); - ycbcrData.mBitDepth = aDescriptor.bitDepth(); + ycbcrData.mColorDepth = aDescriptor.colorDepth(); gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride); } } // namespace ImageDataSerializer } // namespace layers } // namespace mozilla
--- a/gfx/layers/ImageDataSerializer.h +++ b/gfx/layers/ImageDataSerializer.h @@ -60,17 +60,17 @@ void ComputeYCbCrOffsets(int32_t yStride gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor); gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor); Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor); Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor); -Maybe<uint32_t> BitDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor); +Maybe<gfx::ColorDepth> ColorDepthFromBufferDescriptor(const BufferDescriptor& aDescriptor); Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor); uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor); uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor); uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
--- a/gfx/layers/TextureSourceProvider.cpp +++ b/gfx/layers/TextureSourceProvider.cpp @@ -41,32 +41,25 @@ TextureSourceProvider::ReadUnlockTexture for (auto it = texturesIdsToUnlockByPid.ConstIter(); !it.Done(); it.Next()) { TextureSync::SetTexturesUnlocked(it.Key(), *it.UserData()); } #else for (auto& texture : mUnlockAfterComposition) { texture->ReadUnlock(); } #endif - mReferenceUntilAfterComposition.Clear(); mUnlockAfterComposition.Clear(); } void TextureSourceProvider::UnlockAfterComposition(TextureHost* aTexture) { mUnlockAfterComposition.AppendElement(aTexture); } -void -TextureSourceProvider::ReferenceUntilAfterComposition(DataTextureSource* aTextureSource) -{ - mReferenceUntilAfterComposition.AppendElement(aTextureSource); -} - bool TextureSourceProvider::NotifyNotUsedAfterComposition(TextureHost* aTextureHost) { mNotifyNotUsedAfterComposition.AppendElement(aTextureHost); // If Compositor holds many TextureHosts without compositing, // the TextureHosts should be flushed to reduce memory consumption. const int thresholdCount = 5;
--- a/gfx/layers/TextureSourceProvider.h +++ b/gfx/layers/TextureSourceProvider.h @@ -59,23 +59,16 @@ public: /// Most compositor backends operate asynchronously under the hood. This /// means that when a layer stops using a texture it is often desirable to /// wait for the end of the next composition before releasing the texture's /// ReadLock. /// This function provides a convenient way to do this delayed unlocking, if /// the texture itself requires it. virtual void UnlockAfterComposition(TextureHost* aTexture); - /// This is used for client storage support on OSX. On Nvidia hardware, it - /// seems that if we glDeleteTextures on a texture too early, even if we've - /// waited on the texture with glFinishObjectAPPLE, we'll see visual defects. - /// This just holds a reference to the texture until ReadUnlockTextures, - /// but we don't need to unlock it since we have already done so. - void ReferenceUntilAfterComposition(DataTextureSource* aTextureSource); - /// Most compositor backends operate asynchronously under the hood. This /// means that when a layer stops using a texture it is often desirable to /// wait for the end of the next composition before NotifyNotUsed() call. /// This function provides a convenient way to do this delayed NotifyNotUsed() /// call, if the texture itself requires it. /// See bug 1260611 and bug 1252835 /// /// Returns true if notified, false otherwise. @@ -133,19 +126,16 @@ protected: void ReadUnlockTextures(); virtual ~TextureSourceProvider(); private: // An array of locks that will need to be unlocked after the next composition. nsTArray<RefPtr<TextureHost>> mUnlockAfterComposition; - // See ReferenceUntilAfterComposition. - nsTArray<RefPtr<DataTextureSource>> mReferenceUntilAfterComposition; - // An array of TextureHosts that will need to call NotifyNotUsed() after the next composition. nsTArray<RefPtr<TextureHost>> mNotifyNotUsedAfterComposition; }; } // namespace layers } // namespace mozilla #endif // mozilla_gfx_layers_TextureSourceProvider_h
--- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -100,18 +100,18 @@ ImageClient::CreateTextureClientForImage const PlanarYCbCrData* data = ycbcr->GetData(); if (!data) { return nullptr; } texture = TextureClient::CreateForYCbCr(aForwarder, data->mYSize, data->mYStride, data->mCbCrSize, data->mCbCrStride, data->mStereoMode, + data->mColorDepth, data->mYUVColorSpace, - data->mBitDepth, TextureFlags::DEFAULT); if (!texture) { return nullptr; } TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY); if (!autoLock.Succeeded()) { return nullptr;
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -1300,34 +1300,37 @@ TextureClient::CreateForRawBufferAccess( // static already_AddRefed<TextureClient> TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator, gfx::IntSize aYSize, uint32_t aYStride, gfx::IntSize aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace, - uint32_t aBitDepth, TextureFlags aTextureFlags) { if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) { return nullptr; } if (!gfx::Factory::AllowedSurfaceSize(aYSize)) { return nullptr; } - TextureData* data = - BufferTextureData::CreateForYCbCr(aAllocator, - aYSize, aYStride, - aCbCrSize, aCbCrStride, - aStereoMode, aYUVColorSpace, - aBitDepth, aTextureFlags); + TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, + aYSize, + aYStride, + aCbCrSize, + aCbCrStride, + aStereoMode, + aColorDepth, + aYUVColorSpace, + aTextureFlags); if (!data) { return nullptr; } return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator->GetTextureForwarder()); } @@ -1791,33 +1794,34 @@ UpdateYCbCrTextureClient(TextureClient* MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip); MappedYCbCrTextureData mapped; if (!aTexture->BorrowMappedYCbCrData(mapped)) { NS_WARNING("Failed to extract YCbCr info!"); return false; } + uint32_t bytesPerPixel = + BytesPerPixel(SurfaceFormatForColorDepth(aData.mColorDepth)); MappedYCbCrTextureData srcData; srcData.y.data = aData.mYChannel; srcData.y.size = aData.mYSize; srcData.y.stride = aData.mYStride; srcData.y.skip = aData.mYSkip; - MOZ_ASSERT(aData.mBitDepth == 8 || (aData.mBitDepth > 8 && aData.mBitDepth <= 16)); - srcData.y.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1; + srcData.y.bytesPerPixel = bytesPerPixel; srcData.cb.data = aData.mCbChannel; srcData.cb.size = aData.mCbCrSize; srcData.cb.stride = aData.mCbCrStride; srcData.cb.skip = aData.mCbSkip; - srcData.cb.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1; + srcData.cb.bytesPerPixel = bytesPerPixel; srcData.cr.data = aData.mCrChannel; srcData.cr.size = aData.mCbCrSize; srcData.cr.stride = aData.mCbCrStride; srcData.cr.skip = aData.mCrSkip; - srcData.cr.bytesPerPixel = (aData.mBitDepth > 8) ? 2 : 1; + srcData.cr.bytesPerPixel = bytesPerPixel; srcData.metadata = nullptr; if (!srcData.CopyInto(mapped)) { NS_WARNING("Failed to copy image data!"); return false; } if (TextureRequiresLocking(aTexture->GetFlags())) {
--- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -360,18 +360,18 @@ public: // Creates and allocates a TextureClient supporting the YCbCr format. static already_AddRefed<TextureClient> CreateForYCbCr(KnowsCompositor* aAllocator, gfx::IntSize aYSize, uint32_t aYStride, gfx::IntSize aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace, - uint32_t aBitDepth, TextureFlags aTextureFlags); // Creates and allocates a TextureClient (can be accessed through raw // pointers). static already_AddRefed<TextureClient> CreateForRawBufferAccess(KnowsCompositor* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp +++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp @@ -104,34 +104,34 @@ YCbCrTextureClientAllocationHelper::IsCo BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData(); if (!bufferData || aTextureClient->GetSize() != mData.mYSize || bufferData->GetCbCrSize().isNothing() || bufferData->GetCbCrSize().ref() != mData.mCbCrSize || bufferData->GetYUVColorSpace().isNothing() || bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace || - bufferData->GetBitDepth().isNothing() || - bufferData->GetBitDepth().ref() != mData.mBitDepth || + bufferData->GetColorDepth().isNothing() || + bufferData->GetColorDepth().ref() != mData.mColorDepth || bufferData->GetStereoMode().isNothing() || bufferData->GetStereoMode().ref() != mData.mStereoMode) { return false; } return true; } already_AddRefed<TextureClient> YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator) { return TextureClient::CreateForYCbCr(aAllocator, mData.mYSize, mData.mYStride, mData.mCbCrSize, mData.mCbCrStride, mData.mStereoMode, + mData.mColorDepth, mData.mYUVColorSpace, - mData.mBitDepth, mTextureFlags); } TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator) : mSurfaceAllocator(aAllocator) , mMaxPooledSize(kMaxPooledSized) , mLock("TextureClientRecycleAllocatorImp.mLock") , mIsDestroyed(false)
--- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -922,20 +922,16 @@ BufferTextureHost::MaybeNotifyUnlocked() void BufferTextureHost::UnbindTextureSource() { if (mFirstSource && mFirstSource->IsOwnedBy(this)) { mFirstSource->Unbind(); } - if (mFirstSource && mFirstSource->IsDirectMap() && mProvider) { - mProvider->ReferenceUntilAfterComposition(mFirstSource); - } - // This texture is not used by any layer anymore. // If the texture doesn't have an intermediate buffer, it means we are // compositing synchronously on the CPU, so we don't need to wait until // the end of the next composition to ReadUnlock (which other textures do // by default). // If the texture has an intermediate buffer we don't care either because // texture uploads are also performed synchronously for BufferTextureHost. ReadUnlock(); @@ -963,24 +959,24 @@ BufferTextureHost::GetYUVColorSpace() co { if (mFormat == gfx::SurfaceFormat::YUV) { const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); return desc.yUVColorSpace(); } return YUVColorSpace::UNKNOWN; } -uint32_t -BufferTextureHost::GetBitDepth() const +gfx::ColorDepth +BufferTextureHost::GetColorDepth() const { if (mFormat == gfx::SurfaceFormat::YUV) { const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); - return desc.bitDepth(); + return desc.colorDepth(); } - return 8; + return gfx::ColorDepth::COLOR_8; } bool BufferTextureHost::UploadIfNeeded() { return MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr); } @@ -1083,27 +1079,27 @@ BufferTextureHost::Upload(nsIntRegion *a srcU = mFirstSource->GetNextSibling()->AsDataTextureSource(); srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource(); } RefPtr<gfx::DataSourceSurface> tempY = gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc), desc.yStride(), desc.ySize(), - SurfaceFormatForAlphaBitDepth(desc.bitDepth())); + SurfaceFormatForColorDepth(desc.colorDepth())); RefPtr<gfx::DataSourceSurface> tempCb = gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc), desc.cbCrStride(), desc.cbCrSize(), - SurfaceFormatForAlphaBitDepth(desc.bitDepth())); + SurfaceFormatForColorDepth(desc.colorDepth())); RefPtr<gfx::DataSourceSurface> tempCr = gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc), desc.cbCrStride(), desc.cbCrSize(), - SurfaceFormatForAlphaBitDepth(desc.bitDepth())); + SurfaceFormatForColorDepth(desc.colorDepth())); // We don't support partial updates for Y U V textures NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures"); if (!tempY || !tempCb || !tempCr || !srcY->Update(tempY) || !srcU->Update(tempCb) || !srcV->Update(tempCr)) {
--- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -446,19 +446,19 @@ public: * Return the format used for reading the texture. * Apple's YCBCR_422 is R8G8B8X8. */ virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); } virtual YUVColorSpace GetYUVColorSpace() const { return YUVColorSpace::UNKNOWN; } /** - * Return the bit depth of the image. Used with YUV textures. + * Return the color depth of the image. Used with YUV textures. */ - virtual uint32_t GetBitDepth() const { return 8; } + virtual gfx::ColorDepth GetColorDepth() const { return gfx::ColorDepth::COLOR_8; } /** * Called during the transaction. The TextureSource may or may not be composited. * * Note that this is called outside of lock/unlock. */ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {} @@ -752,17 +752,17 @@ public: * * If the shared format is YCbCr and the compositor does not support it, * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV). */ virtual gfx::SurfaceFormat GetFormat() const override; virtual YUVColorSpace GetYUVColorSpace() const override; - virtual uint32_t GetBitDepth() const override; + virtual gfx::ColorDepth GetColorDepth() const override; virtual gfx::IntSize GetSize() const override { return mSize; } virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override; virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; } virtual BufferTextureHost* AsBufferTextureHost() override { return this; }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -893,17 +893,17 @@ CompositorD3D11::DrawGeometry(const Geom return; } const float* yuvToRgb = gfxUtils::YuvToRgbMatrix4x3RowMajor(ycbcrEffect->mYUVColorSpace); memcpy(&mPSConstants.yuvColorMatrix, yuvToRgb, sizeof(mPSConstants.yuvColorMatrix)); // Adjust range according to the bit depth. mPSConstants.vCoefficient[0] = - RescalingFactorForAlphaBitDepth(ycbcrEffect->mBitDepth); + RescalingFactorForColorDepth(ycbcrEffect->mColorDepth); TextureSourceD3D11* sourceY = source->GetSubSource(Y)->AsSourceD3D11(); TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11(); TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11(); ID3D11ShaderResourceView* srViews[3] = { sourceY->GetShaderResourceView(), sourceCb->GetShaderResourceView(), sourceCr->GetShaderResourceView() };
--- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -621,17 +621,17 @@ DXGIYCbCrTextureData::Create(IDirect3DTe IDirect3DTexture9* aTextureCb, IDirect3DTexture9* aTextureCr, HANDLE aHandleY, HANDLE aHandleCb, HANDLE aHandleCr, const gfx::IntSize& aSize, const gfx::IntSize& aSizeY, const gfx::IntSize& aSizeCbCr, - uint32_t aBitDepth, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace) { if (!aHandleY || !aHandleCb || !aHandleCr || !aTextureY || !aTextureCb || !aTextureCr) { return nullptr; } DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData(); @@ -639,30 +639,30 @@ DXGIYCbCrTextureData::Create(IDirect3DTe texture->mHandles[1] = aHandleCb; texture->mHandles[2] = aHandleCr; texture->mD3D9Textures[0] = aTextureY; texture->mD3D9Textures[1] = aTextureCb; texture->mD3D9Textures[2] = aTextureCr; texture->mSize = aSize; texture->mSizeY = aSizeY; texture->mSizeCbCr = aSizeCbCr; - texture->mBitDepth = aBitDepth; + texture->mColorDepth = aColorDepth; texture->mYUVColorSpace = aYUVColorSpace; return texture; } DXGIYCbCrTextureData* DXGIYCbCrTextureData::Create(ID3D11Texture2D* aTextureY, ID3D11Texture2D* aTextureCb, ID3D11Texture2D* aTextureCr, const gfx::IntSize& aSize, const gfx::IntSize& aSizeY, const gfx::IntSize& aSizeCbCr, - uint32_t aBitDepth, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace) { if (!aTextureY || !aTextureCb || !aTextureCr) { return nullptr; } aTextureY->SetPrivateDataInterface(sD3D11TextureUsage, new TextureMemoryMeasurer(aSize.width * aSize.height)); @@ -701,17 +701,17 @@ DXGIYCbCrTextureData::Create(ID3D11Textu texture->mHandles[1] = handleCb; texture->mHandles[2] = handleCr; texture->mD3D11Textures[0] = aTextureY; texture->mD3D11Textures[1] = aTextureCb; texture->mD3D11Textures[2] = aTextureCr; texture->mSize = aSize; texture->mSizeY = aSizeY; texture->mSizeCbCr = aSizeCbCr; - texture->mBitDepth = aBitDepth; + texture->mColorDepth = aColorDepth; texture->mYUVColorSpace = aYUVColorSpace; return texture; } void DXGIYCbCrTextureData::FillInfo(TextureData::Info& aInfo) const { @@ -720,20 +720,24 @@ DXGIYCbCrTextureData::FillInfo(TextureDa aInfo.supportsMoz2D = false; aInfo.hasIntermediateBuffer = false; aInfo.hasSynchronization = false; } void DXGIYCbCrTextureData::SerializeSpecific(SurfaceDescriptorDXGIYCbCr* const aOutDesc) { - *aOutDesc = SurfaceDescriptorDXGIYCbCr( - (WindowsHandle)mHandles[0], (WindowsHandle)mHandles[1], (WindowsHandle)mHandles[2], - mSize, mSizeY, mSizeCbCr, mBitDepth, mYUVColorSpace - ); + *aOutDesc = SurfaceDescriptorDXGIYCbCr((WindowsHandle)mHandles[0], + (WindowsHandle)mHandles[1], + (WindowsHandle)mHandles[2], + mSize, + mSizeY, + mSizeCbCr, + mColorDepth, + mYUVColorSpace); } bool DXGIYCbCrTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { SurfaceDescriptorDXGIYCbCr desc; SerializeSpecific(&desc); @@ -1171,17 +1175,17 @@ DXGITextureHostD3D11::PushDisplayItems(w } DXGIYCbCrTextureHostD3D11::DXGIYCbCrTextureHostD3D11(TextureFlags aFlags, const SurfaceDescriptorDXGIYCbCr& aDescriptor) : TextureHost(aFlags) , mSize(aDescriptor.size()) , mSizeCbCr(aDescriptor.sizeCbCr()) , mIsLocked(false) - , mBitDepth(aDescriptor.bitDepth()) + , mColorDepth(aDescriptor.colorDepth()) , mYUVColorSpace(aDescriptor.yUVColorSpace()) { mHandles[0] = aDescriptor.handleY(); mHandles[1] = aDescriptor.handleCb(); mHandles[2] = aDescriptor.handleCr(); } bool
--- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -140,27 +140,27 @@ public: IDirect3DTexture9* aTextureCb, IDirect3DTexture9* aTextureCr, HANDLE aHandleY, HANDLE aHandleCb, HANDLE aHandleCr, const gfx::IntSize& aSize, const gfx::IntSize& aSizeY, const gfx::IntSize& aSizeCbCr, - uint32_t aBitDepth, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace); static DXGIYCbCrTextureData* Create(ID3D11Texture2D* aTextureCb, ID3D11Texture2D* aTextureY, ID3D11Texture2D* aTextureCr, const gfx::IntSize& aSize, const gfx::IntSize& aSizeY, const gfx::IntSize& aSizeCbCr, - uint32_t aBitDepth, + gfx::ColorDepth aColorDepth, YUVColorSpace aYUVColorSpace); virtual bool Lock(OpenMode) override { return true; } virtual void Unlock() override {} virtual void FillInfo(TextureData::Info& aInfo) const override; @@ -188,36 +188,36 @@ public: return mSizeY; } gfx::IntSize GetCbCrSize() const { return mSizeCbCr; } - uint32_t GetBitDepth() const + gfx::ColorDepth GetColorDepth() const { - return mBitDepth; + return mColorDepth; } YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; } ID3D11Texture2D* GetD3D11Texture(size_t index) { return mD3D11Textures[index]; } protected: RefPtr<ID3D11Texture2D> mD3D11Textures[3]; RefPtr<IDirect3DTexture9> mD3D9Textures[3]; HANDLE mHandles[3]; gfx::IntSize mSize; gfx::IntSize mSizeY; gfx::IntSize mSizeCbCr; - uint32_t mBitDepth; + gfx::ColorDepth mColorDepth; YUVColorSpace mYUVColorSpace; }; /** * TextureSource that provides with the necessary APIs to be composited by a * CompositorD3D11. */ class TextureSourceD3D11 @@ -410,19 +410,19 @@ public: virtual bool AcquireTextureSource(CompositableTextureSourceRef& aTexture) override; virtual void DeallocateDeviceData() override{} virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) override; virtual gfx::SurfaceFormat GetFormat() const override{ return gfx::SurfaceFormat::YUV; } - virtual YUVColorSpace GetYUVColorSpace() const override { return mYUVColorSpace; } + virtual gfx::ColorDepth GetColorDepth() const override { return mColorDepth; } - virtual uint32_t GetBitDepth() const override { return mBitDepth; } + virtual YUVColorSpace GetYUVColorSpace() const override { return mYUVColorSpace; } virtual bool Lock() override; virtual void Unlock() override; virtual gfx::IntSize GetSize() const override { return mSize; } virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override @@ -457,17 +457,17 @@ protected: RefPtr<ID3D11Texture2D> mTextures[3]; RefPtr<DataTextureSourceD3D11> mTextureSources[3]; gfx::IntSize mSize; gfx::IntSize mSizeCbCr; WindowsHandle mHandles[3]; bool mIsLocked; - uint32_t mBitDepth; + gfx::ColorDepth mColorDepth; YUVColorSpace mYUVColorSpace; }; class CompositingRenderTargetD3D11 : public CompositingRenderTarget, public TextureSourceD3D11 { public: CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -4,16 +4,17 @@ using struct gfxPoint from "gfxPoint.h"; using nsIntRegion from "nsRegion.h"; using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h"; using mozilla::StereoMode from "ImageTypes.h"; using mozilla::YUVColorSpace from "ImageTypes.h"; using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h"; +using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h"; using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h"; using gfxImageFormat from "gfxTypes.h"; namespace mozilla { namespace layers { @@ -47,17 +48,17 @@ struct SurfaceDescriptorD3D10 { struct SurfaceDescriptorDXGIYCbCr { WindowsHandle handleY; WindowsHandle handleCb; WindowsHandle handleCr; IntSize size; IntSize sizeY; IntSize sizeCbCr; - uint32_t bitDepth; + ColorDepth colorDepth; YUVColorSpace yUVColorSpace; }; struct SurfaceDescriptorMacIOSurface { uint32_t surfaceId; double scaleFactor; bool isOpaque; }; @@ -107,18 +108,18 @@ struct YCbCrDescriptor { IntSize ySize; uint32_t yStride; IntSize cbCrSize; uint32_t cbCrStride; uint32_t yOffset; uint32_t cbOffset; uint32_t crOffset; StereoMode stereoMode; + ColorDepth colorDepth; YUVColorSpace yUVColorSpace; - uint32_t bitDepth; bool hasIntermediateBuffer; }; union BufferDescriptor { RGBDescriptor; YCbCrDescriptor; };
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -119,27 +119,27 @@ SharedPlanarYCbCrImage::AdoptData(const std::max(aData.mYSize.width, std::max(aData.mYSize.height, std::max(aData.mCbCrSize.width, aData.mCbCrSize.height))) <= fwd->GetMaxTextureSize(); bool hasIntermediateBuffer = ComputeHasIntermediateBuffer( gfx::SurfaceFormat::YUV, fwd->GetCompositorBackendType(), supportsTextureDirectMapping); static_cast<BufferTextureData*>(mTextureClient->GetInternalData()) - ->SetDesciptor(YCbCrDescriptor(aData.mYSize, - aData.mYStride, - aData.mCbCrSize, - aData.mCbCrStride, - yOffset, - cbOffset, - crOffset, - aData.mStereoMode, - aData.mYUVColorSpace, - aData.mBitDepth, - hasIntermediateBuffer)); + ->SetDescriptor(YCbCrDescriptor(aData.mYSize, + aData.mYStride, + aData.mCbCrSize, + aData.mCbCrStride, + yOffset, + cbOffset, + crOffset, + aData.mStereoMode, + aData.mColorDepth, + aData.mYUVColorSpace, + hasIntermediateBuffer)); return true; } bool SharedPlanarYCbCrImage::IsValid() const { return mTextureClient && mTextureClient->IsValid(); @@ -186,17 +186,17 @@ SharedPlanarYCbCrImage::Allocate(PlanarY mData.mCrChannel = aData.mCrChannel; mData.mYSize = aData.mYSize; mData.mCbCrSize = aData.mCbCrSize; mData.mPicX = aData.mPicX; mData.mPicY = aData.mPicY; mData.mPicSize = aData.mPicSize; mData.mStereoMode = aData.mStereoMode; mData.mYUVColorSpace = aData.mYUVColorSpace; - mData.mBitDepth = aData.mBitDepth; + mData.mColorDepth = aData.mColorDepth; // those members are not always equal to aData's, due to potentially different // packing. mData.mYSkip = 0; mData.mCbSkip = 0; mData.mCrSkip = 0; mData.mYStride = aData.mYStride; mData.mCbCrStride = aData.mCbCrStride;
--- a/gfx/layers/mlgpu/MLGDevice.cpp +++ b/gfx/layers/mlgpu/MLGDevice.cpp @@ -290,33 +290,32 @@ MLGDevice::GetBufferForColorSpace(YUVCol return nullptr; } mColorSpaceBuffers[aColorSpace] = resource; return resource; } RefPtr<MLGBuffer> -MLGDevice::GetBufferForBitDepthCoefficient(uint8_t aBitDepth) +MLGDevice::GetBufferForColorDepthCoefficient(ColorDepth aColorDepth) { - ColorDepth depth = ColorDepthForAlphaBitDepth(aBitDepth); - if (mColorDepthBuffers[depth]) { - return mColorDepthBuffers[depth]; + if (mColorDepthBuffers[aColorDepth]) { + return mColorDepthBuffers[aColorDepth]; } - YCbCrBitDepthConstants buffer; - buffer.coefficient = gfx::RescalingFactorForAlphaBitDepth(aBitDepth); + YCbCrColorDepthConstants buffer; + buffer.coefficient = gfx::RescalingFactorForColorDepth(aColorDepth); RefPtr<MLGBuffer> resource = CreateBuffer( MLGBufferType::Constant, sizeof(buffer), MLGUsage::Immutable, &buffer); if (!resource) { return nullptr; } - mColorDepthBuffers[depth] = resource; + mColorDepthBuffers[aColorDepth] = resource; return resource; } bool MLGDevice::Synchronize() { return true; }
--- a/gfx/layers/mlgpu/MLGDevice.h +++ b/gfx/layers/mlgpu/MLGDevice.h @@ -357,17 +357,18 @@ public: void SetPSTexture(uint32_t aSlot, TextureSource* aSource); void SetSamplerMode(uint32_t aIndex, gfx::SamplingFilter aFilter); // This creates or returns a previously created constant buffer, containing // a YCbCrShaderConstants instance. RefPtr<MLGBuffer> GetBufferForColorSpace(YUVColorSpace aColorSpace); // This creates or returns a previously created constant buffer, containing // a YCbCrBitDepthConstants instance. - RefPtr<MLGBuffer> GetBufferForBitDepthCoefficient(uint8_t aBitDepth); + RefPtr<MLGBuffer> GetBufferForColorDepthCoefficient( + gfx::ColorDepth aColorDepth); // A shared buffer that can be used to build VertexBufferSections. SharedVertexBuffer* GetSharedVertexBuffer() { return mSharedVertexBuffer.get(); } // A shared buffer that can be used to build ConstantBufferSections. Intended // to be used with vertex shaders. SharedConstantBuffer* GetSharedVSBuffer() { @@ -489,17 +490,17 @@ private: UniquePtr<BufferCache> mConstantBufferCache; nsCString mFailureId; nsCString mFailureMessage; bool mInitialized; typedef EnumeratedArray<YUVColorSpace, YUVColorSpace::UNKNOWN, RefPtr<MLGBuffer>> ColorSpaceArray; ColorSpaceArray mColorSpaceBuffers; - typedef EnumeratedArray<gfx::ColorDepth, gfx::ColorDepth::MAX, RefPtr<MLGBuffer>> ColorDepthArray; + typedef EnumeratedArray<gfx::ColorDepth, gfx::ColorDepth::UNKNOWN, RefPtr<MLGBuffer>> ColorDepthArray; ColorDepthArray mColorDepthBuffers; protected: bool mIsValid; bool mCanUseClearView; bool mCanUseConstantBufferOffsetBinding; size_t mMaxConstantBufferBindSize;
--- a/gfx/layers/mlgpu/RenderPassMLGPU.cpp +++ b/gfx/layers/mlgpu/RenderPassMLGPU.cpp @@ -814,17 +814,17 @@ VideoRenderPass::SetupPipeline() MOZ_ASSERT(colorSpace != YUVColorSpace::UNKNOWN); RefPtr<MLGBuffer> ps1 = mDevice->GetBufferForColorSpace(colorSpace); if (!ps1) { return; } RefPtr<MLGBuffer> ps2 = - mDevice->GetBufferForBitDepthCoefficient(mHost->GetBitDepth()); + mDevice->GetBufferForColorDepthCoefficient(mHost->GetColorDepth()); if (!ps2) { return; } if (mGeometry == GeometryMode::UnitQuad) { mDevice->SetVertexShader(VertexShaderID::TexturedQuad); } else { mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
--- a/gfx/layers/mlgpu/ShaderDefinitionsMLGPU.h +++ b/gfx/layers/mlgpu/ShaderDefinitionsMLGPU.h @@ -87,17 +87,17 @@ struct MaskInformation uint32_t hasMask; uint32_t padding[2]; }; struct YCbCrShaderConstants { float yuvColorMatrix[3][4]; }; -struct YCbCrBitDepthConstants { +struct YCbCrColorDepthConstants { float coefficient; uint32_t padding[3]; }; struct BlendVertexShaderConstants { float backdropTransform[4][4]; };
--- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -983,17 +983,17 @@ CompositorOGL::GetShaderConfigFor(Effect case EffectTypes::SOLID_COLOR: config.SetRenderColor(true); break; case EffectTypes::YCBCR: { config.SetYCbCr(true); EffectYCbCr* effectYCbCr = static_cast<EffectYCbCr*>(aEffect); config.SetColorMultiplier( - RescalingFactorForAlphaBitDepth(effectYCbCr->mBitDepth)); + RescalingFactorForColorDepth(effectYCbCr->mColorDepth)); config.SetTextureTarget( effectYCbCr->mTexture->AsSourceOGL()->GetTextureTarget()); break; } case EffectTypes::NV12: config.SetNV12(true); config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB); break; @@ -1481,16 +1481,17 @@ CompositorOGL::DrawGeometry(const Geomet BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform); } if (mixBlendBackdrop) { BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2); } BindAndDrawGeometryWithTextureRect(program, aGeometry, texturedEffect->mTextureCoords, source); + source->AsSourceOGL()->MaybeFenceTexture(); } break; case EffectTypes::YCBCR: { EffectYCbCr* effectYCbCr = static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get()); TextureSource* sourceYCbCr = effectYCbCr->mTexture; const int Y = 0, Cb = 1, Cr = 2; TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL(); @@ -1521,16 +1522,19 @@ CompositorOGL::DrawGeometry(const Geomet if (mixBlendBackdrop) { BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4); } didSetBlendMode = SetBlendMode(gl(), blendMode); BindAndDrawGeometryWithTextureRect(program, aGeometry, effectYCbCr->mTextureCoords, sourceYCbCr->GetSubSource(Y)); + sourceY->MaybeFenceTexture(); + sourceCb->MaybeFenceTexture(); + sourceCr->MaybeFenceTexture(); } break; case EffectTypes::NV12: { EffectNV12* effectNV12 = static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get()); TextureSource* sourceNV12 = effectNV12->mTexture; const int Y = 0, CbCr = 1; TextureSourceOGL* sourceY = sourceNV12->GetSubSource(Y)->AsSourceOGL(); @@ -1558,16 +1562,18 @@ CompositorOGL::DrawGeometry(const Geomet if (mixBlendBackdrop) { BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3); } didSetBlendMode = SetBlendMode(gl(), blendMode); BindAndDrawGeometryWithTextureRect(program, aGeometry, effectNV12->mTextureCoords, sourceNV12->GetSubSource(Y)); + sourceY->MaybeFenceTexture(); + sourceCbCr->MaybeFenceTexture(); } break; case EffectTypes::RENDER_TARGET: { EffectRenderTarget* effectRenderTarget = static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get()); RefPtr<CompositingRenderTargetOGL> surface = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get()); @@ -1640,16 +1646,19 @@ CompositorOGL::DrawGeometry(const Geomet program->SetTexturePass2(true); BindAndDrawGeometryWithTextureRect(program, aGeometry, effectComponentAlpha->mTextureCoords, effectComponentAlpha->mOnBlack); mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA); + + sourceOnBlack->MaybeFenceTexture(); + sourceOnWhite->MaybeFenceTexture(); } break; default: MOZ_ASSERT(false, "Unhandled effect type"); break; } if (didSetBlendMode) { @@ -1932,33 +1941,33 @@ CompositorOGL::CreateDataTextureSourceAr uint8_t* buf = bufferTexture->GetBuffer(); const BufferDescriptor& buffDesc = bufferTexture->GetBufferDescriptor(); const YCbCrDescriptor& desc = buffDesc.get_YCbCrDescriptor(); RefPtr<gfx::DataSourceSurface> tempY = gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc), desc.yStride(), desc.ySize(), - SurfaceFormatForAlphaBitDepth(desc.bitDepth())); + SurfaceFormatForColorDepth(desc.colorDepth())); if (!tempY) { return nullptr; } RefPtr<gfx::DataSourceSurface> tempCb = gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc), desc.cbCrStride(), desc.cbCrSize(), - SurfaceFormatForAlphaBitDepth(desc.bitDepth())); + SurfaceFormatForColorDepth(desc.colorDepth())); if (!tempCb) { return nullptr; } RefPtr<gfx::DataSourceSurface> tempCr = gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc), desc.cbCrStride(), desc.cbCrSize(), - SurfaceFormatForAlphaBitDepth(desc.bitDepth())); + SurfaceFormatForColorDepth(desc.colorDepth())); if (!tempCr) { return nullptr; } RefPtr<DirectMapTextureSource> srcY = new DirectMapTextureSource(this, tempY); RefPtr<DirectMapTextureSource> srcU = new DirectMapTextureSource(this, tempCb); RefPtr<DirectMapTextureSource> srcV = new DirectMapTextureSource(this, tempCr);
--- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -314,53 +314,81 @@ GLTextureSource::IsValid() const DirectMapTextureSource::DirectMapTextureSource(TextureSourceProvider* aProvider, gfx::DataSourceSurface* aSurface) : GLTextureSource(aProvider, 0, LOCAL_GL_TEXTURE_RECTANGLE_ARB, aSurface->GetSize(), aSurface->GetFormat()) + , mSync(0) { MOZ_ASSERT(aSurface); UpdateInternal(aSurface, nullptr, nullptr, true); } +DirectMapTextureSource::~DirectMapTextureSource() +{ + if (!mSync || !gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) { + return; + } + + gl()->fDeleteSync(mSync); + mSync = 0; +} + bool DirectMapTextureSource::Update(gfx::DataSourceSurface* aSurface, nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset) { if (!aSurface) { return false; } return UpdateInternal(aSurface, aDestRegion, aSrcOffset, false); } +void +DirectMapTextureSource::MaybeFenceTexture() +{ + if (!gl() || + !gl()->MakeCurrent() || + gl()->IsDestroyed()) { + return; + } + + if (mSync) { + gl()->fDeleteSync(mSync); + } + mSync = gl()->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + + bool DirectMapTextureSource::Sync(bool aBlocking) { - if (!gl() || !gl()->MakeCurrent()) { + if (!gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) { // We use this function to decide whether we can unlock the texture // and clean it up. If we return false here and for whatever reason // the context is absent or invalid, the compositor will keep a // reference to this texture forever. return true; } - if (!gl()->IsDestroyed()) { - if (aBlocking) { - gl()->fFinishObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle); - } else { - return gl()->fTestObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle); - } + if (!mSync) { + return false; } - return true; + + GLenum waitResult = gl()->fClientWaitSync(mSync, + LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT, + aBlocking ? LOCAL_GL_TIMEOUT_IGNORED : 0); + return waitResult == LOCAL_GL_ALREADY_SIGNALED || + waitResult == LOCAL_GL_CONDITION_SATISFIED; } bool DirectMapTextureSource::UpdateInternal(gfx::DataSourceSurface* aSurface, nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset, bool aInit) { @@ -404,16 +432,21 @@ DirectMapTextureSource::UpdateInternal(g mTextureHandle, aSurface->GetSize(), nullptr, aInit, srcPoint, LOCAL_GL_TEXTURE0, LOCAL_GL_TEXTURE_RECTANGLE_ARB); + if (mSync) { + gl()->fDeleteSync(mSync); + mSync = 0; + } + gl()->fPixelStorei(LOCAL_GL_UNPACK_CLIENT_STORAGE_APPLE, LOCAL_GL_FALSE); return true; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // SurfaceTextureHost
--- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -87,16 +87,24 @@ public: , mHasCachedSamplingFilter(false) {} virtual bool IsValid() const = 0; virtual void BindTexture(GLenum aTextureUnit, gfx::SamplingFilter aSamplingFilter) = 0; + // To be overridden in textures that need this. This method will be called + // when the compositor has used the texture to draw. This allows us to set + // a fence with glFenceSync which we can wait on later to ensure the GPU + // is done with the draw calls using that texture. We would like to be able + // to simply use glFinishObjectAPPLE, but this returns earlier than + // expected with nvidia drivers. + virtual void MaybeFenceTexture() {} + virtual gfx::IntSize GetSize() const = 0; virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_2D; } virtual gfx::SurfaceFormat GetFormat() const = 0; virtual GLenum GetWrapMode() const = 0; @@ -293,33 +301,38 @@ protected: // should be alive until the ~ClientStorageTextureSource(). And if we try to // update the surface we mapped before, we need to call Sync() to make sure // the surface is not used by compositor. class DirectMapTextureSource : public GLTextureSource { public: DirectMapTextureSource(TextureSourceProvider* aProvider, gfx::DataSourceSurface* aSurface); + ~DirectMapTextureSource(); virtual bool Update(gfx::DataSourceSurface* aSurface, nsIntRegion* aDestRegion = nullptr, gfx::IntPoint* aSrcOffset = nullptr) override; virtual bool IsDirectMap() override { return true; } // If aBlocking is false, check if this texture is no longer being used // by the GPU - if aBlocking is true, this will block until the GPU is // done with it. virtual bool Sync(bool aBlocking) override; + virtual void MaybeFenceTexture() override; + private: bool UpdateInternal(gfx::DataSourceSurface* aSurface, nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset, bool aInit); + + GLsync mSync; }; class GLTextureHost : public TextureHost { public: GLTextureHost(TextureFlags aFlags, GLuint aTextureHandle, GLenum aTarget,
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp +++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp @@ -376,17 +376,17 @@ AsyncImagePipelineManager::ApplyAsyncIma aPipeline->mIsChanged = false; wr::LayoutSize contentSize { aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height() }; wr::DisplayListBuilder builder(aPipelineId, contentSize); float opacity = 1.0f; Maybe<wr::WrClipId> referenceFrameId = builder.PushStackingContext( - wr::ToLayoutRect(aPipeline->mScBounds), + wr::ToRoundedLayoutRect(aPipeline->mScBounds), nullptr, nullptr, &opacity, aPipeline->mScTransform.IsIdentity() ? nullptr : &aPipeline->mScTransform, wr::TransformStyle::Flat, nullptr, aPipeline->mMixBlendMode, nsTArray<wr::WrFilterOp>(), @@ -399,25 +399,25 @@ AsyncImagePipelineManager::ApplyAsyncIma if (aPipeline->mScaleToSize.isSome()) { rect = LayoutDeviceRect(0, 0, aPipeline->mScaleToSize.value().width, aPipeline->mScaleToSize.value().height); } if (aPipeline->mUseExternalImage) { MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost()); Range<wr::ImageKey> range_keys(&keys[0], keys.Length()); aPipeline->mCurrentTexture->PushDisplayItems(builder, - wr::ToLayoutRect(rect), - wr::ToLayoutRect(rect), + wr::ToRoundedLayoutRect(rect), + wr::ToRoundedLayoutRect(rect), aPipeline->mFilter, range_keys); HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture->AsWebRenderTextureHost()); } else { MOZ_ASSERT(keys.Length() == 1); - builder.PushImage(wr::ToLayoutRect(rect), - wr::ToLayoutRect(rect), + builder.PushImage(wr::ToRoundedLayoutRect(rect), + wr::ToRoundedLayoutRect(rect), true, aPipeline->mFilter, keys[0]); } } builder.PopStackingContext(referenceFrameId.isSome());
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp +++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp @@ -669,35 +669,35 @@ struct DIGroup if (empty) { ClearImageKey(aWrManager, true); return; } PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder); // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);? - bool isOpaque = false; + wr::OpacityType opacity = wr::OpacityType::HasAlphaChannel; TakeExternalSurfaces(recorder, mExternalSurfaces, aWrManager, aResources); bool hasItems = recorder->Finish(); GP("%d Finish\n", hasItems); Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength); if (!mKey) { if (!hasItems) // we don't want to send a new image that doesn't have any items in it return; wr::ImageKey key = aWrManager->WrBridge()->GetNextImageKey(); GP("No previous key making new one %d\n", key.mHandle); - wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), isOpaque); + wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity); MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t)); if (!aResources.AddBlobImage(key, descriptor, bytes)) { return; } mKey = Some(key); } else { - wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), isOpaque); + wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity); auto bottomRight = mInvalidRect.BottomRight(); GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, dtSize.width, dtSize.height); MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width && bottomRight.y <= dtSize.height); GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height); if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) { return; } } @@ -1886,17 +1886,21 @@ WebRenderCommandBuilder::GenerateFallbac nsAutoPtr<nsDisplayItemGeometry> newGeometry; newGeometry = aItem->AllocateGeometry(aDisplayListBuilder); fallbackData->SetGeometry(std::move(newGeometry)); gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ? gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8; if (useBlobImage) { bool snapped; - bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds); + wr::OpacityType opacity = + aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped) + .Contains(paintBounds) + ? wr::OpacityType::Opaque + : wr::OpacityType::HasAlphaChannel; std::vector<RefPtr<ScaledFont>> fonts; RefPtr<WebRenderDrawEventRecorder> recorder = MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts) { size_t count = aScaledFonts.size(); aStream.write((const char*)&count, sizeof(count)); for (auto& scaled : aScaledFonts) { BlobFont font = { @@ -1917,17 +1921,17 @@ WebRenderCommandBuilder::GenerateFallbac fallbackData->mBasicLayerManager, scale, highlight); recorder->FlushItem(IntRect(0, 0, paintSize.width, paintSize.height)); TakeExternalSurfaces(recorder, fallbackData->mExternalSurfaces, mManager, aResources); recorder->Finish(); if (isInvalidated) { Range<uint8_t> bytes((uint8_t *)recorder->mOutputStream.mData, recorder->mOutputStream.mLength); wr::ImageKey key = mManager->WrBridge()->GetNextImageKey(); - wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque); + wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), opacity); if (!aResources.AddBlobImage(key, descriptor, bytes)) { return nullptr; } fallbackData->SetKey(key); fallbackData->SetFonts(fonts); } else { // If there is no invalidation region and we don't have a image key, // it means we don't need to push image for the item.
--- a/gfx/layers/wr/WebRenderMessageUtils.h +++ b/gfx/layers/wr/WebRenderMessageUtils.h @@ -44,27 +44,27 @@ struct ParamTraits<mozilla::wr::ImageDes static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.format); WriteParam(aMsg, aParam.width); WriteParam(aMsg, aParam.height); WriteParam(aMsg, aParam.stride); - WriteParam(aMsg, aParam.is_opaque); + WriteParam(aMsg, aParam.opacity); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { return ReadParam(aMsg, aIter, &aResult->format) && ReadParam(aMsg, aIter, &aResult->width) && ReadParam(aMsg, aIter, &aResult->height) && ReadParam(aMsg, aIter, &aResult->stride) - && ReadParam(aMsg, aIter, &aResult->is_opaque); + && ReadParam(aMsg, aIter, &aResult->opacity); } }; template<> struct ParamTraits<mozilla::wr::IdNamespace> : public PlainOldDataSerializer<mozilla::wr::IdNamespace> { }; @@ -178,11 +178,17 @@ struct ParamTraits<mozilla::wr::WebRende }; template<> struct ParamTraits<mozilla::wr::MemoryReport> : public PlainOldDataSerializer<mozilla::wr::MemoryReport> { }; +template<> +struct ParamTraits<mozilla::wr::OpacityType> + : public PlainOldDataSerializer<mozilla::wr::OpacityType> +{ +}; + } // namespace IPC #endif // GFX_WEBRENDERMESSAGEUTILS_H
--- a/gfx/tests/gtest/TestTextures.cpp +++ b/gfx/tests/gtest/TestTextures.cpp @@ -261,17 +261,17 @@ TEST(Layers, TextureYCbCrSerialization) clientData.mCrChannel = crSurface->Data(); clientData.mYSize = ySurface->GetSize(); clientData.mPicSize = ySurface->GetSize(); clientData.mCbCrSize = cbSurface->GetSize(); clientData.mYStride = ySurface->Stride(); clientData.mCbCrStride = cbSurface->Stride(); clientData.mStereoMode = StereoMode::MONO; clientData.mYUVColorSpace = YUVColorSpace::BT601; - clientData.mBitDepth = 8; + clientData.mColorDepth = ColorDepth::COLOR_8; clientData.mYSkip = 0; clientData.mCbSkip = 0; clientData.mCrSkip = 0; clientData.mCrSkip = 0; clientData.mPicX = 0; clientData.mPicX = 0; uint32_t namespaceId = 1; @@ -290,16 +290,23 @@ TEST(Layers, TextureYCbCrSerialization) retry--; } // Skip this testing if IPDL connection is not ready if (!retry && !imageBridge->IPCOpen()) { return; } - RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(imageBridge, clientData.mYSize, clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride, - StereoMode::MONO, YUVColorSpace::BT601, - 8, TextureFlags::DEALLOCATE_CLIENT); + RefPtr<TextureClient> client = + TextureClient::CreateForYCbCr(imageBridge, + clientData.mYSize, + clientData.mYStride, + clientData.mCbCrSize, + clientData.mCbCrStride, + StereoMode::MONO, + gfx::ColorDepth::COLOR_8, + YUVColorSpace::BT601, + TextureFlags::DEALLOCATE_CLIENT); TestTextureClientYCbCr(client, clientData); // XXX - Test more texture client types. }
--- a/gfx/tests/gtest/TextureHelper.h +++ b/gfx/tests/gtest/TextureHelper.h @@ -55,26 +55,26 @@ CreateYCbCrTextureClientWithBackend(Laye clientData.mStereoMode = StereoMode::MONO; clientData.mYSkip = 0; clientData.mCbSkip = 0; clientData.mCrSkip = 0; clientData.mCrSkip = 0; clientData.mPicX = 0; clientData.mPicX = 0; - // Create YCbCrTexture for basice backend. + // Create YCbCrTexture for basic backend. if (aLayersBackend == LayersBackend::LAYERS_BASIC) { return TextureClient::CreateForYCbCr(nullptr, clientData.mYSize, clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride, StereoMode::MONO, + gfx::ColorDepth::COLOR_8, YUVColorSpace::BT601, - 8, TextureFlags::DEALLOCATE_CLIENT); } #ifdef XP_WIN RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetImageDevice(); if (device && aLayersBackend == LayersBackend::LAYERS_D3D11) { DXGIYCbCrTextureAllocationHelper helper(clientData, TextureFlags::DEFAULT, device);
--- a/gfx/vr/VRDisplayHost.cpp +++ b/gfx/vr/VRDisplayHost.cpp @@ -203,40 +203,57 @@ VRDisplayHost::RemoveLayer(VRLayerParent vm->RefreshVRDisplays(); } void VRDisplayHost::StartFrame() { AUTO_PROFILER_TRACING("VR", "GetSensorState"); + TimeStamp now = TimeStamp::Now(); #if defined(MOZ_WIDGET_ANDROID) + const TimeStamp lastFrameStart = mDisplayInfo.mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames]; const bool isPresenting = mLastUpdateDisplayInfo.GetPresentingGroups() != 0; - double duration = mLastFrameStart.IsNull() ? 0.0 : (TimeStamp::Now() - mLastFrameStart).ToMilliseconds(); + double duration = lastFrameStart.IsNull() ? 0.0 : (now - lastFrameStart).ToMilliseconds(); /** * Do not start more VR frames until the last submitted frame is already processed. */ if (isPresenting && mLastStartedFrame > 0 && mDisplayInfo.mDisplayState.mLastSubmittedFrameId < mLastStartedFrame && duration < (double)ANDROID_MAX_FRAME_DURATION) { return; } #endif // !defined(MOZ_WIDGET_ANDROID) - mLastFrameStart = TimeStamp::Now(); ++mDisplayInfo.mFrameId; - mDisplayInfo.mLastSensorState[mDisplayInfo.mFrameId % kVRMaxLatencyFrames] = GetSensorState(); + size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames; + mDisplayInfo.mLastSensorState[bufferIndex] = GetSensorState(); + mDisplayInfo.mLastFrameStart[bufferIndex] = now; mFrameStarted = true; #if defined(MOZ_WIDGET_ANDROID) mLastStartedFrame = mDisplayInfo.mFrameId; #endif // !defined(MOZ_WIDGET_ANDROID) } void VRDisplayHost::NotifyVSync() { /** + * If this display isn't presenting, refresh the sensors and trigger + * VRDisplay.requestAnimationFrame at the normal 2d display refresh rate. + */ + if (mDisplayInfo.mPresentingGroups == 0) { + VRManager *vm = VRManager::Get(); + MOZ_ASSERT(vm); + vm->NotifyVRVsync(mDisplayInfo.mDisplayID); + } +} + +void +VRDisplayHost::CheckWatchDog() +{ + /** * We will trigger a new frame immediately after a successful frame texture * submission. If content fails to call VRDisplay.submitFrame after * dom.vr.display.rafMaxDuration milliseconds has elapsed since the last * VRDisplay.requestAnimationFrame, we act as a "watchdog" and kick-off * a new VRDisplay.requestAnimationFrame to avoid a render loop stall and * to give content a chance to recover. * * If the lower level VR platform API's are rejecting submitted frames, @@ -255,41 +272,54 @@ VRDisplayHost::NotifyVSync() * The slowest expected refresh rate for a VR display currently is an * Oculus CV1 when ASW (Asynchronous Space Warp) is enabled, at 45hz. * A dom.vr.display.rafMaxDuration value of 50 milliseconds results in a 20hz * rate, which avoids inadvertent triggering of the watchdog during * Oculus ASW even if every second frame is dropped. */ bool bShouldStartFrame = false; - if (mDisplayInfo.mPresentingGroups == 0) { - // If this display isn't presenting, refresh the sensors and trigger - // VRDisplay.requestAnimationFrame at the normal 2d display refresh rate. + // If content fails to call VRDisplay.submitFrame, we must eventually + // time-out and trigger a new frame. + TimeStamp lastFrameStart = mDisplayInfo.mLastFrameStart[mDisplayInfo.mFrameId % kVRMaxLatencyFrames]; + if (lastFrameStart.IsNull()) { bShouldStartFrame = true; } else { - // If content fails to call VRDisplay.submitFrame, we must eventually - // time-out and trigger a new frame. - if (mLastFrameStart.IsNull()) { + TimeDuration duration = TimeStamp::Now() - lastFrameStart; + if (duration.ToMilliseconds() > gfxPrefs::VRDisplayRafMaxDuration()) { bShouldStartFrame = true; - } else { - TimeDuration duration = TimeStamp::Now() - mLastFrameStart; - if (duration.ToMilliseconds() > gfxPrefs::VRDisplayRafMaxDuration()) { - bShouldStartFrame = true; - } } } if (bShouldStartFrame) { VRManager *vm = VRManager::Get(); MOZ_ASSERT(vm); vm->NotifyVRVsync(mDisplayInfo.mDisplayID); } } void +VRDisplayHost::Run1msTasks(double aDeltaTime) +{ + // To override in children +} + +void +VRDisplayHost::Run10msTasks() +{ + CheckWatchDog(); +} + +void +VRDisplayHost::Run100msTasks() +{ + // to override in children +} + +void VRDisplayHost::SubmitFrameInternal(const layers::SurfaceDescriptor &aTexture, uint64_t aFrameId, const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect) { #if !defined(MOZ_WIDGET_ANDROID) MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread()); #endif // !defined(MOZ_WIDGET_ANDROID)
--- a/gfx/vr/VRDisplayHost.h +++ b/gfx/vr/VRDisplayHost.h @@ -40,16 +40,19 @@ public: void RemoveLayer(VRLayerParent* aLayer); virtual void ZeroSensor() = 0; virtual void StartPresentation() = 0; virtual void StopPresentation() = 0; virtual void StartVRNavigation(); virtual void StopVRNavigation(const TimeDuration& aTimeout); void NotifyVSync(); + virtual void Run1msTasks(double aDeltaTime); + virtual void Run10msTasks(); + virtual void Run100msTasks(); void StartFrame(); void SubmitFrame(VRLayerParent* aLayer, const layers::SurfaceDescriptor& aTexture, uint64_t aFrameId, const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect); @@ -92,19 +95,19 @@ protected: virtual VRHMDSensorState GetSensorState() = 0; RefPtr<VRThread> mSubmitThread; private: void SubmitFrameInternal(const layers::SurfaceDescriptor& aTexture, uint64_t aFrameId, const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect); + void CheckWatchDog(); VRDisplayInfo mLastUpdateDisplayInfo; - TimeStamp mLastFrameStart; bool mFrameStarted; #if defined(MOZ_WIDGET_ANDROID) protected: uint64_t mLastSubmittedFrameId; uint64_t mLastStartedFrame; #endif // defined(MOZ_WIDGET_ANDROID) #if defined(XP_WIN)
--- a/gfx/vr/VRManager.cpp +++ b/gfx/vr/VRManager.cpp @@ -40,34 +40,51 @@ using namespace mozilla::gfx; using namespace mozilla::layers; using namespace mozilla::gl; namespace mozilla { namespace gfx { static StaticRefPtr<VRManager> sVRManagerSingleton; + /** + * When VR content is active, we run the tasks at 1ms + * intervals, enabling multiple events to be processed + * per frame, such as haptic feedback pulses. + */ +const uint32_t kVRActiveTaskInterval = 1; // milliseconds + + /** + * When VR content is inactive, we run the tasks at 100ms + * intervals, enabling VR display enumeration and + * presentation startup to be relatively responsive + * while not consuming unnecessary resources. + */ +const uint32_t kVRIdleTaskInterval = 100; // milliseconds + /*static*/ void VRManager::ManagerInit() { MOZ_ASSERT(NS_IsMainThread()); // TODO: We should make VRManager::ManagerInit // be called when entering VR content pages. if (sVRManagerSingleton == nullptr) { sVRManagerSingleton = new VRManager(); ClearOnShutdown(&sVRManagerSingleton); } } VRManager::VRManager() : mInitialized(false) + , mAccumulator100ms(0.0f) , mVRDisplaysRequested(false) , mVRControllersRequested(false) , mVRServiceStarted(false) + , mTaskInterval(0) { MOZ_COUNT_CTOR(VRManager); MOZ_ASSERT(sVRManagerSingleton == nullptr); RefPtr<VRSystemManager> mgr; /** * We must add the VRDisplayManager's to mManagers in a careful order to @@ -146,16 +163,17 @@ VRManager::~VRManager() MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mInitialized); MOZ_COUNT_DTOR(VRManager); } void VRManager::Destroy() { + StopTasks(); mVRDisplays.Clear(); mVRControllers.Clear(); for (uint32_t i = 0; i < mManagers.Length(); ++i) { mManagers[i]->Destroy(); } #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) if (mVRService) { mVRService->Stop(); @@ -190,16 +208,17 @@ VRManager::Shutdown() } #endif mVRServiceStarted = false; } void VRManager::Init() { + StartTasks(); mInitialized = true; } /* static */VRManager* VRManager::Get() { MOZ_ASSERT(sVRManagerSingleton != nullptr); @@ -247,33 +266,212 @@ VRManager::UpdateRequestedDevices() * This must be called even when no WebVR site is active. * If we don't have a 2d display attached to the system, we can call this * at the VR display's native refresh rate. **/ void VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp) { MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread()); - UpdateRequestedDevices(); for (const auto& manager : mManagers) { manager->NotifyVSync(); } +} + +void +VRManager::StartTasks() +{ + MOZ_ASSERT(VRListenerThread()); + if (!mTaskTimer) { + mTaskInterval = GetOptimalTaskInterval(); + mTaskTimer = NS_NewTimer(); + mTaskTimer->SetTarget(VRListenerThreadHolder::Loop()->SerialEventTarget()); + mTaskTimer->InitWithNamedFuncCallback( + TaskTimerCallback, + this, + mTaskInterval, + nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP, + "VRManager::TaskTimerCallback"); + } +} + +void +VRManager::StopTasks() +{ + if (mTaskTimer) { + MOZ_ASSERT(VRListenerThread()); + mTaskTimer->Cancel(); + mTaskTimer = nullptr; + } +} + +/*static*/ void +VRManager::StopVRListenerThreadTasks() +{ + if (sVRManagerSingleton) { + sVRManagerSingleton->StopTasks(); + } +} + +/*static*/ void +VRManager::TaskTimerCallback(nsITimer* aTimer, void* aClosure) +{ + /** + * It is safe to use the pointer passed in aClosure to reference the + * VRManager object as the timer is canceled in VRManager::Destroy. + * VRManager::Destroy set mInitialized to false, which is asserted + * in the VRManager destructor, guaranteeing that this functions + * runs if and only if the VRManager object is valid. + */ + VRManager* self = static_cast<VRManager*>(aClosure); + self->RunTasks(); +} + +void +VRManager::RunTasks() +{ + MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread()); + + // Will be called once every 1ms when a VR presentation + // is active or once per vsync when a VR presentation is + // not active. + + TimeStamp now = TimeStamp::Now(); + double lastTickMs = mAccumulator100ms; + double deltaTime = 0.0f; + if (!mLastTickTime.IsNull()) { + deltaTime = (now - mLastTickTime).ToMilliseconds(); + } + mAccumulator100ms += deltaTime; + mLastTickTime = now; + + if (deltaTime > 0.0f && floor(mAccumulator100ms) != floor(lastTickMs)) { + // Even if more than 1 ms has passed, we will only + // execute Run1msTasks() once. + Run1msTasks(deltaTime); + } + + if (floor(mAccumulator100ms * 0.1f) != floor(lastTickMs * 0.1f)) { + // Even if more than 10 ms has passed, we will only + // execute Run10msTasks() once. + Run10msTasks(); + } + + if (mAccumulator100ms >= 100.0f) { + // Even if more than 100 ms has passed, we will only + // execute Run100msTasks() once. + Run100msTasks(); + mAccumulator100ms = fmod(mAccumulator100ms, 100.0f); + } + + uint32_t optimalTaskInterval = GetOptimalTaskInterval(); + if (mTaskTimer && optimalTaskInterval != mTaskInterval) { + mTaskTimer->SetDelay(optimalTaskInterval); + mTaskInterval = optimalTaskInterval; + } +} + +uint32_t +VRManager::GetOptimalTaskInterval() +{ + /** + * When either VR content is detected or VR hardware + * has already been activated, we schedule tasks more + * frequently. + */ + bool wantGranularTasks = mVRDisplaysRequested || + mVRControllersRequested || + mVRDisplays.Count() || + mVRControllers.Count(); + if (wantGranularTasks) { + return kVRActiveTaskInterval; + } + + return kVRIdleTaskInterval; +} + +/** + * Run1msTasks() is guaranteed not to be + * called more than once within 1ms. + * When VR is not active, this will be + * called once per VSync if it wasn't + * called within the last 1ms. + */ +void +VRManager::Run1msTasks(double aDeltaTime) +{ + MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread()); + + for (const auto& manager : mManagers) { + manager->Run1msTasks(aDeltaTime); + } + + for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) { + gfx::VRDisplayHost* display = iter.UserData(); + display->Run1msTasks(aDeltaTime); + } +} + +/** + * Run10msTasks() is guaranteed not to be + * called more than once within 10ms. + * When VR is not active, this will be + * called once per VSync if it wasn't + * called within the last 10ms. + */ +void +VRManager::Run10msTasks() +{ + MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread()); + + UpdateRequestedDevices(); + + for (const auto& manager : mManagers) { + manager->Run10msTasks(); + } + + for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) { + gfx::VRDisplayHost* display = iter.UserData(); + display->Run10msTasks(); + } +} + +/** + * Run100msTasks() is guaranteed not to be + * called more than once within 100ms. + * When VR is not active, this will be + * called once per VSync if it wasn't + * called within the last 100ms. + */ +void +VRManager::Run100msTasks() +{ + MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread()); // We must continually refresh the VR display enumeration to check // for events that we must fire such as Window.onvrdisplayconnect // Note that enumeration itself may activate display hardware, such // as Oculus, so we only do this when we know we are displaying content // that is looking for VR displays. RefreshVRDisplays(); // Update state and enumeration of VR controllers RefreshVRControllers(); CheckForInactiveTimeout(); + + for (const auto& manager : mManagers) { + manager->Run100msTasks(); + } + + for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) { + gfx::VRDisplayHost* display = iter.UserData(); + display->Run100msTasks(); + } } void VRManager::CheckForInactiveTimeout() { // Shut down the VR devices when not in use if (mVRDisplaysRequested || mVRControllersRequested) { // We are using a VR device, keep it alive @@ -306,17 +504,17 @@ VRManager::NotifyVRVsync(const uint32_t& } } RefPtr<VRDisplayHost> display = GetDisplay(aDisplayID); if (display) { display->StartFrame(); } - RefreshVRDisplays(); + DispatchVRDisplayInfoUpdate(); } void VRManager::EnumerateVRDisplays() { /** * Throttle the rate of enumeration to the interval set in * VRDisplayEnumerateInterval
--- a/gfx/vr/VRManager.h +++ b/gfx/vr/VRManager.h @@ -9,16 +9,17 @@ #include "nsRefPtrHashtable.h" #include "nsTArray.h" #include "nsTHashtable.h" #include "nsDataHashtable.h" #include "mozilla/TimeStamp.h" #include "gfxVR.h" +class nsITimer; namespace mozilla { namespace layers { class TextureHost; } namespace gfx { class VRLayerParent; class VRManagerParent; @@ -57,26 +58,35 @@ public: void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex, double aIntensity, double aDuration, const VRManagerPromise& aPromise); void StopVibrateHaptic(uint32_t aControllerIdx); void NotifyVibrateHapticCompleted(const VRManagerPromise& aPromise); void DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult); void StartVRNavigation(const uint32_t& aDisplayID); void StopVRNavigation(const uint32_t& aDisplayID, const TimeDuration& aTimeout); + static void StopVRListenerThreadTasks(); protected: VRManager(); ~VRManager(); private: void Init(); void Destroy(); void Shutdown(); + void StartTasks(); + void StopTasks(); + static void TaskTimerCallback(nsITimer* aTimer, void* aClosure); + void RunTasks(); + void Run1msTasks(double aDeltaTime); + void Run10msTasks(); + void Run100msTasks(); + uint32_t GetOptimalTaskInterval(); void DispatchVRDisplayInfoUpdate(); void UpdateRequestedDevices(); void EnumerateVRDisplays(); void CheckForInactiveTimeout(); typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet; VRManagerParentSet mVRManagerParents; @@ -90,22 +100,26 @@ private: typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRControllerHost> VRControllerHostHashMap; VRControllerHostHashMap mVRControllers; Atomic<bool> mInitialized; TimeStamp mLastControllerEnumerationTime; TimeStamp mLastDisplayEnumerationTime; TimeStamp mLastActiveTime; + TimeStamp mLastTickTime; + double mAccumulator100ms; RefPtr<VRSystemManagerPuppet> mPuppetManager; RefPtr<VRSystemManagerExternal> mExternalManager; #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) RefPtr<VRService> mVRService; #endif bool mVRDisplaysRequested; bool mVRControllersRequested; bool mVRServiceStarted; + uint32_t mTaskInterval; + RefPtr<nsITimer> mTaskTimer; }; } // namespace gfx } // namespace mozilla #endif // GFX_VR_MANAGER_H
--- a/gfx/vr/VRThread.cpp +++ b/gfx/vr/VRThread.cpp @@ -1,23 +1,24 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "VRThread.h" #include "nsThreadUtils.h" +#include "VRManager.h" namespace mozilla { namespace gfx { static StaticRefPtr<VRListenerThreadHolder> sVRListenerThreadHolder; -static bool sFinishedVRListenerShutDown = false; +static bool sFinishedVRListenerShutDown = true; static const uint32_t kDefaultThreadLifeTime = 60; // in 60 seconds. static const uint32_t kDelayPostTaskTime = 20000; // in 20000 ms. VRListenerThreadHolder* GetVRListenerThreadHolder() { return sVRListenerThreadHolder; } @@ -88,26 +89,26 @@ VRListenerThreadHolder::CreateThread() return vrThread; } void VRListenerThreadHolder::Start() { MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!"); MOZ_ASSERT(!sVRListenerThreadHolder, "The VR listener thread has already been started!"); - + sFinishedVRListenerShutDown = false; sVRListenerThreadHolder = new VRListenerThreadHolder(); } void VRListenerThreadHolder::Shutdown() { MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!"); MOZ_ASSERT(sVRListenerThreadHolder, "The VR listener thread has already been shut down!"); - + VRManager::StopVRListenerThreadTasks(); sVRListenerThreadHolder = nullptr; SpinEventLoopUntil([&]() { return sFinishedVRListenerShutDown; }); } /* static */ bool VRListenerThreadHolder::IsInVRListenerThread() {
--- a/gfx/vr/external_api/moz_external_vr.h +++ b/gfx/vr/external_api/moz_external_vr.h @@ -24,17 +24,17 @@ namespace mozilla { #ifdef MOZILLA_INTERNAL_API namespace dom { enum class GamepadHand : uint8_t; enum class GamepadCapabilityFlags : uint16_t; } #endif // MOZILLA_INTERNAL_API namespace gfx { -static const int32_t kVRExternalVersion = 2; +static const int32_t kVRExternalVersion = 3; // We assign VR presentations to groups with a bitmask. // Currently, we will only display either content or chrome. // Later, we will have more groups to support VR home spaces and // multitasking environments. // These values are not exposed to regular content and only affect // chrome-only API's. They may be changed at any time. static const uint32_t kVRGroupNone = 0; @@ -43,16 +43,17 @@ static const uint32_t kVRGroupChrome = 1 static const uint32_t kVRGroupAll = 0xffffffff; static const int kVRDisplayNameMaxLen = 256; static const int kVRControllerNameMaxLen = 256; static const int kVRControllerMaxCount = 16; static const int kVRControllerMaxButtons = 64; static const int kVRControllerMaxAxis = 16; static const int kVRLayerMaxCount = 8; +static const int kVRHapticsMaxCount = 32; #if defined(__ANDROID__) typedef uint64_t VRLayerTextureHandle; #else typedef void* VRLayerTextureHandle; #endif struct Point3D_POD @@ -349,24 +350,44 @@ struct VRLayerState { VRLayerType type; union { VRLayer_2D_Content layer_2d_content; VRLayer_Stereo_Immersive layer_stereo_immersive; }; }; +struct VRHapticState +{ + // Reference frame for timing. + // When 0, this does not represent an active haptic pulse. + uint64_t inputFrameID; + // Index within VRSystemState.controllerState identifying the controller + // to emit the haptic pulse + uint32_t controllerIndex; + // 0-based index indicating which haptic actuator within the controller + uint32_t hapticIndex; + // Start time of the haptic feedback pulse, relative to the start of + // inputFrameID, in seconds + float pulseStart; + // Duration of the haptic feedback pulse, in seconds + float pulseDuration; + // Intensity of the haptic feedback pulse, from 0.0f to 1.0f + float pulseIntensity; +}; + struct VRBrowserState { #if defined(__ANDROID__) bool shutdown; #endif // defined(__ANDROID__) bool presentationActive; bool navigationTransitionActive; VRLayerState layerState[kVRLayerMaxCount]; + VRHapticState hapticState[kVRHapticsMaxCount]; }; struct VRSystemState { bool enumerationCompleted; VRDisplayState displayState; VRHMDSensorState sensorState; VRControllerState controllerState[kVRControllerMaxCount];
--- a/gfx/vr/gfxVR.cpp +++ b/gfx/vr/gfxVR.cpp @@ -53,16 +53,34 @@ VRSystemManager::NotifyVSync() // Ensure that the controller state is updated at least // on every 2d display VSync when not in a VR presentation. if (!GetIsPresenting()) { HandleInput(); } } +void +VRSystemManager::Run1msTasks(double aDeltaTime) +{ + // To be overridden by children +} + +void +VRSystemManager::Run10msTasks() +{ + // To be overridden by children +} + +void +VRSystemManager::Run100msTasks() +{ + // To be overridden by children +} + /** * VRSystemManager::GetHMDs must not be called unless * VRSystemManager::ShouldInhibitEnumeration is called * on all VRSystemManager instances and they all return * false. * * This is used to ensure that VR devices that can be * enumerated by multiple API's are only enumerated by
--- a/gfx/vr/gfxVR.h +++ b/gfx/vr/gfxVR.h @@ -58,16 +58,17 @@ struct VRDisplayInfo VRDeviceType mType; uint32_t mPresentingGroups; uint32_t mGroupMask; uint64_t mFrameId; VRDisplayState mDisplayState; VRControllerState mControllerState[kVRControllerMaxCount]; VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames]; + TimeStamp mLastFrameStart[kVRMaxLatencyFrames]; const VRHMDSensorState& GetSensorState() const { return mLastSensorState[mFrameId % kVRMaxLatencyFrames]; } VRDeviceType GetType() const { return mType; } uint32_t GetDisplayID() const { return mDisplayID; } const char* GetDisplayName() const { return mDisplayState.mDisplayName; } @@ -182,16 +183,19 @@ protected: public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRSystemManager) virtual void Destroy() = 0; virtual void Shutdown() = 0; virtual void Enumerate() = 0; virtual void NotifyVSync(); + virtual void Run1msTasks(double aDeltaTime); + virtual void Run10msTasks(); + virtual void Run100msTasks(); virtual bool ShouldInhibitEnumeration(); virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) = 0; virtual bool GetIsPresenting() = 0; virtual void HandleInput() = 0; virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0; virtual void ScanForControllers() = 0; virtual void RemoveControllers() = 0; virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
--- a/gfx/vr/gfxVRExternal.cpp +++ b/gfx/vr/gfxVRExternal.cpp @@ -49,16 +49,17 @@ static const char* kShmemName = "/moz.ge using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::gfx::impl; using namespace mozilla::layers; using namespace mozilla::dom; VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState) : VRDisplayHost(VRDeviceType::External) + , mHapticPulseRemaining{} , mBrowserState{} , mLastSensorState{} { MOZ_COUNT_CTOR_INHERITED(VRDisplayExternal, VRDisplayHost); mDisplayInfo.mDisplayState = aDisplayState; // default to an identity quaternion mLastSensorState.pose.orientation[3] = 1.0f; @@ -68,35 +69,52 @@ VRDisplayExternal::~VRDisplayExternal() { Destroy(); MOZ_COUNT_DTOR_INHERITED(VRDisplayExternal, VRDisplayHost); } void VRDisplayExternal::Destroy() { + StopAllHaptics(); StopPresentation(); } void VRDisplayExternal::ZeroSensor() { } void -VRDisplayExternal::Refresh() +VRDisplayExternal::Run1msTasks(double aDeltaTime) +{ + VRDisplayHost::Run1msTasks(aDeltaTime); + UpdateHaptics(aDeltaTime); +} + +void +VRDisplayExternal::Run10msTasks() { + VRDisplayHost::Run10msTasks(); + ExpireNavigationTransition(); + PullState(); + PushState(); + // 1ms tasks will always be run before + // the 10ms tasks, so no need to include + // them here as well. +} + +void +VRDisplayExternal::ExpireNavigationTransition() +{ if (!mVRNavigationTransitionEnd.IsNull() && TimeStamp::Now() > mVRNavigationTransitionEnd) { mBrowserState.navigationTransitionActive = false; } - - PullState(); - PushState(); } VRHMDSensorState VRDisplayExternal::GetSensorState() { return mLastSensorState; } @@ -109,57 +127,20 @@ VRDisplayExternal::StartPresentation() mTelemetry.Clear(); mTelemetry.mPresentationStart = TimeStamp::Now(); // Indicate that we are ready to start immersive mode mBrowserState.presentationActive = true; mBrowserState.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive; PushState(); -#if defined(MOZ_WIDGET_ANDROID) - mLastSubmittedFrameId = 0; - mLastStartedFrame = 0; - /** - * Android compositor is paused when presentation starts. That causes VRManager::NotifyVsync() not to be called. - * We post a VRTask to call VRManager::NotifyVsync() while the compositor is paused on Android. - * VRManager::NotifyVsync() should be called constinuosly while the compositor is paused because Gecko WebVR Architecture - * relies on that to act as a "watchdog" in order to avoid render loop stalls and recover from SubmitFrame call timeouts. - */ - PostVRTask(); -#endif - // TODO - Implement telemetry: - + mDisplayInfo.mDisplayState.mLastSubmittedFrameId = 0; // mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames; } -#if defined(MOZ_WIDGET_ANDROID) -void -VRDisplayExternal::PostVRTask() { - MessageLoop * vrLoop = VRListenerThreadHolder::Loop(); - if (!vrLoop || !mBrowserState.presentationActive) { - return; - } - RefPtr<Runnable> task = NewRunnableMethod( - "VRDisplayExternal::RunVRTask", - this, - &VRDisplayExternal::RunVRTask); - VRListenerThreadHolder::Loop()->PostDelayedTask(task.forget(), 50); -} - -void -VRDisplayExternal::RunVRTask() { - if (mBrowserState.presentationActive) { - VRManager *vm = VRManager::Get(); - vm->NotifyVsync(TimeStamp::Now()); - PostVRTask(); - } -} - -#endif // defined(MOZ_WIDGET_ANDROID) - void VRDisplayExternal::StopPresentation() { if (!mBrowserState.presentationActive) { return; } // Indicate that we have stopped immersive mode @@ -301,16 +282,135 @@ VRDisplayExternal::SubmitFrame(const lay #endif } #endif // defined(MOZ_WIDGET_ANDROID) return mDisplayInfo.mDisplayState.mLastSubmittedFrameSuccessful; } void +VRDisplayExternal::VibrateHaptic(uint32_t aControllerIdx, + uint32_t aHapticIndex, + double aIntensity, + double aDuration, + const VRManagerPromise& aPromise) +{ + TimeStamp now = TimeStamp::Now(); + size_t bestSlotIndex = 0; + // Default to an empty slot, or the slot holding the oldest haptic pulse + for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) { + const VRHapticState& state = mBrowserState.hapticState[i]; + if (state.inputFrameID == 0) { + // Unused slot, use it + bestSlotIndex = i; + break; + } + if (mHapticPulseRemaining[i] < mHapticPulseRemaining[bestSlotIndex]) { + // If no empty slots are available, fall back to overriding + // the pulse which is ending soonest. + bestSlotIndex = i; + } + } + // Override the last pulse on the same actuator if present. + for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) { + const VRHapticState& state = mBrowserState.hapticState[i]; + if (state.inputFrameID == 0) { + // This is an empty slot -- no match + continue; + } + if (state.controllerIndex == aControllerIdx && + state.hapticIndex == aHapticIndex) { + // Found pulse on same actuator -- let's override it. + bestSlotIndex = i; + } + } + ClearHapticSlot(bestSlotIndex); + + // Populate the selected slot with new haptic state + size_t bufferIndex = mDisplayInfo.mFrameId % kVRMaxLatencyFrames; + VRHapticState& bestSlot = mBrowserState.hapticState[bestSlotIndex]; + bestSlot.inputFrameID = + mDisplayInfo.mLastSensorState[bufferIndex].inputFrameID; + bestSlot.controllerIndex = aControllerIdx; + bestSlot.hapticIndex = aHapticIndex; + bestSlot.pulseStart = + (now - mDisplayInfo.mLastFrameStart[bufferIndex]).ToSeconds(); + bestSlot.pulseDuration = aDuration; + bestSlot.pulseIntensity = aIntensity; + // Convert from seconds to ms + mHapticPulseRemaining[bestSlotIndex] = aDuration * 1000.0f; + MOZ_ASSERT(bestSlotIndex <= mHapticPromises.Length()); + if (bestSlotIndex == mHapticPromises.Length()) { + mHapticPromises.AppendElement( + UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise))); + } else { + mHapticPromises[bestSlotIndex] = + UniquePtr<VRManagerPromise>(new VRManagerPromise(aPromise)); + } + PushState(); +} + +void +VRDisplayExternal::ClearHapticSlot(size_t aSlot) +{ + MOZ_ASSERT(aSlot < mozilla::ArrayLength(mBrowserState.hapticState)); + memset(&mBrowserState.hapticState[aSlot], 0, sizeof(VRHapticState)); + mHapticPulseRemaining[aSlot] = 0.0f; + if (aSlot < mHapticPromises.Length() && mHapticPromises[aSlot]) { + VRManager* vm = VRManager::Get(); + vm->NotifyVibrateHapticCompleted(*mHapticPromises[aSlot]); + mHapticPromises[aSlot] = nullptr; + } +} + +void +VRDisplayExternal::UpdateHaptics(double aDeltaTime) +{ + bool bNeedPush = false; + // Check for any haptic pulses that have ended and clear them + for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) { + const VRHapticState& state = mBrowserState.hapticState[i]; + if (state.inputFrameID == 0) { + // Nothing in this slot + continue; + } + mHapticPulseRemaining[i] -= aDeltaTime; + if (mHapticPulseRemaining[i] <= 0.0f) { + // The pulse has finished + ClearHapticSlot(i); + bNeedPush = true; + } + } + if (bNeedPush) { + PushState(); + } +} + +void +VRDisplayExternal::StopVibrateHaptic(uint32_t aControllerIdx) +{ + for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) { + VRHapticState& state = mBrowserState.hapticState[i]; + if (state.controllerIndex == aControllerIdx) { + memset(&state, 0, sizeof(VRHapticState)); + } + } + PushState(); +} + +void +VRDisplayExternal::StopAllHaptics() +{ + for (size_t i = 0; i < mozilla::ArrayLength(mBrowserState.hapticState); i++) { + ClearHapticSlot(i); + } + PushState(); +} + +void VRDisplayExternal::PushState(bool aNotifyCond) { VRManager *vm = VRManager::Get(); VRSystemManagerExternal* manager = vm->GetExternalManager(); manager->PushState(&mBrowserState, aNotifyCond); } #if defined(MOZ_WIDGET_ANDROID) @@ -538,25 +638,24 @@ VRSystemManagerExternal::Shutdown() } CloseShmem(); #if defined(MOZ_WIDGET_ANDROID) mDoShutdown = false; #endif } void -VRSystemManagerExternal::NotifyVSync() +VRSystemManagerExternal::Run100msTasks() { - VRSystemManager::NotifyVSync(); + VRSystemManager::Run100msTasks(); + // 1ms and 10ms tasks will always be run before + // the 100ms tasks, so no need to run them + // redundantly here. CheckForShutdown(); - - if (mDisplay) { - mDisplay->Refresh(); - } } void VRSystemManagerExternal::Enumerate() { if (mDisplay == nullptr) { OpenShmem(); if (mExternalShmem) { @@ -618,64 +717,83 @@ VRSystemManagerExternal::GetIsPresenting return displayInfo.GetPresentingGroups() != 0; } return false; } void VRSystemManagerExternal::VibrateHaptic(uint32_t aControllerIdx, - uint32_t aHapticIndex, - double aIntensity, - double aDuration, - const VRManagerPromise& aPromise) + uint32_t aHapticIndex, + double aIntensity, + double aDuration, + const VRManagerPromise& aPromise) { - // TODO - Implement this + if (mDisplay) { + // VRDisplayClient::FireGamepadEvents() assigns a controller ID with ranges + // based on displayID. We must translate this to the indexes understood by + // VRDisplayExternal. + uint32_t controllerBaseIndex = + kVRControllerMaxCount * mDisplay->GetDisplayInfo().mDisplayID; + uint32_t controllerIndex = aControllerIdx - controllerBaseIndex; + double aDurationSeconds = aDuration * 0.001f; + mDisplay->VibrateHaptic( + controllerIndex, aHapticIndex, aIntensity, aDurationSeconds, aPromise); + } } void VRSystemManagerExternal::StopVibrateHaptic(uint32_t aControllerIdx) { - // TODO - Implement this + if (mDisplay) { + // VRDisplayClient::FireGamepadEvents() assigns a controller ID with ranges + // based on displayID. We must translate this to the indexes understood by + // VRDisplayExternal. + uint32_t controllerBaseIndex = + kVRControllerMaxCount * mDisplay->GetDisplayInfo().mDisplayID; + uint32_t controllerIndex = aControllerIdx - controllerBaseIndex; + mDisplay->StopVibrateHaptic(controllerIndex); + } } void -VRSystemManagerExternal::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) +VRSystemManagerExternal::GetControllers( + nsTArray<RefPtr<VRControllerHost>>& aControllerResult) { - // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal + // Controller updates are handled in VRDisplayClient for + // VRSystemManagerExternal aControllerResult.Clear(); } void VRSystemManagerExternal::ScanForControllers() { - // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal - if (mDisplay) { - mDisplay->Refresh(); - } + // Controller updates are handled in VRDisplayClient for + // VRSystemManagerExternal return; } void VRSystemManagerExternal::HandleInput() { - // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal - if (mDisplay) { - mDisplay->Refresh(); - } + // Controller updates are handled in VRDisplayClient for + // VRSystemManagerExternal return; } void VRSystemManagerExternal::RemoveControllers() { - // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal + if (mDisplay) { + mDisplay->StopAllHaptics(); + } + // Controller updates are handled in VRDisplayClient for + // VRSystemManagerExternal } - #if defined(MOZ_WIDGET_ANDROID) bool VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState /* = nullptr */, VRControllerState* aControllerState /* = nullptr */, const std::function<bool()>& aWaitCondition /* = nullptr */) { MOZ_ASSERT(mExternalShmem);
--- a/gfx/vr/gfxVRExternal.h +++ b/gfx/vr/gfxVRExternal.h @@ -9,27 +9,26 @@ #include "nsTArray.h" #include "nsIScreen.h" #include "nsCOMPtr.h" #include "mozilla/RefPtr.h" #include "mozilla/gfx/2D.h" #include "mozilla/EnumeratedArray.h" +#include "mozilla/UniquePtr.h" #include "gfxVR.h" #include "VRDisplayHost.h" #if defined(XP_MACOSX) class MacIOSurface; #endif namespace mozilla { namespace gfx { -class VRThread; - namespace impl { class VRDisplayExternal : public VRDisplayHost { public: void ZeroSensor() override; protected: @@ -43,49 +42,61 @@ protected: uint64_t aFrameId, const gfx::Rect& aLeftEyeRect, const gfx::Rect& aRightEyeRect) override; public: explicit VRDisplayExternal(const VRDisplayState& aDisplayState); void Refresh(); const VRControllerState& GetLastControllerState(uint32_t aStateIndex) const; + void VibrateHaptic(uint32_t aControllerIdx, + uint32_t aHapticIndex, + double aIntensity, + double aDuration, + const VRManagerPromise& aPromise); + void StopVibrateHaptic(uint32_t aControllerIdx); + void StopAllHaptics(); + void Run1msTasks(double aDeltaTime) override; + void Run10msTasks() override; protected: virtual ~VRDisplayExternal(); void Destroy(); private: bool PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture, VRLayerTextureType* aTextureType, VRLayerTextureHandle* aTextureHandle); void PushState(bool aNotifyCond = false); #if defined(MOZ_WIDGET_ANDROID) bool PullState(const std::function<bool()>& aWaitCondition = nullptr); - void PostVRTask(); - void RunVRTask(); #else bool PullState(); #endif - + void ClearHapticSlot(size_t aSlot); + void ExpireNavigationTransition(); + void UpdateHaptics(double aDeltaTime); + nsTArray<UniquePtr<VRManagerPromise>> mHapticPromises; + // Duration of haptic pulse time remaining (milliseconds) + double mHapticPulseRemaining[kVRHapticsMaxCount]; VRTelemetry mTelemetry; TimeStamp mVRNavigationTransitionEnd; VRBrowserState mBrowserState; VRHMDSensorState mLastSensorState; }; } // namespace impl class VRSystemManagerExternal : public VRSystemManager { public: static already_AddRefed<VRSystemManagerExternal> Create(VRExternalShmem* aAPIShmem = nullptr); virtual void Destroy() override; virtual void Shutdown() override; - virtual void NotifyVSync() override; + virtual void Run100msTasks() override; virtual void Enumerate() override; virtual bool ShouldInhibitEnumeration() override; virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override; virtual bool GetIsPresenting() override; virtual void HandleInput() override; virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) override; virtual void ScanForControllers() override;
--- a/gfx/vr/gfxVROculus.cpp +++ b/gfx/vr/gfxVROculus.cpp @@ -1316,23 +1316,23 @@ VRControllerOculus::VRControllerOculus(d , mHandTrigger(0.0f) , mVibrateThread(nullptr) , mIsVibrateStopped(false) { MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost); VRControllerState& state = mControllerInfo.mControllerState; - const char* touchID = ""; + char* touchID = (char*)""; switch (aHand) { case dom::GamepadHand::Left: - touchID = "Oculus Touch (Left)"; + touchID = (char*)"Oculus Touch (Left)"; break; case dom::GamepadHand::Right: - touchID = "Oculus Touch (Right)"; + touchID = (char*)"Oculus Touch (Right)"; break; default: MOZ_ASSERT(false); break; } strncpy(state.controllerName, touchID, kVRControllerNameMaxLen); @@ -1803,16 +1803,23 @@ VRSystemManagerOculus::HandleButtonPress return; } if (pressedDiff & aButtonMask || touchedDiff & aButtonMask) { // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or // touched event, otherwise it is an old event and needs to notify // the button has been released. + if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("Oculus handleButton(aButton = %d, length = %d, controller: %s.)", + aButton, + controller->GetControllerInfo().GetNumButtons(), + controller->GetControllerInfo().GetControllerName()); + } NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed, aButtonMask & aButtonTouched, (aButtonMask & aButtonPressed) ? 1.0L : 0.0L); } } void VRSystemManagerOculus::HandleIndexTriggerPress(uint32_t aControllerIdx, @@ -1829,16 +1836,23 @@ VRSystemManagerOculus::HandleIndexTrigge // We prefer to let developers to set their own threshold for the adjustment. // Therefore, we don't check ButtonPressed and ButtonTouched with TouchMask here. // we just check the button value is larger than the threshold value or not. const float threshold = gfxPrefs::VRControllerTriggerThreshold(); // Avoid sending duplicated events in IPC channels. if (oldValue != aValue || touchedDiff & aTouchMask) { + if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("Oculus handleIndexTrigger(aButton = %d, length = %d, controller: %s.)", + aButton, + controller->GetControllerInfo().GetNumButtons(), + controller->GetControllerInfo().GetControllerName()); + } NewButtonEvent(aControllerIdx, aButton, aValue > threshold, aButtonTouched & aTouchMask, aValue); controller->SetIndexTrigger(aValue); } } void VRSystemManagerOculus::HandleHandTriggerPress(uint32_t aControllerIdx, @@ -1850,31 +1864,45 @@ VRSystemManagerOculus::HandleHandTrigger const float oldValue = controller->GetHandTrigger(); // We prefer to let developers to set their own threshold for the adjustment. // Therefore, we don't check ButtonPressed and ButtonTouched with TouchMask here. // we just check the button value is larger than the threshold value or not. const float threshold = gfxPrefs::VRControllerTriggerThreshold(); // Avoid sending duplicated events in IPC channels. if (oldValue != aValue) { + if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("Oculus handleHandTrigger(aButton = %d, length = %d, controller: %s.)", + aButton, + controller->GetControllerInfo().GetNumButtons(), + controller->GetControllerInfo().GetControllerName()); + } NewButtonEvent(aControllerIdx, aButton, aValue > threshold, aValue > threshold, aValue); controller->SetHandTrigger(aValue); } } void VRSystemManagerOculus::HandleTouchEvent(uint32_t aControllerIdx, uint32_t aButton, uint64_t aTouchMask, uint64_t aButtonTouched) { RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]); MOZ_ASSERT(controller); const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched); if (touchedDiff & aTouchMask) { + if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("Oculus HandleTouchEvent(aButton = %d, length = %d, controller: %s.)", + aButton, + controller->GetControllerInfo().GetNumButtons(), + controller->GetControllerInfo().GetControllerName()); + } NewButtonEvent(aControllerIdx, aButton, false, aTouchMask & aButtonTouched, 0.0f); } } void VRSystemManagerOculus::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis, float aValue) { @@ -1882,16 +1910,23 @@ VRSystemManagerOculus::HandleAxisMove(ui MOZ_ASSERT(controller); float value = aValue; if (abs(aValue) < 0.0000009f) { value = 0.0f; // Clear noise signal } if (controller->GetAxisMove(aAxis) != value) { + if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("Oculus HandleAxisMove(aAxis = %d, length = %d, controller: %s.)", + aAxis, + controller->GetControllerInfo().GetNumAxes(), + controller->GetControllerInfo().GetControllerName()); + } NewAxisMove(aControllerIdx, aAxis, value); controller->SetAxisMove(aAxis, value); } } void VRSystemManagerOculus::HandlePoseTracking(uint32_t aControllerIdx, const GamepadPoseState& aPose, @@ -1990,17 +2025,21 @@ VRSystemManagerOculus::ScanForController switch (activeControllerArray[i]) { case ovrControllerType::ovrControllerType_LTouch: hand = GamepadHand::Left; break; case ovrControllerType::ovrControllerType_RTouch: hand = GamepadHand::Right; break; default: +<<<<<<< working copy + continue; +======= break; +>>>>>>> merge rev } RefPtr<VRControllerOculus> oculusController = new VRControllerOculus(hand, mDisplay->GetDisplayInfo().GetDisplayID()); mOculusController.AppendElement(oculusController); // Not already present, add it. AddGamepad(oculusController->GetControllerInfo());
--- a/gfx/vr/gfxVROpenVR.cpp +++ b/gfx/vr/gfxVROpenVR.cpp @@ -792,107 +792,146 @@ VRSystemManagerOpenVR::HandleInput() case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad: if (mIsWindowsMR) { // Adjust the input mapping for Windows MR which has // different order. axisIdx = (axisIdx == 0) ? 2 : 0; buttonIdx = (buttonIdx == 0) ? 4 : 0; } - HandleAxisMove(i, axisIdx, - state.rAxis[j].x); + if (!HandleAxisMove(i, axisIdx, state.rAxis[j].x)) { + RemoveControllers(); + return; + } ++axisIdx; - HandleAxisMove(i, axisIdx, - state.rAxis[j].y * yAxisInvert); + + if (!HandleAxisMove(i, axisIdx, state.rAxis[j].y * yAxisInvert)) { + RemoveControllers(); + return; + } ++axisIdx; - HandleButtonPress(i, buttonIdx, - ::vr::ButtonMaskFromId( - static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)), - state.ulButtonPressed, state.ulButtonTouched); + + if (!HandleButtonPress(i, buttonIdx, + ::vr::ButtonMaskFromId( + static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; if (mIsWindowsMR) { axisIdx = (axisIdx == 4) ? 2 : 4; buttonIdx = (buttonIdx == 5) ? 1 : 2; } break; case vr::EVRControllerAxisType::k_eControllerAxis_Trigger: if (j <= 2) { - HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x); + if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) { + RemoveControllers(); + return; + } ++buttonIdx; ++triggerIdx; } else { // For SteamVR Knuckles. - HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x); + if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) { + RemoveControllers(); + return; + } ++buttonIdx; ++triggerIdx; - HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y); + if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y)) { + RemoveControllers(); + return; + } ++buttonIdx; ++triggerIdx; } break; } } MOZ_ASSERT(axisIdx == controller->GetControllerInfo().GetNumAxes()); const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty( trackedIndex, ::vr::Prop_SupportedButtons_Uint64); if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_A)) { - HandleButtonPress(i, buttonIdx, - BTN_MASK_FROM_ID(k_EButton_A), - state.ulButtonPressed, state.ulButtonTouched); + if (!HandleButtonPress(i, buttonIdx, + BTN_MASK_FROM_ID(k_EButton_A), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_Grip)) { - HandleButtonPress(i, buttonIdx, - BTN_MASK_FROM_ID(k_EButton_Grip), - state.ulButtonPressed, state.ulButtonTouched); + if (!HandleButtonPress(i, buttonIdx, + BTN_MASK_FROM_ID(k_EButton_Grip), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) { - HandleButtonPress(i, buttonIdx, - BTN_MASK_FROM_ID(k_EButton_ApplicationMenu), - state.ulButtonPressed, state.ulButtonTouched); + if (!HandleButtonPress(i, buttonIdx, + BTN_MASK_FROM_ID(k_EButton_ApplicationMenu), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } if (mIsWindowsMR) { // button 4 in Windows MR has already been assigned // to k_eControllerAxis_TrackPad. ++buttonIdx; } if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_DPad_Left)) { - HandleButtonPress(i, buttonIdx, - BTN_MASK_FROM_ID(k_EButton_DPad_Left), - state.ulButtonPressed, state.ulButtonTouched); + if (!HandleButtonPress(i, buttonIdx, + BTN_MASK_FROM_ID(k_EButton_DPad_Left), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_DPad_Up)) { - HandleButtonPress(i, buttonIdx, + if (!HandleButtonPress(i, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Up), - state.ulButtonPressed, state.ulButtonTouched); + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_DPad_Right)) { - HandleButtonPress(i, buttonIdx, - BTN_MASK_FROM_ID(k_EButton_DPad_Right), - state.ulButtonPressed, state.ulButtonTouched); + if (!HandleButtonPress(i, buttonIdx, + BTN_MASK_FROM_ID(k_EButton_DPad_Right), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } if (supportedButtons & BTN_MASK_FROM_ID(k_EButton_DPad_Down)) { - HandleButtonPress(i, buttonIdx, - BTN_MASK_FROM_ID(k_EButton_DPad_Down), - state.ulButtonPressed, state.ulButtonTouched); + if (!HandleButtonPress(i, buttonIdx, + BTN_MASK_FROM_ID(k_EButton_DPad_Down), + state.ulButtonPressed, state.ulButtonTouched)) { + RemoveControllers(); + return; + } ++buttonIdx; } MOZ_ASSERT(buttonIdx == controller->GetControllerInfo().GetNumButtons()); controller->SetButtonPressed(state.ulButtonPressed); controller->SetButtonTouched(state.ulButtonTouched); // Start to process pose @@ -936,77 +975,104 @@ VRSystemManagerOpenVR::HandleInput() poseState.linearVelocity[2] = pose.vVelocity.v[2]; poseState.isPositionValid = true; } HandlePoseTracking(i, poseState, controller); } } } -void +bool VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx, uint32_t aButton, uint64_t aButtonMask, uint64_t aButtonPressed, uint64_t aButtonTouched) { RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]); MOZ_ASSERT(controller); const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed); const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched); if (!pressedDiff && !touchedDiff) { - return; + return true; } if (pressedDiff & aButtonMask || touchedDiff & aButtonMask) { // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or // touched event, otherwise it is an old event and needs to notify // the button has been released. + if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleButton(aButton = %d, length = %d, controller: %s.)", + aButton, + controller->GetControllerInfo().GetNumButtons(), + controller->GetControllerInfo().GetControllerName()); + return false; + } NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed, aButtonMask & aButtonTouched, (aButtonMask & aButtonPressed) ? 1.0L : 0.0L); } + return true; } -void +bool VRSystemManagerOpenVR::HandleTriggerPress(uint32_t aControllerIdx, uint32_t aButton, uint32_t aTrigger, float aValue) { RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]); MOZ_ASSERT(controller); const float oldValue = controller->GetTrigger(aTrigger); // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55. // We prefer to let developers to set their own threshold for the adjustment. // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here. // we just check the button value is larger than the threshold value or not. const float threshold = gfxPrefs::VRControllerTriggerThreshold(); // Avoid sending duplicated events in IPC channels. if (oldValue != aValue) { + if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleTrigger(aButton = %d, length = %d, controller: %s.)", + aButton, + controller->GetControllerInfo().GetNumButtons(), + controller->GetControllerInfo().GetControllerName()); + return false; + } NewButtonEvent(aControllerIdx, aButton, aValue > threshold, aValue > threshold, aValue); controller->SetTrigger(aTrigger, aValue); } + return true; } -void +bool VRSystemManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis, float aValue) { RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]); MOZ_ASSERT(controller); if (controller->GetAxisMove(aAxis) != aValue) { + if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) { + // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count. + MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleAxis(aAxis = %d, length = %d, controller: %s.)", + aAxis, + controller->GetControllerInfo().GetNumAxes(), + controller->GetControllerInfo().GetControllerName()); + return false; + } NewAxisMove(aControllerIdx, aAxis, aValue); controller->SetAxisMove(aAxis, aValue); } + return true; } void VRSystemManagerOpenVR::HandlePoseTracking(uint32_t aControllerIdx, const GamepadPoseState& aPose, VRControllerHost* aController) { MOZ_ASSERT(aController);
--- a/gfx/vr/gfxVROpenVR.h +++ b/gfx/vr/gfxVROpenVR.h @@ -141,26 +141,26 @@ public: double aDuration, const VRManagerPromise& aPromise) override; virtual void StopVibrateHaptic(uint32_t aControllerIdx) override; protected: VRSystemManagerOpenVR(); private: - void HandleButtonPress(uint32_t aControllerIdx, + bool HandleButtonPress(uint32_t aControllerIdx, uint32_t aButton, uint64_t aButtonMask, uint64_t aButtonPressed, uint64_t aButtonTouched); - void HandleTriggerPress(uint32_t aControllerIdx, + bool HandleTriggerPress(uint32_t aControllerIdx, uint32_t aButton, uint32_t aTrigger, float aValue); - void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis, + bool HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis, float aValue); void HandlePoseTracking(uint32_t aControllerIdx, const dom::GamepadPoseState& aPose, VRControllerHost* aController); dom::GamepadHand GetGamepadHandFromControllerRole( ::vr::ETrackedControllerRole aRole); void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType, ::vr::TrackedDeviceIndex_t aDeviceIndex,
--- a/gfx/vr/gfxVRPuppet.cpp +++ b/gfx/vr/gfxVRPuppet.cpp @@ -707,16 +707,31 @@ VRSystemManagerPuppet::Destroy() void VRSystemManagerPuppet::Shutdown() { mPuppetHMDs.Clear(); } void +VRSystemManagerPuppet::Run10msTasks() +{ + VRSystemManager::Run10msTasks(); + + /** + * When running headless mochitests on some of our automated test + * infrastructure, 2d display vsyncs are not always generated. + * To workaround, we produce a vsync manually. + */ + VRManager *vm = VRManager::Get(); + MOZ_ASSERT(vm); + vm->NotifyVsync(TimeStamp::Now()); +} + +void VRSystemManagerPuppet::NotifyVSync() { VRSystemManager::NotifyVSync(); for (const auto& display: mPuppetHMDs) { display->Refresh(); } }
--- a/gfx/vr/gfxVRPuppet.h +++ b/gfx/vr/gfxVRPuppet.h @@ -134,16 +134,17 @@ public: virtual void RemoveControllers() override; virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex, double aIntensity, double aDuration, const VRManagerPromise& aPromise) override; virtual void StopVibrateHaptic(uint32_t aControllerIdx) override; virtual void NotifyVSync() override; + virtual void Run10msTasks() override; protected: VRSystemManagerPuppet(); private: void HandleButtonPress(uint32_t aControllerIdx, uint32_t aButton, uint64_t aButtonMask,
--- a/gfx/vr/ipc/VRGPUChild.cpp +++ b/gfx/vr/ipc/VRGPUChild.cpp @@ -38,42 +38,13 @@ VRGPUChild::Get() MOZ_ASSERT(IsCreated(), "VRGPUChild haven't initialized yet."); return sVRGPUChildSingleton; } /*static*/ void VRGPUChild::ShutDown() { MOZ_ASSERT(NS_IsMainThread()); - if (sVRGPUChildSingleton) { - sVRGPUChildSingleton->Destroy(); - sVRGPUChildSingleton = nullptr; - } -} - -class DeferredDeleteVRGPUChild : public Runnable -{ -public: - explicit DeferredDeleteVRGPUChild(RefPtr<VRGPUChild> aChild) - : Runnable("gfx::DeferredDeleteVRGPUChild") - , mChild(std::move(aChild)) - { - } - - NS_IMETHODIMP Run() override { - mChild->Close(); - return NS_OK; - } - -private: - RefPtr<VRGPUChild> mChild; -}; - -void -VRGPUChild::Destroy() -{ - // Keep ourselves alive until everything has been shut down - RefPtr<VRGPUChild> selfRef = this; - NS_DispatchToMainThread(new DeferredDeleteVRGPUChild(this)); + sVRGPUChildSingleton = nullptr; } } // namespace gfx } // namespace mozilla
--- a/gfx/vr/ipc/VRGPUChild.h +++ b/gfx/vr/ipc/VRGPUChild.h @@ -21,18 +21,16 @@ public: static bool InitForGPUProcess(Endpoint<PVRGPUChild>&& aEndpoint); static bool IsCreated(); static void ShutDown(); protected: explicit VRGPUChild() {} ~VRGPUChild() {} - void Destroy(); - private: DISALLOW_COPY_AND_ASSIGN(VRGPUChild); }; } // namespace gfx } // namespace mozilla #endif // GFX_VR_GPU_CHILD_H \ No newline at end of file
--- a/gfx/vr/ipc/VRManagerParent.cpp +++ b/gfx/vr/ipc/VRManagerParent.cpp @@ -128,16 +128,17 @@ VRManagerParent::CreateSameProcess() bool VRManagerParent::CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint) { MessageLoop* loop = VRListenerThreadHolder::Loop(); RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid(), false); vmp->mVRListenerThreadHolder = VRListenerThreadHolder::GetSingleton(); + vmp->mSelfRef = vmp; loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>( "gfx::VRManagerParent::Bind", vmp, &VRManagerParent::Bind, std::move(aEndpoint))); return true; } @@ -221,17 +222,16 @@ VRManagerParent::RecvSetHaveEventListene mHaveEventListener = aHaveEventListener; return IPC_OK(); } mozilla::ipc::IPCResult VRManagerParent::RecvControllerListenerAdded() { // Force update the available controllers for GamepadManager, - // remove the existing controllers and sync them by NotifyVsync(). VRManager* vm = VRManager::Get(); vm->RemoveControllers(); mHaveControllerListener = true; return IPC_OK(); } mozilla::ipc::IPCResult VRManagerParent::RecvControllerListenerRemoved() @@ -271,35 +271,57 @@ mozilla::ipc::IPCResult VRManagerParent::RecvCreateVRServiceTestController(const nsCString& aID, const uint32_t& aPromiseID) { uint32_t controllerIdx = 1; // ID's are 1 based nsTArray<VRControllerInfo> controllerInfoArray; impl::VRControllerPuppet* controllerPuppet = nullptr; VRManager* vm = VRManager::Get(); /** - * When running headless mochitests on some of our automated test - * infrastructure, 2d display vsyncs are not always generated. - * In this case, the test controllers can't be created immediately - * after the VR display was created as the state of the VR displays - * are updated during vsync. - * To workaround, we produce a vsync manually. + * The controller is created asynchronously in the VRListener thread. + * We will wait up to kMaxControllerCreationTime milliseconds before + * assuming that the controller will never be created. */ - vm->NotifyVsync(TimeStamp::Now()); + const int kMaxControllerCreationTime = 1000; + /** + * min(100ms, kVRIdleTaskInterval) * 10 as a very + * pessimistic estimation of the maximum duration possible. + * It's possible that the IPC message queues could be so busy + * that this time is elapsed while still succeeding to create + * the controllers; however, in this case the browser would be + * locking up for more than a second at a time and something else + * has gone horribly wrong. + */ + const int kTestInterval = 10; + /** + * We will keep checking every kTestInterval milliseconds until + * we see the controllers or kMaxControllerCreationTime milliseconds + * have elapsed and we will give up. + */ - // Get VRControllerPuppet from VRManager - vm->GetVRControllerInfo(controllerInfoArray); - for (auto& controllerInfo : controllerInfoArray) { - if (controllerInfo.GetType() == VRDeviceType::Puppet) { - if (controllerIdx == mControllerTestID) { - controllerPuppet = static_cast<impl::VRControllerPuppet*>( - vm->GetController(controllerInfo.GetControllerID()).get()); - break; + int testDuration = 0; + while (!controllerPuppet && testDuration < kMaxControllerCreationTime) { + testDuration += kTestInterval; +#ifdef XP_WIN + Sleep(kTestInterval); +#else + sleep(kTestInterval); +#endif + + // Get VRControllerPuppet from VRManager + vm->GetVRControllerInfo(controllerInfoArray); + for (auto& controllerInfo : controllerInfoArray) { + if (controllerInfo.GetType() == VRDeviceType::Puppet) { + if (controllerIdx == mControllerTestID) { + controllerPuppet = static_cast<impl::VRControllerPuppet*>( + vm->GetController(controllerInfo.GetControllerID()).get()); + break; + } + ++controllerIdx; } - ++controllerIdx; } } // We might not have a controllerPuppet if the test did // not create a VR display first. if (!controllerPuppet) { // We send a device ID of "0" to indicate failure if (SendReplyCreateVRServiceTestController(aID, aPromiseID, 0)) {
--- a/gfx/vr/ipc/VRManagerParent.h +++ b/gfx/vr/ipc/VRManagerParent.h @@ -106,16 +106,20 @@ class VRManagerPromise final public: explicit VRManagerPromise(RefPtr<VRManagerParent> aParent, uint32_t aPromiseID) : mParent(aParent), mPromiseID(aPromiseID) {} ~VRManagerPromise() { mParent = nullptr; } + bool operator==(const VRManagerPromise& aOther) const + { + return mParent == aOther.mParent && mPromiseID == aOther.mPromiseID; + } private: RefPtr<VRManagerParent> mParent; uint32_t mPromiseID; }; } // namespace mozilla } // namespace gfx
--- a/gfx/vr/ipc/VRMessageUtils.h +++ b/gfx/vr/ipc/VRMessageUtils.h @@ -110,16 +110,19 @@ struct ParamTraits<mozilla::gfx::VRDispl WriteParam(aMsg, aParam.mDisplayID); WriteParam(aMsg, aParam.mPresentingGroups); WriteParam(aMsg, aParam.mGroupMask); WriteParam(aMsg, aParam.mFrameId); WriteParam(aMsg, aParam.mDisplayState); for (size_t i = 0; i < mozilla::ArrayLength(aParam.mLastSensorState); i++) { WriteParam(aMsg, aParam.mLastSensorState[i]); } + for (size_t i = 0; i < mozilla::ArrayLength(aParam.mLastFrameStart); i++) { + WriteParam(aMsg, aParam.mLastFrameStart[i]); + } for (size_t i = 0; i < mozilla::ArrayLength(aParam.mControllerState); i++) { WriteParam(aMsg, aParam.mControllerState[i]); } } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { if (!ReadParam(aMsg, aIter, &(aResult->mType)) || @@ -130,16 +133,21 @@ struct ParamTraits<mozilla::gfx::VRDispl !ReadParam(aMsg, aIter, &(aResult->mDisplayState))) { return false; } for (size_t i = 0; i < mozilla::ArrayLength(aResult->mLastSensorState); i++) { if (!ReadParam(aMsg, aIter, &(aResult->mLastSensorState[i]))) { return false; } } + for (size_t i = 0; i < mozilla::ArrayLength(aResult->mLastFrameStart); i++) { + if (!ReadParam(aMsg, aIter, &(aResult->mLastFrameStart[i]))) { + return false; + } + } for (size_t i = 0; i < mozilla::ArrayLength(aResult->mControllerState); i++) { if (!ReadParam(aMsg, aIter, &(aResult->mControllerState[i]))) { return false; } } return true; } };
--- a/gfx/vr/service/OpenVRSession.cpp +++ b/gfx/vr/service/OpenVRSession.cpp @@ -3,25 +3,31 @@ #if defined(XP_WIN) #include <d3d11.h> #include "mozilla/gfx/DeviceManagerDx.h" #endif // defined(XP_WIN) #include "mozilla/dom/GamepadEventTypes.h" #include "mozilla/dom/GamepadBinding.h" +#include "VRThread.h" #if !defined(M_PI) #define M_PI 3.14159265358979323846264338327950288 #endif #define BTN_MASK_FROM_ID(_id) \ ::vr::ButtonMaskFromId(vr::EVRButtonId::_id) -static const uint32_t kNumOpenVRHaptcs = 1; +// Haptic feedback is updated every 5ms, as this is +// the minimum period between new haptic pulse requests. +// Effectively, this results in a pulse width modulation +// with an interval of 5ms. Through experimentation, the +// maximum duty cycle was found to be about 3.9ms +const uint32_t kVRHapticUpdateInterval = 5; using namespace mozilla::gfx; namespace mozilla { namespace gfx { namespace { @@ -93,19 +99,22 @@ UpdateTrigger(VRControllerState& aState, }; // anonymous namespace OpenVRSession::OpenVRSession() : VRSession() , mVRSystem(nullptr) , mVRChaperone(nullptr) , mVRCompositor(nullptr) - , mControllerDeviceIndex{0} + , mControllerDeviceIndex{} + , mHapticPulseRemaining{} + , mHapticPulseIntensity{} , mShouldQuit(false) , mIsWindowsMR(false) + , mControllerHapticStateMutex("OpenVRSession::mControllerHapticStateMutex") { } OpenVRSession::~OpenVRSession() { Shutdown(); } @@ -157,16 +166,19 @@ OpenVRSession::Initialize(mozilla::gfx:: // Configure coordinate system mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated); if (!InitState(aSystemState)) { Shutdown(); return false; } + StartHapticThread(); + StartHapticTimer(); + // Succeeded return true; } #if defined(XP_WIN) bool OpenVRSession::CreateD3DObjects() { @@ -179,16 +191,18 @@ OpenVRSession::CreateD3DObjects() } return true; } #endif void OpenVRSession::Shutdown() { + StopHapticTimer(); + StopHapticThread(); if (mVRSystem || mVRCompositor || mVRSystem) { ::vr::VR_Shutdown(); mVRCompositor = nullptr; mVRChaperone = nullptr; mVRSystem = nullptr; } } @@ -375,16 +389,18 @@ OpenVRSession::UpdateHeadsetPose(VRSyste } } void OpenVRSession::EnumerateControllers(VRSystemState& aState) { MOZ_ASSERT(mVRSystem); + MutexAutoLock lock(mControllerHapticStateMutex); + bool controllerPresent[kVRControllerMaxCount] = { false }; // Basically, we would have HMDs in the tracked devices, // but we are just interested in the controllers. for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1; trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) { if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) { @@ -487,17 +503,17 @@ OpenVRSession::EnumerateControllers(VRSy } nsCString deviceId; GetControllerDeviceId(deviceType, trackedDevice, deviceId); strncpy(controllerState.controllerName, deviceId.BeginReading(), kVRControllerNameMaxLen); controllerState.numButtons = numButtons; controllerState.numAxes = numAxes; - controllerState.numHaptics = kNumOpenVRHaptcs; + controllerState.numHaptics = kNumOpenVRHaptics; // If the Windows MR controller doesn't has the amount // of buttons or axes as our expectation, switching off // the workaround for Windows MR. if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) { mIsWindowsMR = false; NS_WARNING("OpenVR - Switching off Windows MR mode."); } @@ -755,17 +771,16 @@ OpenVRSession::GetControllerDeviceId(::v void OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState) { UpdateHeadsetPose(aSystemState); UpdateEyeParameters(aSystemState); EnumerateControllers(aSystemState); UpdateControllerButtons(aSystemState); UpdateControllerPoses(aSystemState); - aSystemState.sensorState.inputFrameID++; } bool OpenVRSession::ShouldQuit() const { return mShouldQuit; } @@ -925,10 +940,158 @@ OpenVRSession::StopPresentation() } bool OpenVRSession::StartPresentation() { return true; } +void +OpenVRSession::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex, + float aIntensity, float aDuration) +{ + MutexAutoLock lock(mControllerHapticStateMutex); + if (aHapticIndex >= kNumOpenVRHaptics || + aControllerIdx >= kVRControllerMaxCount) { + return; + } + + ::vr::TrackedDeviceIndex_t deviceIndex = mControllerDeviceIndex[aControllerIdx]; + if (deviceIndex == 0) { + return; + } + + mHapticPulseRemaining[aControllerIdx][aHapticIndex] = aDuration; + mHapticPulseIntensity[aControllerIdx][aHapticIndex] = aIntensity; + + /** + * TODO - The haptic feedback pulses will have latency of one frame and we + * are simulating intensity with pulse-width modulation. + * We should use of the OpenVR Input API to correct this + * and replace the TriggerHapticPulse calls which have been + * deprecated. + */ +} + +void +OpenVRSession::StartHapticThread() +{ + if (!mHapticThread) { + mHapticThread = new VRThread(NS_LITERAL_CSTRING("VR_OpenVR_Haptics")); + } + mHapticThread->Start(); +} + +void +OpenVRSession::StopHapticThread() +{ + if (mHapticThread) { + mHapticThread->Shutdown(); + mHapticThread = nullptr; + } +} + +void +OpenVRSession::StartHapticTimer() +{ + if (!mHapticTimer && mHapticThread) { + mLastHapticUpdate = TimeStamp(); + mHapticTimer = NS_NewTimer(); + mHapticTimer->SetTarget(mHapticThread->GetThread()->EventTarget()); + mHapticTimer->InitWithNamedFuncCallback( + HapticTimerCallback, + this, + kVRHapticUpdateInterval, + nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP, + "OpenVRSession::HapticTimerCallback"); + } +} + +void +OpenVRSession::StopHapticTimer() +{ + if (mHapticTimer) { + mHapticTimer->Cancel(); + mHapticTimer = nullptr; + } +} + +/*static*/ void +OpenVRSession::HapticTimerCallback(nsITimer* aTimer, void* aClosure) +{ + /** + * It is safe to use the pointer passed in aClosure to reference the + * OpenVRSession object as the timer is canceled in OpenVRSession::Shutdown, + * which is called by the OpenVRSession destructor, guaranteeing + * that this function runs if and only if the VRManager object is valid. + */ + OpenVRSession* self = static_cast<OpenVRSession*>(aClosure); + self->UpdateHaptics(); +} + +void +OpenVRSession::UpdateHaptics() +{ + MOZ_ASSERT(mHapticThread->GetThread() == NS_GetCurrentThread()); + MOZ_ASSERT(mVRSystem); + + MutexAutoLock lock(mControllerHapticStateMutex); + + TimeStamp now = TimeStamp::Now(); + if (mLastHapticUpdate.IsNull()) { + mLastHapticUpdate = now; + return; + } + float deltaTime = (float)(now - mLastHapticUpdate).ToSeconds(); + mLastHapticUpdate = now; + + for (int iController = 0; iController < kVRControllerMaxCount; iController++) { + for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) { + ::vr::TrackedDeviceIndex_t deviceIndex = mControllerDeviceIndex[iController]; + if (deviceIndex == 0) { + continue; + } + float intensity = mHapticPulseIntensity[iController][iHaptic]; + float duration = mHapticPulseRemaining[iController][iHaptic]; + if (duration <= 0.0f || intensity <= 0.0f) { + continue; + } + // We expect OpenVR to vibrate for 5 ms, but we found it only response the + // commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them + // to a loop of 3.9 ms for make users feel that is a continuous events. + const float microSec = (duration < 0.0039f ? duration : 0.0039f) * 1000000.0f * intensity; + mVRSystem->TriggerHapticPulse(deviceIndex, iHaptic, (uint32_t)microSec); + + duration -= deltaTime; + if (duration < 0.0f) { + duration = 0.0f; + } + mHapticPulseRemaining[iController][iHaptic] = duration; + } + } +} + +void +OpenVRSession::StopVibrateHaptic(uint32_t aControllerIdx) +{ + MutexAutoLock lock(mControllerHapticStateMutex); + if (aControllerIdx >= kVRControllerMaxCount) { + return; + } + for (int iHaptic = 0; iHaptic < kNumOpenVRHaptics; iHaptic++) { + mHapticPulseRemaining[aControllerIdx][iHaptic] = 0.0f; + } +} + +void +OpenVRSession::StopAllHaptics() +{ + MutexAutoLock lock(mControllerHapticStateMutex); + for (auto& controller : mHapticPulseRemaining) { + for (auto& haptic : controller) { + haptic = 0.0f; + } + } +} + } // namespace mozilla } // namespace gfx
--- a/gfx/vr/service/OpenVRSession.h +++ b/gfx/vr/service/OpenVRSession.h @@ -6,50 +6,62 @@ #ifndef GFX_VR_SERVICE_OPENVRSESSION_H #define GFX_VR_SERVICE_OPENVRSESSION_H #include "VRSession.h" #include "openvr.h" #include "mozilla/gfx/2D.h" +#include "mozilla/TimeStamp.h" #include "moz_external_vr.h" #if defined(XP_WIN) #include <d3d11_1.h> #elif defined(XP_MACOSX) class MacIOSurface; #endif +class nsITimer; namespace mozilla { namespace gfx { +class VRThread; + +static const int kNumOpenVRHaptics = 1; class OpenVRSession : public VRSession { public: OpenVRSession(); virtual ~OpenVRSession(); bool Initialize(mozilla::gfx::VRSystemState& aSystemState) override; void Shutdown() override; void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) override; void StartFrame(mozilla::gfx::VRSystemState& aSystemState) override; bool ShouldQuit() const override; bool StartPresentation() override; void StopPresentation() override; bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override; + void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex, + float aIntensity, float aDuration) override; + void StopVibrateHaptic(uint32_t aControllerIdx) override; + void StopAllHaptics() override; private: // OpenVR State ::vr::IVRSystem* mVRSystem = nullptr; ::vr::IVRChaperone* mVRChaperone = nullptr; ::vr::IVRCompositor* mVRCompositor = nullptr; ::vr::TrackedDeviceIndex_t mControllerDeviceIndex[kVRControllerMaxCount]; + float mHapticPulseRemaining[kVRControllerMaxCount][kNumOpenVRHaptics]; + float mHapticPulseIntensity[kVRControllerMaxCount][kNumOpenVRHaptics]; bool mShouldQuit; bool mIsWindowsMR; + TimeStamp mLastHapticUpdate; bool InitState(mozilla::gfx::VRSystemState& aSystemState); void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState); void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState); void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState); void EnumerateControllers(VRSystemState& aState); void UpdateControllerPoses(VRSystemState& aState); void UpdateControllerButtons(VRSystemState& aState); @@ -59,14 +71,23 @@ private: const VRLayerEyeRect& aLeftEyeRect, const VRLayerEyeRect& aRightEyeRect); #if defined(XP_WIN) bool CreateD3DObjects(); #endif void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType, ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId); + void UpdateHaptics(); + void StartHapticThread(); + void StopHapticThread(); + void StartHapticTimer(); + void StopHapticTimer(); + static void HapticTimerCallback(nsITimer* aTimer, void* aClosure); + RefPtr<nsITimer> mHapticTimer; + RefPtr<VRThread> mHapticThread; + mozilla::Mutex mControllerHapticStateMutex; }; } // namespace mozilla } // namespace gfx #endif // GFX_VR_SERVICE_OPENVRSESSION_H
--- a/gfx/vr/service/VRService.cpp +++ b/gfx/vr/service/VRService.cpp @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "VRService.h" #include "OpenVRSession.h" #include "gfxPrefs.h" #include "base/thread.h" // for Thread +#include <cstring> // for memcmp using namespace mozilla; using namespace mozilla::gfx; using namespace std; namespace { int64_t @@ -52,20 +53,23 @@ VRService::Create() RefPtr<VRService> service = new VRService(); return service.forget(); } VRService::VRService() : mSystemState{} , mBrowserState{} + , mBrowserGeneration(0) , mServiceThread(nullptr) , mShutdownRequested(false) , mAPIShmem(nullptr) , mTargetShmemFile(0) + , mLastHapticState{} + , mFrameStartTime{} { // When we have the VR process, we map the memory // of mAPIShmem from GPU process. // If we don't have the VR process, we will instantiate // mAPIShmem in VRService. if (!gfxPrefs::VRProcessEnabled()) { mAPIShmem = new VRExternalShmem(); memset(mAPIShmem, 0, sizeof(VRExternalShmem)); @@ -285,67 +289,116 @@ VRService::ServiceWaitForImmersive() void VRService::ServiceImmersiveMode() { MOZ_ASSERT(IsInServiceThread()); MOZ_ASSERT(mSession); mSession->ProcessEvents(mSystemState); + UpdateHaptics(); PushState(mSystemState); PullState(mBrowserState); if (mSession->ShouldQuit() || mShutdownRequested) { // Shut down MessageLoop::current()->PostTask(NewRunnableMethod( "gfx::VRService::ServiceShutdown", this, &VRService::ServiceShutdown )); return; } else if (!IsImmersiveContentActive(mBrowserState)) { // Exit immersive mode + mSession->StopAllHaptics(); mSession->StopPresentation(); MessageLoop::current()->PostTask(NewRunnableMethod( "gfx::VRService::ServiceWaitForImmersive", this, &VRService::ServiceWaitForImmersive )); return; } uint64_t newFrameId = FrameIDFromBrowserState(mBrowserState); if (newFrameId != mSystemState.displayState.mLastSubmittedFrameId) { // A new immersive frame has been received. // Submit the textures to the VR system compositor. bool success = false; for (int iLayer=0; iLayer < kVRLayerMaxCount; iLayer++) { const VRLayerState& layer = mBrowserState.layerState[iLayer]; if (layer.type == VRLayerType::LayerType_Stereo_Immersive) { + // SubmitFrame may block in order to control the timing for + // the next frame start success = mSession->SubmitFrame(layer.layer_stereo_immersive); break; } } // Changing mLastSubmittedFrameId triggers a new frame to start // rendering. Changes to mLastSubmittedFrameId and the values // used for rendering, such as headset pose, must be pushed // atomically to the browser. mSystemState.displayState.mLastSubmittedFrameId = newFrameId; mSystemState.displayState.mLastSubmittedFrameSuccessful = success; + + // StartFrame may block to control the timing for the next frame start mSession->StartFrame(mSystemState); + mSystemState.sensorState.inputFrameID++; + size_t historyIndex = mSystemState.sensorState.inputFrameID % ArrayLength(mFrameStartTime); + mFrameStartTime[historyIndex] = TimeStamp::Now(); PushState(mSystemState); } // Continue immersive mode MessageLoop::current()->PostTask(NewRunnableMethod( "gfx::VRService::ServiceImmersiveMode", this, &VRService::ServiceImmersiveMode )); } void +VRService::UpdateHaptics() +{ + MOZ_ASSERT(IsInServiceThread()); + MOZ_ASSERT(mSession); + + for (size_t i = 0; i < ArrayLength(mBrowserState.hapticState); i++) { + VRHapticState& state = mBrowserState.hapticState[i]; + VRHapticState& lastState = mLastHapticState[i]; + // Note that VRHapticState is asserted to be a POD type, thus memcmp is safe + if (memcmp(&state, &lastState, sizeof(VRHapticState)) == 0) { + // No change since the last update + continue; + } + if (state.inputFrameID == 0) { + // The haptic feedback was stopped + mSession->StopVibrateHaptic(state.controllerIndex); + } else { + TimeStamp now; + if (now.IsNull()) { + // TimeStamp::Now() is expensive, so we + // must call it only when needed and save the + // output for further loop iterations. + now = TimeStamp::Now(); + } + // This is a new haptic pulse, or we are overriding a prior one + size_t historyIndex = state.inputFrameID % ArrayLength(mFrameStartTime); + float startOffset = (float)(now - mFrameStartTime[historyIndex]).ToSeconds(); + + // state.pulseStart is guaranteed never to be in the future + mSession->VibrateHaptic(state.controllerIndex, + state.hapticIndex, + state.pulseIntensity, + state.pulseDuration + state.pulseStart - startOffset); + } + // Record the state for comparison in the next run + memcpy(&lastState, &state, sizeof(VRHapticState)); + } +} + +void VRService::PushState(const mozilla::gfx::VRSystemState& aState) { if (!mAPIShmem) { return; } // Copying the VR service state to the shmem is atomic, infallable, // and non-blocking on x86/x64 architectures. Arm requires a mutex // that is locked for the duration of the memcpy to and from shmem on @@ -380,19 +433,22 @@ VRService::PullState(mozilla::gfx::VRBro #if defined(MOZ_WIDGET_ANDROID) if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) { memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState)); pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex)); } #else VRExternalShmem tmp; - memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem)); - if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) { - memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState)); + if (mAPIShmem->browserGenerationA != mBrowserGeneration) { + memcpy(&tmp, mAPIShmem, sizeof(VRExternalShmem)); + if (tmp.browserGenerationA == tmp.browserGenerationB && tmp.browserGenerationA != 0 && tmp.browserGenerationA != -1) { + memcpy(&aState, &tmp.browserState, sizeof(VRBrowserState)); + mBrowserGeneration = tmp.browserGenerationA; + } } #endif } VRExternalShmem* VRService::GetAPIShmem() { return mAPIShmem;
--- a/gfx/vr/service/VRService.h +++ b/gfx/vr/service/VRService.h @@ -14,16 +14,18 @@ namespace base { class Thread; } // namespace base namespace mozilla { namespace gfx { class VRSession; +static const int kVRFrameTimingHistoryDepth = 100; + class VRService { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRService) static already_AddRefed<VRService> Create(); void Start(); void Stop(); @@ -46,25 +48,29 @@ private: * by the browser. */ VRSystemState mSystemState; /** * VRBrowserState contains the most recent state of the browser. * mBrowserState is memcpy'ed from the Shmem atomically */ VRBrowserState mBrowserState; + int64_t mBrowserGeneration; UniquePtr<VRSession> mSession; base::Thread* mServiceThread; bool mShutdownRequested; VRExternalShmem* MOZ_OWNING_REF mAPIShmem; base::ProcessHandle mTargetShmemFile; + VRHapticState mLastHapticState[kVRHapticsMaxCount]; + TimeStamp mFrameStartTime[kVRFrameTimingHistoryDepth]; bool IsInServiceThread(); + void UpdateHaptics(); /** * The VR Service thread is a state machine that always has one * task queued depending on the state. * * VR Service thread state task functions: */ void ServiceInitialize();
--- a/gfx/vr/service/VRSession.h +++ b/gfx/vr/service/VRSession.h @@ -29,16 +29,20 @@ public: virtual bool Initialize(mozilla::gfx::VRSystemState& aSystemState) = 0; virtual void Shutdown() = 0; virtual void ProcessEvents(mozilla::gfx::VRSystemState& aSystemState) = 0; virtual void StartFrame(mozilla::gfx::VRSystemState& aSystemState) = 0; virtual bool ShouldQuit() const = 0; virtual bool StartPresentation() = 0; virtual void StopPresentation() = 0; virtual bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) = 0; + virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex, + float aIntensity, float aDuration) = 0; + virtual void StopVibrateHaptic(uint32_t aControllerIdx) = 0; + virtual void StopAllHaptics() = 0; #if defined(XP_WIN) protected: bool CreateD3DContext(RefPtr<ID3D11Device> aDevice); RefPtr<ID3D11Device1> mDevice; RefPtr<ID3D11DeviceContext1> mContext; ID3D11Device1* GetD3DDevice(); ID3D11DeviceContext1* GetD3DDeviceContext();
--- a/gfx/webrender_bindings/WebRenderTypes.h +++ b/gfx/webrender_bindings/WebRenderTypes.h @@ -96,44 +96,49 @@ ImageFormatToSurfaceFormat(ImageFormat a struct ImageDescriptor: public wr::WrImageDescriptor { // We need a default constructor for ipdl serialization. ImageDescriptor() { format = (ImageFormat)0; width = 0; height = 0; stride = 0; - is_opaque = false; + opacity = OpacityType::HasAlphaChannel; } ImageDescriptor(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) { format = wr::SurfaceFormatToImageFormat(aFormat).value(); width = aSize.width; height = aSize.height; stride = 0; - is_opaque = gfx::IsOpaqueFormat(aFormat); + opacity = gfx::IsOpaqueFormat(aFormat) ? OpacityType::Opaque + : OpacityType::HasAlphaChannel; } ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride, gfx::SurfaceFormat aFormat) { format = wr::SurfaceFormatToImageFormat(aFormat).value(); width = aSize.width; height = aSize.height; stride = aByteStride; - is_opaque = gfx::IsOpaqueFormat(aFormat); + opacity = gfx::IsOpaqueFormat(aFormat) ? OpacityType::Opaque + : OpacityType::HasAlphaChannel; } - ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride, gfx::SurfaceFormat aFormat, bool opaque) + ImageDescriptor(const gfx::IntSize& aSize, + uint32_t aByteStride, + gfx::SurfaceFormat aFormat, + OpacityType aOpacity) { format = wr::SurfaceFormatToImageFormat(aFormat).value(); width = aSize.width; height = aSize.height; stride = aByteStride; - is_opaque = opaque; + opacity = aOpacity; } }; // Whenever possible, use wr::WindowId instead of manipulating uint64_t. inline uint64_t AsUint64(const WindowId& aId) { return static_cast<uint64_t>(aId.mHandle); }
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -33,16 +33,24 @@ use core_graphics::font::CGFont; pub enum WrExternalImageBufferType { TextureHandle = 0, TextureRectHandle = 1, TextureArrayHandle = 2, TextureExternalHandle = 3, ExternalBuffer = 4, } +/// Used to indicate if an image is opaque, or has an alpha channel. +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum OpacityType { + Opaque = 0, + HasAlphaChannel = 1, +} + impl WrExternalImageBufferType { fn to_wr(self) -> ExternalImageType { match self { WrExternalImageBufferType::TextureHandle => ExternalImageType::TextureHandle(TextureTarget::Default), WrExternalImageBufferType::TextureRectHandle => ExternalImageType::TextureHandle(TextureTarget::Rect), WrExternalImageBufferType::TextureArrayHandle => @@ -278,30 +286,30 @@ impl From<ImageMask> for WrImageMask { #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct WrImageDescriptor { pub format: ImageFormat, pub width: u32, pub height: u32, pub stride: u32, - pub is_opaque: bool, + pub opacity: OpacityType, } impl<'a> Into<ImageDescriptor> for &'a WrImageDescriptor { fn into(self) -> ImageDescriptor { ImageDescriptor { size: DeviceUintSize::new(self.width, self.height), stride: if self.stride != 0 { Some(self.stride) } else { None }, format: self.format, - is_opaque: self.is_opaque, + is_opaque: self.opacity == OpacityType::Opaque, offset: 0, allow_mipmaps: false, } } } /// cbindgen:field-names=[mHandle] #[repr(C)]
--- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -160,16 +160,24 @@ enum class MixBlendMode : uint32_t { Hue = 12, Saturation = 13, Color = 14, Luminosity = 15, Sentinel /* this must be last for serialization purposes. */ }; +// Used to indicate if an image is opaque, or has an alpha channel. +enum class OpacityType : uint8_t { + Opaque = 0, + HasAlphaChannel = 1, + + Sentinel /* this must be last for serialization purposes. */ +}; + enum class RepeatMode : uint32_t { Stretch, Repeat, Round, Space, Sentinel /* this must be last for serialization purposes. */ }; @@ -1006,24 +1014,24 @@ struct WrExternalImageHandler { } }; struct WrImageDescriptor { ImageFormat format; uint32_t width; uint32_t height; uint32_t stride; - bool is_opaque; + OpacityType opacity; bool operator==(const WrImageDescriptor& aOther) const { return format == aOther.format && width == aOther.width && height == aOther.height && stride == aOther.stride && - is_opaque == aOther.is_opaque; + opacity == aOther.opacity; } }; using NormalizedRect = TypedRect<float, NormalizedCoordinates>; struct WrTransformProperty { uint64_t id; LayoutTransform transform;
--- a/gfx/ycbcr/YCbCrUtils.cpp +++ b/gfx/ycbcr/YCbCrUtils.cpp @@ -101,70 +101,72 @@ ConvertYCbCrToRGB(const layers::PlanarYC (aData.mCbCrSize.height == aData.mYSize.height || aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1)); // Used if converting to 8 bits YUV. UniquePtr<uint8_t[]> yChannel; UniquePtr<uint8_t[]> cbChannel; UniquePtr<uint8_t[]> crChannel; layers::PlanarYCbCrData dstData; - const layers::PlanarYCbCrData& srcData = aData.mBitDepth == 8 ? aData : dstData; + const layers::PlanarYCbCrData& srcData = + aData.mColorDepth == ColorDepth::COLOR_8 ? aData : dstData; - if (aData.mBitDepth != 8) { - MOZ_ASSERT(aData.mBitDepth > 8 && aData.mBitDepth <= 16); + if (aData.mColorDepth != ColorDepth::COLOR_8) { // Convert to 8 bits data first. dstData.mPicSize = aData.mPicSize; dstData.mPicX = aData.mPicX; dstData.mPicY = aData.mPicY; dstData.mYSize = aData.mYSize; // We align the destination stride to 32 bytes, so that libyuv can use // SSE optimised code. dstData.mYStride = (aData.mYSize.width + 31) & ~31; dstData.mCbCrSize = aData.mCbCrSize; dstData.mCbCrStride = (aData.mCbCrSize.width + 31) & ~31; dstData.mYUVColorSpace = aData.mYUVColorSpace; - dstData.mBitDepth = 8; + dstData.mColorDepth = ColorDepth::COLOR_8; size_t ySize = GetAlignedStride<1>(dstData.mYStride, aData.mYSize.height); size_t cbcrSize = GetAlignedStride<1>(dstData.mCbCrStride, aData.mCbCrSize.height); if (ySize == 0 || cbcrSize == 0) { return; } yChannel = MakeUnique<uint8_t[]>(ySize); cbChannel = MakeUnique<uint8_t[]>(cbcrSize); crChannel = MakeUnique<uint8_t[]>(cbcrSize); dstData.mYChannel = yChannel.get(); dstData.mCbChannel = cbChannel.get(); dstData.mCrChannel = crChannel.get(); + int bitDepth = BitDepthForColorDepth(aData.mColorDepth); + ConvertYCbCr16to8Line(dstData.mYChannel, dstData.mYStride, reinterpret_cast<uint16_t*>(aData.mYChannel), aData.mYStride / 2, aData.mYSize.width, aData.mYSize.height, - aData.mBitDepth); + bitDepth); ConvertYCbCr16to8Line(dstData.mCbChannel, dstData.mCbCrStride, reinterpret_cast<uint16_t*>(aData.mCbChannel), aData.mCbCrStride / 2, aData.mCbCrSize.width, aData.mCbCrSize.height, - aData.mBitDepth); + bitDepth); ConvertYCbCr16to8Line(dstData.mCrChannel, dstData.mCbCrStride, reinterpret_cast<uint16_t*>(aData.mCrChannel), aData.mCbCrStride / 2, aData.mCbCrSize.width, aData.mCbCrSize.height, - aData.mBitDepth); + bitDepth); } YUVType yuvtype = TypeFromSize(srcData.mYSize.width, srcData.mYSize.height, srcData.mCbCrSize.width, srcData.mCbCrSize.height);
--- a/intl/l10n/docs/fluent_tutorial.rst +++ b/intl/l10n/docs/fluent_tutorial.rst @@ -651,16 +651,17 @@ select the strategy to be used: This strategy replaces all Latin characters with their accented equivalents, and duplicates some vowels to create roughly 30% longer strings. - :js:`bidi` - ɥsıʅƃuƎ ıpıԐ This strategy replaces all Latin characters with their 180 degree rotated versions and enforces right to left text flow using Unicode UAX#9 `Explicit Directional Embeddings`__. + In this mode, the UI directionality will also be set to right-to-left. __ https://www.unicode.org/reports/tr9/#Explicit_Directional_Embeddings Inner Structure of Fluent ========================= The inner structure of Fluent in Gecko is out of scope of this tutorial, but since the class and file names may show up during debugging or profiling,
--- a/intl/locale/LocaleService.cpp +++ b/intl/locale/LocaleService.cpp @@ -411,23 +411,37 @@ LocaleService::FilterMatches(const nsTAr HANDLE_STRATEGY; } } } bool LocaleService::IsAppLocaleRTL() { - nsAutoCString locale; - GetAppLocaleAsBCP47(locale); - + // First, let's check if there's a manual override + // preference for directionality set. int pref = Preferences::GetInt("intl.uidirection", -1); if (pref >= 0) { return (pref > 0); } + + // If not, check if there is a pseudo locale `bidi` + // set. + nsAutoCString locale; + if (NS_SUCCEEDED(Preferences::GetCString("intl.l10n.pseudo", locale))) { + if (locale.EqualsLiteral("bidi")) { + return true; + } + if (locale.EqualsLiteral("accented")) { + return false; + } + } + + GetAppLocaleAsBCP47(locale); + return uloc_isRightToLeft(locale.get()); } NS_IMETHODIMP LocaleService::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) { MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
--- a/intl/locale/tests/unit/test_localeService.js +++ b/intl/locale/tests/unit/test_localeService.js @@ -137,16 +137,41 @@ add_test(function test_requestedLocales( }); add_test(function test_isAppLocaleRTL() { Assert.ok(typeof localeService.isAppLocaleRTL === 'boolean'); run_next_test(); }); +add_test(function test_isAppLocaleRTL_pseudo() { + let avLocales = localeService.availableLocales; + let reqLocales = localeService.requestedLocales; + + localeService.availableLocales = ["en-US"]; + localeService.requestedLocales = ["en-US"]; + Services.prefs.setIntPref("intl.uidirection", -1); + Services.prefs.setCharPref("intl.l10n.pseudo", ""); + + Assert.ok(localeService.isAppLocaleRTL === false); + + Services.prefs.setCharPref("intl.l10n.pseudo", "bidi"); + Assert.ok(localeService.isAppLocaleRTL === true); + + Services.prefs.setCharPref("intl.l10n.pseudo", "accented"); + Assert.ok(localeService.isAppLocaleRTL === false); + + // Clean up + localeService.availableLocales = avLocales; + localeService.requestedLocales = reqLocales; + Services.prefs.clearUserPref("intl.l10n.pseudo"); + + run_next_test(); +}); + add_test(function test_packagedLocales() { const locales = localeService.packagedLocales; Assert.ok(locales.length !== 0, "Packaged locales are empty"); run_next_test(); }); add_test(function test_availableLocales() { const avLocales = localeService.availableLocales;
--- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -1383,23 +1383,30 @@ AccessibleCaretManager::DispatchCaretSta commonAncestorFrame = commonAncestorNode->AsContent()->GetPrimaryFrame(); } if (commonAncestorFrame && rootFrame) { nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame, rect); nsRect clampedRect = nsLayoutUtils::ClampRectToScrollFrames(commonAncestorFrame, rect); nsLayoutUtils::TransformRect(commonAncestorFrame, rootFrame, clampedRect); - domRect->SetLayoutRect(clampedRect); + rect = clampedRect; init.mSelectionVisible = !clampedRect.IsEmpty(); } else { - domRect->SetLayoutRect(rect); init.mSelectionVisible = true; } + // The rect computed above is relative to rootFrame, which is the (layout) + // viewport frame. However, the consumers of this event expect the bounds + // of the selection relative to the screen (visual viewport origin), so + // translate between the two. + rect -= mPresShell->GetVisualViewportOffsetRelativeToLayoutViewport(); + + domRect->SetLayoutRect(rect); + // Send isEditable info w/ event detail. This info can help determine // whether to show cut command on selection dialog or not. init.mSelectionEditable = commonAncestorFrame && GetEditingHostForFrame(commonAncestorFrame); init.mBoundingClientRect = domRect; init.mReason = aReason; init.mCollapsed = sel->IsCollapsed();
--- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -3427,27 +3427,33 @@ ComputeWhereToScroll(int16_t aWhereToScr *aRangeMax = std::max(resultCoord, aRectMin); return resultCoord; } /** * This function takes a scrollable frame, a rect in the coordinate system * of the scrolled frame, and a desired percentage-based scroll * position and attempts to scroll the rect to that position in the - * scrollport. + * visual viewport. * * This needs to work even if aRect has a width or height of zero. + * + * Note that, since we are performing a layout scroll, it's possible that + * this fnction will sometimes be unsuccessful; the content will move as + * fast as it can on the screen using layout viewport scrolling, and then + * stop there, even if it could get closer to the desired position by + * moving the visual viewport within the layout viewport. */ static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable, const nsRect& aRect, nsIPresShell::ScrollAxis aVertical, nsIPresShell::ScrollAxis aHorizontal, uint32_t aFlags) { - nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition(); + nsPoint scrollPt = aFrameAsScrollable->GetVisualViewportOffset(); nsRect visibleRect(scrollPt, aFrameAsScrollable->GetVisualViewportSize()); nsSize lineSize; // Don't call GetLineScrollAmount unless we actually need it. Not only // does this save time, but it's not safe to call GetLineScrollAmount // during reflow (because it depends on font size inflation and doesn't // use the in-reflow-safe font-size inflation path). If we did call it, @@ -10601,16 +10607,26 @@ nsIPresShell::SetVisualViewportSize(nsco if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) { rootScrollFrame->MarkScrollbarsDirtyForReflow(); } MarkFixedFramesForReflow(nsIPresShell::eResize); } } +nsPoint +nsIPresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const +{ + nsPoint result; + if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) { + result = GetVisualViewportOffset() - sf->GetScrollPosition(); + } + return result; +} + void nsIPresShell::RecomputeFontSizeInflationEnabled() { mFontSizeInflationEnabled = DetermineFontSizeInflationState(); float fontScale = nsLayoutUtils::SystemFontScale(); if (fontScale == 0.0f) { return;
--- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -1672,16 +1672,18 @@ public: void SetVisualViewportOffset(const nsPoint& aScrollOffset) { mVisualViewportOffset = aScrollOffset; } nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; } + nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const; + virtual void WindowSizeMoveDone() = 0; virtual void SysColorChanged() = 0; virtual void ThemeChanged() = 0; virtual void BackingScaleFactorChanged() = 0; /** * Documents belonging to an invisible DocShell must not be painted ever. */
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2936,16 +2936,28 @@ ScrollFrameHelper::ScrollToImpl(nsPoint !isScrollOriginDowngrade; if (allowScrollOriginChange) { mLastScrollOrigin = aOrigin; mAllowScrollOriginDowngrade = false; } mLastSmoothScrollOrigin = nullptr; mScrollGeneration = ++sScrollGenerationCounter; + // If the new scroll offset is going to clobber APZ's scroll offset, for + // the RCD-RSF this will have the effect of resetting the visual viewport + // offset to be the same as the new scroll (= layout viewport) offset. + // The APZ callback transform, which reflects the difference between these + // offsets, will subsequently be cleared. However, it we wait for APZ to + // clear it, the main thread could end up using the old value and get + // incorrect results, so just clear it now. + if (mIsRoot && nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin)) { + content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(), + nsINode::DeleteProperty<CSSPoint>); + } + ScrollVisual(); bool schedulePaint = true; if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && !nsLayoutUtils::ShouldDisableApzForElement(content) && gfxPrefs::APZPaintSkipping()) { // If APZ is enabled with paint-skipping, there are certain conditions in // which we can skip paints: @@ -4112,16 +4124,26 @@ ScrollFrameHelper::GetVisualViewportSize { nsIPresShell* presShell = mOuter->PresShell(); if (mIsRoot && presShell->IsVisualViewportSizeSet()) { return presShell->GetVisualViewportSize(); } return mScrollPort.Size(); } +nsPoint +ScrollFrameHelper::GetVisualViewportOffset() const +{ + nsIPresShell* presShell = mOuter->PresShell(); + if (mIsRoot && presShell->IsVisualViewportSizeSet()) { + return presShell->GetVisualViewportOffset(); + } + return GetScrollPosition(); +} + static void AdjustForWholeDelta(int32_t aDelta, nscoord* aCoord) { if (aDelta < 0) { *aCoord = nscoord_MIN; } else if (aDelta > 0) { *aCoord = nscoord_MAX; }
--- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -212,16 +212,17 @@ public: mScrollPort.XMost() - mScrolledFrame->GetRect().XMost(); pt.y = mScrollPort.y - mScrolledFrame->GetPosition().y; return pt; } nsRect GetScrollRange() const; // Get the scroll range assuming the viewport has size (aWidth, aHeight). nsRect GetScrollRange(nscoord aWidth, nscoord aHeight) const; nsSize GetVisualViewportSize() const; + nsPoint GetVisualViewportOffset() const; void ScrollSnap(nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD); void ScrollSnap(const nsPoint &aDestination, nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD); protected: nsRect GetScrollRangeForClamping() const; public: @@ -852,16 +853,19 @@ public: return mHelper.GetLogicalScrollPosition(); } virtual nsRect GetScrollRange() const override { return mHelper.GetScrollRange(); } virtual nsSize GetVisualViewportSize() const override { return mHelper.GetVisualViewportSize(); } + virtual nsPoint GetVisualViewportOffset() const override { + return mHelper.GetVisualViewportOffset(); + } virtual nsSize GetLineScrollAmount() const override { return mHelper.GetLineScrollAmount(); } virtual nsSize GetPageScrollAmount() const override { return mHelper.GetPageScrollAmount(); } /** * @note This method might destroy the frame, pres shell and other objects. @@ -1304,16 +1308,19 @@ public: return mHelper.GetLogicalScrollPosition(); } virtual nsRect GetScrollRange() const override { return mHelper.GetScrollRange(); } virtual nsSize GetVisualViewportSize() const override { return mHelper.GetVisualViewportSize(); } + virtual nsPoint GetVisualViewportOffset() const override { + return mHelper.GetVisualViewportOffset(); + } virtual nsSize GetLineScrollAmount() const override { return mHelper.GetLineScrollAmount(); } virtual nsSize GetPageScrollAmount() const override { return mHelper.GetPageScrollAmount(); } /** * @note This method might destroy the frame, pres shell and other objects.
--- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -148,16 +148,25 @@ public: */ virtual nsRect GetScrollRange() const = 0; /** * Get the size of the view port to use when clamping the scroll * position. */ virtual nsSize GetVisualViewportSize() const = 0; /** + * Returns the offset of the visual viewport relative to + * the origin of the scrolled content. Note that only the RCD-RSF + * has a distinct visual viewport; for other scroll frames, the + * visual viewport always coincides with the layout viewport, and + * consequently the offset this function returns is equal to + * GetScrollPosition(). + */ + virtual nsPoint GetVisualViewportOffset() const = 0; + /** * Return how much we would try to scroll by in each direction if * asked to scroll by one "line" vertically and horizontally. */ virtual nsSize GetLineScrollAmount() const = 0; /** * Return how much we would try to scroll by in each direction if * asked to scroll by one "page" vertically and horizontally. */
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -253,16 +253,23 @@ pref("dom.script_loader.bytecode_cache.e // Other values might lead to experimental strategies. For more details, have a // look at: ScriptLoader::ShouldCacheBytecode function. pref("dom.script_loader.bytecode_cache.strategy", 0); #ifdef JS_BUILD_BINAST pref("dom.script_loader.binast_encoding.enabled", false); #endif +// Whether window.event is enabled +#ifdef NIGHTLY_BUILD +pref("dom.window.event.enabled", true); +#else +pref("dom.window.event.enabled", false); +#endif + // Fastback caching - if this pref is negative, then we calculate the number // of content viewers to cache based on the amount of available memory. pref("browser.sessionhistory.max_total_viewers", -1); pref("ui.use_native_colors", true); pref("ui.click_hold_context_menus", false); // Pop up context menu on mouseup instead of mousedown, if that's the OS default. @@ -2943,20 +2950,20 @@ pref("layout.css.initial-letter.enabled" // Is support for mix-blend-mode enabled? pref("layout.css.mix-blend-mode.enabled", true); // Is support for isolation enabled? pref("layout.css.isolation.enabled", true); // Is support for CSS Scrollbar color properties enabled? -pref("layout.css.scrollbar-colors.enabled", false); +pref("layout.css.scrollbar-colors.enabled", true); // Is support for scrollbar-width property enabled? -pref("layout.css.scrollbar-width.enabled", false); +pref("layout.css.scrollbar-width.enabled", true); // Set the threshold distance in CSS pixels below which scrolling will snap to // an edge, when scroll snapping is set to "proximity". pref("layout.css.scroll-snap.proximity-threshold", 200); // When selecting the snap point for CSS scroll snapping, the velocity of the // scroll frame is clamped to this speed, in CSS pixels / s. pref("layout.css.scroll-snap.prediction-max-velocity", 2000); @@ -5018,17 +5025,17 @@ pref("extensions.langpacks.signatures.re pref("extensions.webExtensionsMinPlatformVersion", "42.0a1"); pref("extensions.legacy.enabled", true); // Other webextensions prefs pref("extensions.webextensions.keepStorageOnUninstall", false); pref("extensions.webextensions.keepUuidOnUninstall", false); // Redirect basedomain used by identity api pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org"); -pref("extensions.webextensions.restrictedDomains", "accounts-static.cdn.mozilla.net,accounts.firefox.com,addons.cdn.mozilla.net,addons.mozilla.org,api.accounts.firefox.com,content.cdn.mozilla.net,content.cdn.mozilla.net,discovery.addons.mozilla.org,input.mozilla.org,install.mozilla.org,oauth.accounts.firefox.com,profile.accounts.firefox.com,support.mozilla.org,sync.services.mozilla.com,testpilot.firefox.com"); +pref("extensions.webextensions.restrictedDomains", "accounts-static.cdn.mozilla.net,accounts.firefox.com,addons.cdn.mozilla.net,addons.mozilla.org,api.accounts.firefox.com,content.cdn.mozilla.net,discovery.addons.mozilla.org,input.mozilla.org,install.mozilla.org,oauth.accounts.firefox.com,profile.accounts.firefox.com,support.mozilla.org,sync.services.mozilla.com,testpilot.firefox.com"); // Whether or not webextension icon theming is supported. pref("extensions.webextensions.themes.icons.enabled", false); pref("extensions.webextensions.remote", false); // Whether or not the moz-extension resource loads are remoted. For debugging // purposes only. Setting this to false will break moz-extension URI loading // unless other process sandboxing and extension remoting prefs are changed. pref("extensions.webextensions.protocol.remote", true);
--- a/netwerk/base/ProxyAutoConfig.cpp +++ b/netwerk/base/ProxyAutoConfig.cpp @@ -565,19 +565,18 @@ bool PACProxyAlert(JSContext *cx, unsign if (!arg1) return false; nsAutoJSString message; if (!message.init(cx, arg1)) return false; nsAutoString alertMessage; - alertMessage.SetCapacity(32 + message.Length()); - alertMessage += NS_LITERAL_STRING("PAC-alert: "); - alertMessage += message; + alertMessage.AssignLiteral(u"PAC-alert: "); + alertMessage.Append(message); PACLogToConsole(alertMessage); args.rval().setUndefined(); /* return undefined */ return true; } static const JSFunctionSpec PACGlobalFunctions[] = { JS_FN("dnsResolve", PACDnsResolve, 1, 0),
--- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -603,28 +603,16 @@ #define NS_THROTTLEQUEUE_CID \ { /* 4c39159c-cd90-4dd3-97a7-06af5e6d84c4 */ \ 0x4c39159c, \ 0xcd90, \ 0x4dd3, \ {0x97, 0xa7, 0x06, 0xaf, 0x5e, 0x6d, 0x84, 0xc4} \ } -// Background channel registrar used for pairing HttpChannelParent -// and its background channel -#define NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID \ - "@mozilla.org/network/background-channel-registrar;1" -#define NS_BACKGROUNDCHANNELREGISTRAR_CID \ -{ /* 6907788a-17cc-4c2a-a7c5-59ad2d9cc079 */ \ - 0x6907788a, \ - 0x17cc, \ - 0x4c2a, \ - { 0xa7, 0xc5, 0x59, 0xad, 0x2d, 0x9c, 0xc0, 0x79} \ -} - /****************************************************************************** * netwerk/protocol/ftp/ classes */ #define NS_FTPPROTOCOLHANDLER_CID \ { /* 25029490-F132-11d2-9588-00805F369F95 */ \ 0x25029490, \ 0xf132, \
--- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -32,16 +32,17 @@ #include "nsMimeTypes.h" #include "nsDNSPrefetch.h" #include "nsAboutProtocolHandler.h" #include "nsXULAppAPI.h" #include "nsCategoryCache.h" #include "nsIContentSniffer.h" #include "Predictor.h" #include "nsIThreadPool.h" +#include "mozilla/net/BackgroundChannelRegistrar.h" #include "mozilla/net/NeckoChild.h" #include "RedirectChannelRegistrar.h" #include "nsNetCID.h" #if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX) #define BUILD_NETWORK_INFO_SERVICE 1 #endif @@ -238,30 +239,28 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFt #undef LOG #undef LOG_ENABLED #include "nsHttpAuthManager.h" #include "nsHttpBasicAuth.h" #include "nsHttpDigestAuth.h" #include "nsHttpNTLMAuth.h" #include "nsHttpActivityDistributor.h" #include "ThrottleQueue.h" -#include "BackgroundChannelRegistrar.h" #undef LOG #undef LOG_ENABLED namespace mozilla { namespace net { NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpNTLMAuth) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsHttpHandler, nsHttpHandler::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpsHandler, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpAuthManager, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth) NS_GENERIC_FACTORY_CONSTRUCTOR(ThrottleQueue) -NS_GENERIC_FACTORY_CONSTRUCTOR(BackgroundChannelRegistrar) } // namespace net } // namespace mozilla #include "mozilla/net/Dashboard.h" namespace mozilla { namespace net { NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard) } // namespace net @@ -635,16 +634,18 @@ static void nsNetShutdown() // Release the Websocket Admission Manager mozilla::net::WebSocketChannel::Shutdown(); mozilla::net::Http2CompressionCleanup(); mozilla::net::RedirectChannelRegistrar::Shutdown(); + mozilla::net::BackgroundChannelRegistrar::Shutdown(); + delete gNetSniffers; gNetSniffers = nullptr; delete gDataSniffers; gDataSniffers = nullptr; } NS_DEFINE_NAMED_CID(NS_IOSERVICE_CID); NS_DEFINE_NAMED_CID(NS_STREAMTRANSPORTSERVICE_CID); @@ -705,17 +706,16 @@ NS_DEFINE_NAMED_CID(NS_FILEPROTOCOLHANDL NS_DEFINE_NAMED_CID(NS_HTTPPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_HTTPSPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_HTTPBASICAUTH_CID); NS_DEFINE_NAMED_CID(NS_HTTPDIGESTAUTH_CID); NS_DEFINE_NAMED_CID(NS_HTTPNTLMAUTH_CID); NS_DEFINE_NAMED_CID(NS_HTTPAUTHMANAGER_CID); NS_DEFINE_NAMED_CID(NS_HTTPACTIVITYDISTRIBUTOR_CID); NS_DEFINE_NAMED_CID(NS_THROTTLEQUEUE_CID); -NS_DEFINE_NAMED_CID(NS_BACKGROUNDCHANNELREGISTRAR_CID); NS_DEFINE_NAMED_CID(NS_FTPPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_RESPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_EXTENSIONPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURL_CID); NS_DEFINE_NAMED_CID(NS_SUBSTITUTINGURLMUTATOR_CID); NS_DEFINE_NAMED_CID(NS_ABOUTPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_SAFEABOUTPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_ABOUT_BLANK_MODULE_CID); @@ -821,17 +821,16 @@ static const mozilla::Module::CIDEntry k { &kNS_HTTPPROTOCOLHANDLER_CID, false, nullptr, mozilla::net::nsHttpHandlerConstructor }, { &kNS_HTTPSPROTOCOLHANDLER_CID, false, nullptr, mozilla::net::nsHttpsHandlerConstructor }, { &kNS_HTTPBASICAUTH_CID, false, nullptr, mozilla::net::nsHttpBasicAuthConstructor }, { &kNS_HTTPDIGESTAUTH_CID, false, nullptr, mozilla::net::nsHttpDigestAuthConstructor }, { &kNS_HTTPNTLMAUTH_CID, false, nullptr, mozilla::net::nsHttpNTLMAuthConstructor }, { &kNS_HTTPAUTHMANAGER_CID, false, nullptr, mozilla::net::nsHttpAuthManagerConstructor }, { &kNS_HTTPACTIVITYDISTRIBUTOR_CID, false, nullptr, mozilla::net::nsHttpActivityDistributorConstructor }, { &kNS_THROTTLEQUEUE_CID, false, nullptr, mozilla::net::ThrottleQueueConstructor }, - { &kNS_BACKGROUNDCHANNELREGISTRAR_CID, false, nullptr, mozilla::net::BackgroundChannelRegistrarConstructor }, { &kNS_FTPPROTOCOLHANDLER_CID, false, nullptr, nsFtpProtocolHandlerConstructor }, { &kNS_RESPROTOCOLHANDLER_CID, false, nullptr, nsResProtocolHandlerConstructor }, { &kNS_EXTENSIONPROTOCOLHANDLER_CID, false, nullptr, mozilla::ExtensionProtocolHandlerConstructor }, { &kNS_SUBSTITUTINGURL_CID, false, nullptr, mozilla::SubstitutingURLMutatorConstructor }, // do_CreateInstance returns mutator { &kNS_SUBSTITUTINGURLMUTATOR_CID, false, nullptr, mozilla::SubstitutingURLMutatorConstructor }, { &kNS_ABOUTPROTOCOLHANDLER_CID, false, nullptr, nsAboutProtocolHandlerConstructor }, { &kNS_SAFEABOUTPROTOCOLHANDLER_CID, false, nullptr, nsSafeAboutProtocolHandlerConstructor }, { &kNS_ABOUT_BLANK_MODULE_CID, false, nullptr, nsAboutBlank::Create }, @@ -943,17 +942,16 @@ static const mozilla::Module::ContractID { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &kNS_HTTPPROTOCOLHANDLER_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "https", &kNS_HTTPSPROTOCOLHANDLER_CID }, { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "basic", &kNS_HTTPBASICAUTH_CID }, { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "digest", &kNS_HTTPDIGESTAUTH_CID }, { NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "ntlm", &kNS_HTTPNTLMAUTH_CID }, { NS_HTTPAUTHMANAGER_CONTRACTID, &kNS_HTTPAUTHMANAGER_CID }, { NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &kNS_HTTPACTIVITYDISTRIBUTOR_CID }, { NS_THROTTLEQUEUE_CONTRACTID, &kNS_THROTTLEQUEUE_CID }, - { NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID, &kNS_BACKGROUNDCHANNELREGISTRAR_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &kNS_FTPPROTOCOLHANDLER_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "resource", &kNS_RESPROTOCOLHANDLER_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-extension", &kNS_EXTENSIONPROTOCOLHANDLER_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "about", &kNS_ABOUTPROTOCOLHANDLER_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-safe-about", &kNS_SAFEABOUTPROTOCOLHANDLER_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "blank", &kNS_ABOUT_BLANK_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "cache", &kNS_ABOUT_CACHE_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "cache-entry", &kNS_ABOUT_CACHE_ENTRY_MODULE_CID },
--- a/netwerk/protocol/about/nsAboutCacheEntry.cpp +++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp @@ -328,25 +328,27 @@ nsAboutCacheEntry::Channel::OnCacheEntry buffer.AppendLiteral("</td>\n" \ " </tr>\n"); \ PR_END_MACRO nsresult nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry) { nsresult rv; - nsCString buffer; + // This method appears to run in a situation where the run-time stack + // should have plenty of space, so allocating a large string on the + // stack is OK. + nsAutoCStringN<4097> buffer; uint32_t n; nsAutoCString str; rv = entry->GetKey(str); if (NS_FAILED(rv)) return rv; - buffer.SetCapacity(4096); buffer.AssignLiteral("<table>\n" " <tr>\n" " <th>key:</th>\n" " <td id=\"td-key\">"); // Test if the key is actually a URI nsCOMPtr<nsIURI> uri; bool isJS = false;
--- a/netwerk/protocol/http/BackgroundChannelRegistrar.cpp +++ b/netwerk/protocol/http/BackgroundChannelRegistrar.cpp @@ -5,16 +5,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "BackgroundChannelRegistrar.h" #include "HttpBackgroundChannelParent.h" #include "HttpChannelParent.h" #include "nsIInterfaceRequestor.h" #include "nsXULAppAPI.h" +#include "mozilla/StaticPtr.h" + +namespace { +mozilla::StaticRefPtr<mozilla::net::BackgroundChannelRegistrar> gSingleton; +} namespace mozilla { namespace net { NS_IMPL_ISUPPORTS(BackgroundChannelRegistrar, nsIBackgroundChannelRegistrar) BackgroundChannelRegistrar::BackgroundChannelRegistrar() { @@ -25,16 +30,34 @@ BackgroundChannelRegistrar::BackgroundCh MOZ_ASSERT(NS_IsMainThread()); } BackgroundChannelRegistrar::~BackgroundChannelRegistrar() { MOZ_ASSERT(NS_IsMainThread()); } +// static +already_AddRefed<nsIBackgroundChannelRegistrar> +BackgroundChannelRegistrar::GetOrCreate() +{ + if (!gSingleton) { + gSingleton = new BackgroundChannelRegistrar(); + } + return do_AddRef(gSingleton); +} + +// static +void +BackgroundChannelRegistrar::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + gSingleton = nullptr; +} + void BackgroundChannelRegistrar::NotifyChannelLinked( HttpChannelParent* aChannelParent, HttpBackgroundChannelParent* aBgParent) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aChannelParent); MOZ_ASSERT(aBgParent);
--- a/netwerk/protocol/http/BackgroundChannelRegistrar.h +++ b/netwerk/protocol/http/BackgroundChannelRegistrar.h @@ -4,16 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_net_BackgroundChannelRegistrar_h__ #define mozilla_net_BackgroundChannelRegistrar_h__ #include "nsIBackgroundChannelRegistrar.h" #include "nsRefPtrHashtable.h" +#include "mozilla/AlreadyAddRefed.h" namespace mozilla { namespace net { class HttpBackgroundChannelParent; class HttpChannelParent; class BackgroundChannelRegistrar final : public nsIBackgroundChannelRegistrar @@ -23,16 +24,21 @@ class BackgroundChannelRegistrar final : typedef nsRefPtrHashtable<nsUint64HashKey, HttpBackgroundChannelParent> BackgroundChannelHashtable; public: NS_DECL_ISUPPORTS NS_DECL_NSIBACKGROUNDCHANNELREGISTRAR explicit BackgroundChannelRegistrar(); + // Singleton accessor + static already_AddRefed<nsIBackgroundChannelRegistrar> GetOrCreate(); + + static void Shutdown(); + private: virtual ~BackgroundChannelRegistrar(); // A helper function for BackgroundChannelRegistrar itself to callback // HttpChannelParent and HttpBackgroundChannelParent when both objects are // ready. aChannelParent and aBgParent is the pair of HttpChannelParent and // HttpBackgroundChannelParent that should be linked together. void NotifyChannelLinked(HttpChannelParent* aChannelParent,
--- a/netwerk/protocol/http/Http2Compression.cpp +++ b/netwerk/protocol/http/Http2Compression.cpp @@ -1091,17 +1091,16 @@ nsresult Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput, const nsACString &method, const nsACString &path, const nsACString &host, const nsACString &scheme, bool connectForm, nsACString &output) { mSetInitialMaxBufferSizeAllowed = false; mOutput = &output; output.Truncate(); - output.SetCapacity(1024); mParsedContentLength = -1; // first thing's first - context size updates (if necessary) if (mBufferSizeChangeWaiting) { if (mLowestBufferSizeWaiting < mMaxBufferSetting) { EncodeTableSizeChange(mLowestBufferSizeWaiting); } EncodeTableSizeChange(mMaxBufferSetting);
--- a/netwerk/protocol/http/Http2Stream.cpp +++ b/netwerk/protocol/http/Http2Stream.cpp @@ -551,17 +551,17 @@ Http2Stream::GenerateOpen() // case. LOG3(("Stream assigned out of range ID: 0x%X", mStreamID)); return NS_ERROR_UNEXPECTED; } // Now we need to convert the flat http headers into a set // of HTTP/2 headers by writing to mTxInlineFrame{sz} - nsCString compressedData; + nsAutoCStringN<1025> compressedData; nsAutoCString authorityHeader; nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader); if (NS_FAILED(rv)) { MOZ_ASSERT(false); return rv; } nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp +++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp @@ -9,17 +9,17 @@ #include "HttpLog.h" #include "HttpBackgroundChannelParent.h" #include "HttpChannelParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Unused.h" -#include "nsIBackgroundChannelRegistrar.h" +#include "mozilla/net/BackgroundChannelRegistrar.h" #include "nsNetCID.h" #include "nsQueryObject.h" #include "nsThreadUtils.h" using mozilla::dom::ContentParent; using mozilla::ipc::AssertIsInMainProcess; using mozilla::ipc::AssertIsOnBackgroundThread; using mozilla::ipc::BackgroundParent; @@ -49,17 +49,17 @@ public: NS_IMETHOD Run() override { LOG(("HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p channelId=%" PRIu64 "]\n", mActor.get(), mChannelId)); AssertIsInMainProcess(); MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIBackgroundChannelRegistrar> registrar = - do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID); + BackgroundChannelRegistrar::GetOrCreate(); MOZ_ASSERT(registrar); registrar->LinkBackgroundChannel(mChannelId, mActor); return NS_OK; } private: RefPtr<HttpBackgroundChannelParent> mActor;
--- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -21,17 +21,17 @@ #include "mozilla/Unused.h" #include "HttpBackgroundChannelParent.h" #include "HttpChannelParentListener.h" #include "nsHttpHandler.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsISupportsPriority.h" #include "nsIAuthPromptProvider.h" -#include "nsIBackgroundChannelRegistrar.h" +#include "mozilla/net/BackgroundChannelRegistrar.h" #include "nsSerializationHelper.h" #include "nsISerializable.h" #include "nsIApplicationCacheService.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/URIUtils.h" #include "SerializedLoadContext.h" #include "nsIAuthInformation.h" #include "nsIAuthPromptCallback.h" @@ -257,17 +257,17 @@ HttpChannelParent::CleanupBackgroundChan if (!mChannel) { return; } // This HttpChannelParent might still have a reference from // BackgroundChannelRegistrar. nsCOMPtr<nsIBackgroundChannelRegistrar> registrar = - do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID); + BackgroundChannelRegistrar::GetOrCreate(); MOZ_ASSERT(registrar); registrar->DeleteChannel(mChannel->ChannelId()); // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure // is still on going. we need to abort AsyncOpen with failure to destroy // PHttpChannel actor. if (mAsyncOpenBarrier) { @@ -724,17 +724,17 @@ already_AddRefed<GenericPromise> HttpChannelParent::WaitForBgParent() { LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this)); MOZ_ASSERT(!mBgParent); MOZ_ASSERT(mChannel); nsCOMPtr<nsIBackgroundChannelRegistrar> registrar = - do_GetService(NS_BACKGROUNDCHANNELREGISTRAR_CONTRACTID); + BackgroundChannelRegistrar::GetOrCreate(); MOZ_ASSERT(registrar); registrar->LinkHttpChannel(mChannel->ChannelId(), this); if (mBgParent) { RefPtr<GenericPromise> promise = mPromise.Ensure(__func__); // resolve promise immediatedly if bg channel is ready. mPromise.Resolve(true, __func__); return promise.forget(); @@ -1650,20 +1650,16 @@ HttpChannelParent::OnDataAvailable(nsIRe if (httpChannelImpl && httpChannelImpl->IsReadingFromCache()) { transportStatus = NS_NET_STATUS_READING; } static uint32_t const kCopyChunkSize = 128 * 1024; uint32_t toRead = std::min<uint32_t>(aCount, kCopyChunkSize); nsCString data; - if (!data.SetCapacity(toRead, fallible)) { - LOG((" out of memory!")); - return NS_ERROR_OUT_OF_MEMORY; - } int32_t count = static_cast<int32_t>(aCount); while (aCount) { nsresult rv = NS_ReadInputStreamToString(aInputStream, data, toRead); if (NS_FAILED(rv)) { return rv; }
--- a/netwerk/protocol/http/moz.build +++ b/netwerk/protocol/http/moz.build @@ -34,16 +34,17 @@ EXPORTS += [ 'nsHttpHeaderArray.h', 'nsHttpRequestHead.h', 'nsHttpResponseHead.h', ] EXPORTS.mozilla.net += [ 'AltDataOutputStreamChild.h', 'AltDataOutputStreamParent.h', + 'BackgroundChannelRegistrar.h', 'HttpAuthUtils.h', 'HttpBackgroundChannelChild.h', 'HttpBackgroundChannelParent.h', 'HttpBaseChannel.h', 'HttpChannelChild.h', 'HttpChannelParent.h', 'HttpInfo.h', 'nsServerTiming.h',
--- a/netwerk/protocol/http/nsHttpDigestAuth.cpp +++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp @@ -410,17 +410,17 @@ nsHttpDigestAuth::CalculateResponse(cons len += 8; // length of "auth-int" else len += 4; // length of "auth" } nsAutoCString contents; contents.SetCapacity(len); - contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH); + contents.Append(ha1_digest, EXPANDED_DIGEST_LENGTH); contents.Append(':'); contents.Append(nonce); contents.Append(':'); if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { contents.Append(nonce_count, NONCE_COUNT_LENGTH); contents.Append(':'); contents.Append(cnonce); @@ -474,19 +474,19 @@ nsHttpDigestAuth::CalculateHA1(const nsC int16_t len = username.Length() + password.Length() + realm.Length() + 2; if (algorithm & ALGO_MD5_SESS) { int16_t exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2; if (exlen > len) len = exlen; } nsAutoCString contents; - contents.SetCapacity(len + 1); + contents.SetCapacity(len); - contents.Assign(username); + contents.Append(username); contents.Append(':'); contents.Append(realm); contents.Append(':'); contents.Append(password); nsresult rv; rv = MD5Hash(contents.get(), contents.Length()); if (NS_FAILED(rv))
--- a/netwerk/streamconv/converters/nsDirIndexParser.cpp +++ b/netwerk/streamconv/converters/nsDirIndexParser.cpp @@ -138,17 +138,16 @@ nsDirIndexParser::ParseFormat(const char if (! *aFormatStr) break; nsAutoCString name; int32_t len = 0; while (aFormatStr[len] && !nsCRT::IsAsciiSpace(char16_t(aFormatStr[len]))) ++len; - name.SetCapacity(len + 1); name.Append(aFormatStr, len); aFormatStr += len; // Okay, we're gonna monkey with the nsStr. Bold! name.SetLength(nsUnescapeCount(name.BeginWriting())); // All tokens are case-insensitive - http://www.mozilla.org/projects/netlib/dirindexformat.html if (name.LowerCaseEqualsLiteral("description"))
new file mode 100644 --- /dev/null +++ b/python/mozbuild/mozbuild/nodeutil.py @@ -0,0 +1,134 @@ +# 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 absolute_import + +import os +import subprocess +import platform +from mozboot.util import get_state_dir +import which + +from distutils.version import ( + StrictVersion, +) + +NODE_MIN_VERSION = StrictVersion("8.11.0") +NPM_MIN_VERSION = StrictVersion("5.6.0") + + +def find_node_paths(): + """ Determines the possible paths for node executables. + + Returns a list of paths, which includes the build state directory. + """ + # Also add in the location to which `mach bootstrap` or + # `mach artifact toolchain` installs clang. + mozbuild_state_dir, _ = get_state_dir() + + if platform.system() == "Windows": + mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node') + else: + mozbuild_node_path = os.path.join(mozbuild_state_dir, 'node', 'bin') + + # We still fallback to the PATH, since on OSes that don't have toolchain + # artifacts available to download, Node may be coming from $PATH. + paths = [mozbuild_node_path] + os.environ.get('PATH').split(os.pathsep) + + if platform.system() == "Windows": + paths += [ + "%s\\nodejs" % os.environ.get("SystemDrive"), + os.path.join(os.environ.get("ProgramFiles"), "nodejs"), + os.path.join(os.environ.get("PROGRAMW6432"), "nodejs"), + os.path.join(os.environ.get("PROGRAMFILES"), "nodejs") + ] + + return paths + + +def check_executable_version(exe): + """Determine the version of a Node executable by invoking it. + + May raise ``subprocess.CalledProcessError`` or ``ValueError`` on failure. + """ + out = subprocess.check_output([exe, "--version"]).lstrip('v').rstrip() + return StrictVersion(out) + + +def simple_which(filename, path=None): + # Note: On windows, npm uses ".cmd" + exts = [".cmd", ".exe", ""] if platform.system() == "Windows" else [""] + + for ext in exts: + try: + return which.which(filename + ext, path) + except which.WhichError: + pass + + # If we got this far, we didn't find it with any of the extensions, so + # just return. + return None + + +def find_node_executable(nodejs_exe=os.environ.get('NODEJS'), min_version=NODE_MIN_VERSION): + """Find a Node executable from the mozbuild directory. + + Returns a tuple containing the the path to an executable binary and a + version tuple. Both tuple entries will be None if a Node executable + could not be resolved. + """ + if nodejs_exe: + try: + version = check_executable_version(nodejs_exe) + except (subprocess.CalledProcessError, ValueError): + return None, None + + if version >= min_version: + return nodejs_exe, version.version + + return None, None + + # "nodejs" is first in the tuple on the assumption that it's only likely to + # exist on systems (probably linux distros) where there is a program in the path + # called "node" that does something else. + return find_executable(['nodejs', 'node'], min_version) + + +def find_npm_executable(min_version=NPM_MIN_VERSION): + """Find a Node executable from the mozbuild directory. + + Returns a tuple containing the the path to an executable binary and a + version tuple. Both tuple entries will be None if a Node executable + could not be resolved. + """ + return find_executable(["npm"], min_version) + + +def find_executable(names, min_version): + paths = find_node_paths() + + found_exe = None + for name in names: + try: + exe = simple_which(name, paths) + except which.WhichError: + continue + + if not exe: + continue + + if not found_exe: + found_exe = exe + + # We always verify we can invoke the executable and its version is + # sane. + try: + version = check_executable_version(exe) + except (subprocess.CalledProcessError, ValueError): + continue + + if version >= min_version: + return exe, version.version + + return found_exe, None
--- a/python/mozlint/mozlint/parser.py +++ b/python/mozlint/mozlint/parser.py @@ -48,16 +48,19 @@ class Parser(object): if not isinstance(linter[attr], list) or \ not all(isinstance(a, basestring) for a in linter[attr]): raise LinterParseError(relpath, "The {} directive must be a " "list of strings!".format(attr)) invalid_paths = set() for path in linter[attr]: if '*' in path: + if attr == 'include': + raise LinterParseError(relpath, "Paths in the include directive cannot " + "contain globs:\n {}".format(path)) continue abspath = path if not os.path.isabs(abspath): abspath = os.path.join(self.root, path) if not os.path.exists(abspath): invalid_paths.add(' ' + path) @@ -96,10 +99,11 @@ class Parser(object): raise LinterParseError(path, "No lint definitions found!") linters = [] for name, linter in config.iteritems(): linter['name'] = name linter['path'] = path self._validate(linter) linter.setdefault('support-files', []).append(path) + linter.setdefault('include', ['.']) linters.append(linter) return linters
--- a/python/mozlint/mozlint/pathutils.py +++ b/python/mozlint/mozlint/pathutils.py @@ -10,25 +10,22 @@ from mozpack import path as mozpath from mozpack.files import FileFinder class FilterPath(object): """Helper class to make comparing and matching file paths easier.""" def __init__(self, path, exclude=None): self.path = os.path.normpath(path) self._finder = None - self.exclude = exclude @property def finder(self): if self._finder: return self._finder - self._finder = FileFinder( - mozpath.normsep(self.path), - ignore=[mozpath.normsep(e) for e in self.exclude]) + self._finder = FileFinder(mozpath.normsep(self.path)) return self._finder @property def ext(self): return os.path.splitext(self.path)[1].strip('.') @property def exists(self): @@ -89,41 +86,39 @@ def filterpaths(paths, linter, **lintarg if not lintargs.get('use_filters', True) or (not include and not exclude): return paths def normalize(path): if '*' not in path and not os.path.isabs(path): path = os.path.join(root, path) return FilterPath(path) + # Includes are always paths and should always exist. include = map(normalize, include) - exclude = map(normalize, exclude) - # Paths with and without globs will be handled separately, + # Exclude paths with and without globs will be handled separately, # pull them apart now. - includepaths = [p for p in include if p.exists] + exclude = map(normalize, exclude) excludepaths = [p for p in exclude if p.exists] - - includeglobs = [p for p in include if not p.exists] - excludeglobs = [p for p in exclude if not p.exists] + excludeglobs = [p.path for p in exclude if not p.exists] extensions = linter.get('extensions') keep = set() discard = set() for path in map(FilterPath, paths): # Exclude bad file extensions if extensions and path.isfile and path.ext not in extensions: continue if path.match(excludeglobs): continue # First handle include/exclude directives # that exist (i.e don't have globs) - for inc in includepaths: + for inc in include: # Only excludes that are subdirectories of the include # path matter. excs = [e for e in excludepaths if inc.contains(e)] if path.contains(inc): # If specified path is an ancestor of include path, # then lint the include path. keep.add(inc) @@ -136,35 +131,21 @@ def filterpaths(paths, linter, **lintarg elif inc.contains(path): # If the include path is an ancestor of the specified # path, then add the specified path only if there are # no exclude paths in-between them. if not any(e.contains(path) for e in excs): keep.add(path) - # Next handle include/exclude directives that - # contain globs. - if path.isfile: - # If the specified path is a file it must be both - # matched by an include directive and not matched - # by an exclude directive. - if not path.match(includeglobs) or any(e.contains(path) for e in excludepaths): - continue - - keep.add(path) - elif path.isdir: - # If the specified path is a directory, use a - # FileFinder to resolve all relevant globs. - path.exclude = [os.path.relpath(e.path, root) for e in exclude] - for pattern in includeglobs: - for p, f in path.finder.find(pattern.path): - if extensions and os.path.splitext(p)[1][1:] not in extensions: - continue - keep.add(path.join(p)) + # Next expand excludes with globs in them so we can add them to + # the set of files to discard. + for pattern in excludeglobs: + for p, f in path.finder.find(pattern): + discard.add(path.join(p)) # Only pass paths we couldn't exclude here to the underlying linter lintargs['exclude'] = [f.path for f in discard] return [f.path for f in keep] def findobject(path): """
new file mode 100644 --- /dev/null +++ b/python/mozlint/test/linters/invalid_include_with_glob.yml @@ -0,0 +1,6 @@ +--- +BadIncludeLinterWithGlob: + description: Has an invalid include directive. + include: ['**/*.js'] + type: string + payload: foobar
--- a/python/mozlint/test/linters/raises.yml +++ b/python/mozlint/test/linters/raises.yml @@ -1,5 +1,6 @@ --- RaisesLinter: description: Raises an exception + include: ['.'] type: external payload: external:raises
--- a/python/mozlint/test/linters/regex.yml +++ b/python/mozlint/test/linters/regex.yml @@ -1,11 +1,10 @@ --- RegexLinter: description: >- Make sure the string foobar never appears in a js variable file because it is bad. rule: no-foobar - include: - - '**/*.js' - - '**/*.jsm' + include: ['.'] + extensions: ['js', '.jsm'] type: regex payload: foobar
--- a/python/mozlint/test/linters/string.yml +++ b/python/mozlint/test/linters/string.yml @@ -1,12 +1,9 @@ --- StringLinter: description: >- Make sure the string foobar never appears in browser js files because it is bad rule: no-foobar - include: - - '**/*.js' - - '**/*.jsm' + extensions: ['.js', 'jsm'] type: string - extensions: ['.js', 'jsm'] payload: foobar
--- a/python/mozlint/test/linters/warning.yml +++ b/python/mozlint/test/linters/warning.yml @@ -1,13 +1,11 @@ --- WarningLinter: description: >- Make sure the string foobar never appears in browser js files because it is bad, but not too bad (just a warning) rule: no-foobar level: warning - include: - - '**/*.js' - - '**/*.jsm' + include: ['.'] type: string extensions: ['.js', 'jsm'] payload: foobar
--- a/python/mozlint/test/test_parser.py +++ b/python/mozlint/test/test_parser.py @@ -35,23 +35,26 @@ def test_parse_valid_linter(parse): lintobj = lintobj[0] assert isinstance(lintobj, dict) assert 'name' in lintobj assert 'description' in lintobj assert 'type' in lintobj assert 'payload' in lintobj assert 'extensions' in lintobj + assert 'include' in lintobj + assert lintobj['include'] == ['.'] assert set(lintobj['extensions']) == set(['js', 'jsm']) @pytest.mark.parametrize('linter', [ 'invalid_type.yml', 'invalid_extension.ym', 'invalid_include.yml', + 'invalid_include_with_glob.yml', 'invalid_exclude.yml', 'invalid_support_files.yml', 'missing_attrs.yml', 'missing_definition.yml', 'non_existing_include.yml', 'non_existing_exclude.yml', 'non_existing_support_files.yml', ])
--- a/python/mozlint/test/test_pathutils.py +++ b/python/mozlint/test/test_pathutils.py @@ -52,52 +52,49 @@ def test_no_filter(filterpaths): 'exclude': ['**/*.py'], 'use_filters': False, } paths = filterpaths(**args) assert_paths(paths, args['paths']) -def test_extensions(filterpaths): - args = { - 'paths': ['a.py', 'a.js', 'subdir2'], - 'include': ['**'], - 'exclude': [], - 'extensions': ['py'], - } - - paths = filterpaths(**args) - assert_paths(paths, ['a.py', 'subdir2/c.py']) - - TEST_CASES = ( { 'paths': ['a.js', 'subdir1/subdir3/d.js'], 'include': ['.'], 'exclude': ['subdir1'], 'expected': ['a.js'], }, { 'paths': ['a.js', 'subdir1/subdir3/d.js'], 'include': ['subdir1/subdir3'], 'exclude': ['subdir1'], 'expected': ['subdir1/subdir3/d.js'], }, { 'paths': ['.'], - 'include': ['**/*.py'], + 'include': ['.'], 'exclude': ['**/c.py', 'subdir1/subdir3'], + 'extensions': ['py'], + 'expected': ['.'], + }, + { + 'paths': ['a.py', 'a.js', 'subdir1/b.py', 'subdir2/c.py', 'subdir1/subdir3/d.py'], + 'include': ['.'], + 'exclude': ['**/c.py', 'subdir1/subdir3'], + 'extensions': ['py'], 'expected': ['a.py', 'subdir1/b.py'], }, { - 'paths': ['a.py', 'a.js', 'subdir1/b.py', 'subdir2/c.py', 'subdir1/subdir3/d.py'], - 'include': ['**/*.py'], - 'exclude': ['**/c.py', 'subdir1/subdir3'], - 'expected': ['a.py', 'subdir1/b.py'], + 'paths': ['a.py', 'a.js', 'subdir2'], + 'include': ['.'], + 'exclude': [], + 'extensions': ['py'], + 'expected': ['a.py', 'subdir2'], }, ) @pytest.mark.parametrize('test', TEST_CASES) def test_filterpaths(filterpaths, test): expected = test.pop('expected')
--- a/python/mozlint/test/test_roller.py +++ b/python/mozlint/test/test_roller.py @@ -56,20 +56,17 @@ def test_roll_from_subdir(lint, linters) result = lint.roll('no_foobar.js') assert len(result.issues) == 0 assert len(result.failed) == 0 assert result.returncode == 0 # Path relative to root doesn't work result = lint.roll(os.path.join('files', 'no_foobar.js')) assert len(result.issues) == 0 - # TODO Only the external linter is failing, the other two - # should be failing as well. Not using xfail so we don't - # lose coverage from the rest of the test. - assert len(result.failed) == 1 + assert len(result.failed) == 3 assert result.returncode == 1 # Paths from vcs are always joined to root instead of cwd lint.mock_vcs([os.path.join('files', 'no_foobar.js')]) result = lint.roll(outgoing=True) assert len(result.issues) == 0 assert len(result.failed) == 0 assert result.returncode == 0
--- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -498,51 +498,38 @@ ShowProtectedAuthPrompt(PK11SlotInfo* sl // Get protected auth dialogs nsCOMPtr<nsITokenDialogs> dialogs; nsresult nsrv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsITokenDialogs), NS_TOKENDIALOGS_CONTRACTID); if (NS_SUCCEEDED(nsrv)) { - nsProtectedAuthThread* protectedAuthRunnable = new nsProtectedAuthThread(); - if (protectedAuthRunnable) - { - NS_ADDREF(protectedAuthRunnable); - - protectedAuthRunnable->SetParams(slot); + RefPtr<nsProtectedAuthThread> protectedAuthRunnable = new nsProtectedAuthThread(); + protectedAuthRunnable->SetParams(slot); - nsCOMPtr<nsIProtectedAuthThread> runnable = do_QueryInterface(protectedAuthRunnable); - if (runnable) - { - nsrv = dialogs->DisplayProtectedAuth(ir, runnable); + nsrv = dialogs->DisplayProtectedAuth(ir, protectedAuthRunnable); - // We call join on the thread, - // so we can be sure that no simultaneous access will happen. - protectedAuthRunnable->Join(); + // We call join on the thread, + // so we can be sure that no simultaneous access will happen. + protectedAuthRunnable->Join(); - if (NS_SUCCEEDED(nsrv)) - { - SECStatus rv = protectedAuthRunnable->GetResult(); - switch (rv) - { - case SECSuccess: - protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED)); - break; - case SECWouldBlock: - protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY)); - break; - default: - protAuthRetVal = nullptr; - break; - } - } + if (NS_SUCCEEDED(nsrv)) { + SECStatus rv = protectedAuthRunnable->GetResult(); + switch (rv) { + case SECSuccess: + protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED)); + break; + case SECWouldBlock: + protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY)); + break; + default: + protAuthRetVal = nullptr; + break; } - - NS_RELEASE(protectedAuthRunnable); } } return protAuthRetVal; } class PK11PasswordPromptRunnable : public SyncRunnableBase {
--- a/taskcluster/ci/build/android-stuff.yml +++ b/taskcluster/ci/build/android-stuff.yml @@ -78,18 +78,21 @@ android-test-ccov/opt: custom-build-variant-cfg: android-test-ccov tooltool-downloads: internal toolchains: - android-gradle-dependencies - android-sdk-linux - linux64-node - linux64-grcov fetches: - toolchain-linux64-grcov: - - grcov.tar.xz + # We use a fetch instead of toolchains here, because mozharness expects + # it there (since the code is shared with tests that don't support + # `toolchains`). + toolchain: + - linux64-grcov android-lint/opt: description: "Android lint" index: product: mobile job-name: android-lint treeherder: platform: android-4-0-armv7-api16/opt
--- a/taskcluster/ci/test/kind.yml +++ b/taskcluster/ci/test/kind.yml @@ -3,17 +3,16 @@ loader: taskgraph.loader.test:loader kind-dependencies: - build - build-signing - fetch - toolchain transforms: - taskgraph.transforms.tests:transforms - - taskgraph.transforms.use_toolchains:transforms - taskgraph.transforms.job:transforms - taskgraph.transforms.coalesce:transforms - taskgraph.transforms.task:transforms # Each stanza in a file pointed to by 'jobs-from' describes a particular test # suite or sub-suite. These are processed through the transformations described # above to produce a bunch of tasks. See the schema in # `taskcluster/taskgraph/transforms/tests.py` for a description of the fields
--- a/taskcluster/docker/recipes/install-node.sh +++ b/taskcluster/docker/recipes/install-node.sh @@ -1,12 +1,15 @@ #!/bin/bash # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # This script installs Node v8. +# XXX For now, this should match the version installed in +# taskcluster/scripts/misc/repack-node.sh. Later we'll get the ESLint builder +# to use the linux64-node toolchain directly. -wget --progress=dot:mega https://nodejs.org/dist/v8.9.4/node-v8.9.4-linux-x64.tar.gz -echo '21fb4690e349f82d708ae766def01d7fec1b085ce1f5ab30d9bda8ee126ca8fc node-v8.9.4-linux-x64.tar.gz' | sha256sum -c -tar -C /usr/local -xz --strip-components 1 < node-v8.9.4-linux-x64.tar.gz +wget --progress=dot:mega https://nodejs.org/dist/v8.11.3/node-v8.11.3-linux-x64.tar.xz +echo '08e2fcfea66746bd966ea3a89f26851f1238d96f86c33eaf6274f67fce58421a node-v8.11.3-linux-x64.tar.xz' | sha256sum -c +tar -C /usr/local -xJ --strip-components 1 < node-v8.11.3-linux-x64.tar.xz node -v # verify npm -v
--- a/taskcluster/taskgraph/transforms/job/__init__.py +++ b/taskcluster/taskgraph/transforms/job/__init__.py @@ -145,24 +145,24 @@ def get_attribute(dict, key, attributes, is a corresponding value, set `key` in `dict` to that value.''' value = attributes.get(attribute_name) if value: dict[key] = value @transforms.add def use_fetches(config, jobs): - all_fetches = {} + artifact_names = {} for task in config.kind_dependencies_tasks: - if task.kind != 'fetch': - continue - - name = task.label.replace('%s-' % task.kind, '') - get_attribute(all_fetches, name, task.attributes, 'fetch-artifact') + if task.kind in ('fetch', 'toolchain'): + get_attribute( + artifact_names, task.label, task.attributes, + '{kind}-artifact'.format(kind=task.kind), + ) for job in jobs: fetches = job.pop('fetches', None) if not fetches: yield job continue # Hack added for `mach artifact toolchain` to support reading toolchain @@ -170,35 +170,35 @@ def use_fetches(config, jobs): if 'fetch' in fetches and config.params.get('ignore_fetches'): fetches['fetch'][:] = [] job_fetches = [] name = job.get('name', job.get('label')) dependencies = job.setdefault('dependencies', {}) prefix = get_artifact_prefix(job) for kind, artifacts in fetches.items(): - if kind == 'fetch': - for fetch in artifacts: - if fetch not in all_fetches: + if kind in ('fetch', 'toolchain'): + for fetch_name in artifacts: + label = '{kind}-{name}'.format(kind=kind, name=fetch_name) + if label not in artifact_names: raise Exception('Missing fetch job for {kind}-{name}: {fetch}'.format( - kind=config.kind, name=name, fetch=fetch)) + kind=config.kind, name=name, fetch=fetch_name)) - path = all_fetches[fetch] + path = artifact_names[label] if not path.startswith('public/'): - raise Exception('Non-public artifacts not supported for {kind}-{name}: ' - '{fetch}'.format(kind=config.kind, name=name, fetch=fetch)) + raise Exception( + 'Non-public artifacts not supported for {kind}-{name}: ' + '{fetch}'.format(kind=config.kind, name=name, fetch=fetch_name)) - dep = 'fetch-{}'.format(fetch) - dependencies[dep] = dep + dependencies[label] = label job_fetches.append({ 'artifact': path, - 'task': '<{dep}>'.format(dep=dep), + 'task': '<{label}>'.format(label=label), 'extract': True, }) - else: if kind not in dependencies: raise Exception("{name} can't fetch {kind} artifacts because " "it has no {kind} dependencies!".format(name=name, kind=kind)) for artifact in artifacts: if isinstance(artifact, basestring): path = artifact
--- a/taskcluster/taskgraph/transforms/tests.py +++ b/taskcluster/taskgraph/transforms/tests.py @@ -393,19 +393,16 @@ test_description_schema = Schema({ # If None or not specified, a transform sets the target based on OS: # target.dmg (Mac), target.apk (Android), target.tar.bz2 (Linux), # or target.zip (Windows). Optional('target'): optionally_keyed_by( 'test-platform', Any(basestring, None), ), - # A list of artifacts to install from 'toolchain' tasks. - Optional('toolchains'): [basestring], - # A list of artifacts to install from 'fetch' tasks. Optional('fetches'): { basestring: [basestring], }, }, required=True) @transforms.add @@ -748,20 +745,17 @@ def enable_code_coverage(config, tests): for test in tests: if 'ccov' in test['build-platform']: # Do not run tests on fuzzing or opt build if 'opt' in test['build-platform'] or 'fuzzing' in test['build-platform']: test['run-on-projects'] = [] continue # Skip this transform for android code coverage builds. if 'android' in test['build-platform']: - test.setdefault('fetches', {})\ - .setdefault('toolchain-linux64-grcov', [])\ - .append('grcov.tar.xz') - test.setdefault('toolchains', []).append('linux64-grcov') + test.setdefault('fetches', {}).setdefault('toolchain', []).append('linux64-grcov') test['mozharness'].setdefault('extra-options', []).append('--java-code-coverage') yield test continue test['mozharness'].setdefault('extra-options', []).append('--code-coverage') test['instance-size'] = 'xlarge' # Ensure we always run on the projects defined by the build, unless the test # is try only or shouldn't run at all. if test['run-on-projects'] not in [[], ['try']]: @@ -772,24 +766,22 @@ def enable_code_coverage(config, tests): test.pop('schedules-component', None) test.pop('when', None) test['optimization'] = None # Add a toolchain and a fetch task for the grcov binary. if any(p in test['build-platform'] for p in ('linux', 'osx', 'win')): test.setdefault('fetches', {}) test['fetches'].setdefault('fetch', []) - test.setdefault('toolchains', []) + test['fetches'].setdefault('toolchain', []) if 'linux' in test['build-platform']: - test['fetches']['toolchain-linux64-grcov'] = ['grcov.tar.xz'] - test['toolchains'].append('linux64-grcov') + test['fetches']['toolchain'].append('linux64-grcov') elif 'osx' in test['build-platform']: - test['fetches']['toolchain-macosx64-grcov'] = ['grcov.tar.xz'] - test['toolchains'].append('macosx64-grcov') + test['fetches']['toolchain'].append('macosx64-grcov') elif 'win' in test['build-platform']: test['fetches']['fetch'].append('grcov-win-x86_64') if 'talos' in test['test-name']: test['max-run-time'] = 7200 if 'linux' in test['build-platform']: test['docker-image'] = {"in-tree": "desktop1604-test"} test['mozharness']['extra-options'].append('--add-option') @@ -1064,19 +1056,16 @@ def make_job_description(config, tests): jobdesc['description'] = test['description'] jobdesc['attributes'] = attributes jobdesc['dependencies'] = {'build': build_label} jobdesc['job-from'] = test['job-from'] if test.get('fetches'): jobdesc['fetches'] = test['fetches'] - if test.get('toolchains'): - jobdesc['toolchains'] = test.pop('toolchains') - if test['mozharness']['requires-signed-builds'] is True: jobdesc['dependencies']['build-signing'] = test['build-signing-label'] jobdesc['expires-after'] = test['expires-after'] jobdesc['routes'] = [] jobdesc['run-on-projects'] = test['run-on-projects'] jobdesc['scopes'] = [] jobdesc['tags'] = test.get('tags', {})
--- a/testing/mozbase/mozlog/mozlog/commandline.py +++ b/testing/mozbase/mozlog/mozlog/commandline.py @@ -55,16 +55,21 @@ def compact_wrapper(formatter, compact): def buffer_handler_wrapper(handler, buffer_limit): if buffer_limit == "UNLIMITED": buffer_limit = None else: buffer_limit = int(buffer_limit) return handlers.BufferHandler(handler, buffer_limit) +def screenshot_wrapper(formatter, enable_screenshot): + formatter.enable_screenshot = enable_screenshot + return formatter + + def valgrind_handler_wrapper(handler): return handlers.ValgrindHandler(handler) def default_formatter_options(log_type, overrides): formatter_option_defaults = { "raw": { "level": "debug" @@ -80,27 +85,33 @@ def default_formatter_options(log_type, return rv fmt_options = { # <option name>: (<wrapper function>, description, <applicable formatters>, action) # "action" is used by the commandline parser in use. 'verbose': (verbose_wrapper, "Enables verbose mode for the given formatter.", - ["mach"], "store_true"), + {"mach"}, "store_true"), 'compact': (compact_wrapper, "Enables compact mode for the given formatter.", - ["tbpl"], "store_true"), + {"tbpl"}, "store_true"), 'level': (level_filter_wrapper, "A least log level to subscribe to for the given formatter " "(debug, info, error, etc.)", - ["mach", "raw", "tbpl"], "store"), + {"mach", "raw", "tbpl"}, "store"), 'buffer': (buffer_handler_wrapper, "If specified, enables message buffering at the given buffer size limit.", ["mach", "tbpl"], "store"), + 'screenshot': (screenshot_wrapper, + "Enable logging reftest-analyzer compatible screenshot data.", + {"mach"}, "store_true"), + 'no-screenshot': (screenshot_wrapper, + "Disable logging reftest-analyzer compatible screenshot data.", + {"mach"}, "store_false"), } def log_file(name): if name == "-": return sys.stdout # ensure we have a correct dirpath by using realpath dirpath = os.path.dirname(os.path.realpath(name)) @@ -151,22 +162,27 @@ def add_logging_group(parser, include_fo opt_log_type = log_file group_add = group.add_argument for name, (cls, help_str) in six.iteritems(log_formatters): if name in include_formatters: group_add("--log-" + name, action="append", type=opt_log_type, help=help_str) - for optname, (cls, help_str, formatters_, action) in six.iteritems(fmt_options): - for fmt in formatters_: - # make sure fmt is in log_formatters and is accepted - if fmt in log_formatters and fmt in include_formatters: - group_add("--log-%s-%s" % (fmt, optname), action=action, - help=help_str, default=None) + for fmt in include_formatters: + for optname, (cls, help_str, formatters_, action) in six.iteritems(fmt_options): + if fmt not in formatters_: + continue + if optname.startswith("no-") and action == "store_false": + dest = optname.split("-", 1)[1] + else: + dest = optname + dest = dest.replace("-", "_") + group_add("--log-%s-%s" % (fmt, optname), action=action, + help=help_str, default=None, dest="log_%s_%s" % (fmt, dest)) def setup_handlers(logger, formatters, formatter_options, allow_unused_options=False): """ Add handlers to the given logger according to the formatters and options provided. :param logger: The logger configured by this function.
--- a/testing/mozbase/mozlog/mozlog/formatters/machformatter.py +++ b/testing/mozbase/mozlog/mozlog/formatters/machformatter.py @@ -6,46 +6,49 @@ from __future__ import absolute_import import time from mozterm import Terminal from . import base from .process import strstatus +from .tbplformatter import TbplFormatter from ..handlers import SummaryHandler import six from functools import reduce def format_seconds(total): """Format number of seconds to MM:SS.DD form.""" minutes, seconds = divmod(total, 60) return '%2d:%05.2f' % (minutes, seconds) class MachFormatter(base.BaseFormatter): def __init__(self, start_time=None, write_interval=False, write_times=True, terminal=None, disable_colors=False, summary_on_shutdown=False, - verbose=False, **kwargs): + verbose=False, enable_screenshot=False, **kwargs): super(MachFormatter, self).__init__(**kwargs) if start_time is None: start_time = time.time() start_time = int(start_time * 1000) self.start_time = start_time self.write_interval = write_interval self.write_times = write_times self.status_buffer = {} self.has_unexpected = {} self.last_time = None self.term = Terminal(disable_styling=disable_colors) self.verbose = verbose self._known_pids = set() + self.tbpl_formatter = None + self.enable_screenshot = enable_screenshot self.summary = SummaryHandler() self.summary_on_shutdown = summary_on_shutdown def __call__(self, data): self.summary(data) s = super(MachFormatter, self).__call__(data) @@ -178,16 +181,18 @@ class MachFormatter(base.BaseFormatter): if "expected" in data: parent_unexpected = True expected_str = ", expected %s" % data["expected"] else: parent_unexpected = False expected_str = "" + has_screenshots = "reftest_screenshots" in data.get("extra", {}) + test = self._get_test_id(data) # Reset the counts to 0 self.status_buffer[test] = {"count": 0, "unexpected": 0, "pass": 0} self.has_unexpected[test] = bool(subtests['unexpected']) if subtests["count"] != 0: rv = "Test %s%s. Subtests passed %i/%i. Unexpected %s" % ( @@ -210,17 +215,23 @@ class MachFormatter(base.BaseFormatter): rv += self._format_status(data['test'], d) if "expected" not in data and not bool(subtests['unexpected']): color = self.term.green else: color = self.term.red action = color(data['action'].upper()) - return "%s: %s" % (action, rv) + rv = "%s: %s" % (action, rv) + if has_screenshots and self.enable_screenshot: + if self.tbpl_formatter is None: + self.tbpl_formatter = TbplFormatter() + # Create TBPL-like output that can be pasted into the reftest analyser + rv = "\n".join((rv, self.tbpl_formatter.test_end(data))) + return rv def valgrind_error(self, data): rv = " " + data['primary'] + "\n" for line in data['secondary']: rv = rv + line + "\n" return rv
--- a/testing/mozbase/mozlog/mozlog/formatters/tbplformatter.py +++ b/testing/mozbase/mozlog/mozlog/formatters/tbplformatter.py @@ -208,52 +208,57 @@ class TbplFormatter(BaseFormatter): test_id = self.test_id(data["test"]) duration_msg = "" if test_id in self.test_start_times: start_time = self.test_start_times.pop(test_id) time = data["time"] - start_time duration_msg = "took %ims" % time + screenshot_msg = "" + extra = data.get("extra", {}) + if "reftest_screenshots" in extra: + screenshots = extra["reftest_screenshots"] + if len(screenshots) == 3: + screenshot_msg = ("\nREFTEST IMAGE 1 (TEST): data:image/png;base64,%s\n" + "REFTEST IMAGE 2 (REFERENCE): data:image/png;base64,%s") % ( + screenshots[0]["screenshot"], + screenshots[2]["screenshot"]) + elif len(screenshots) == 1: + screenshot_msg = ("\nREFTEST IMAGE: data:image/png;base64,%s" % + screenshots[0]["screenshot"]) + if "expected" in data: message = data.get("message", "") if not message: message = "expected %s" % data["expected"] if "stack" in data: message += "\n%s" % data["stack"] if message and message[-1] == "\n": message = message[:-1] - extra = data.get("extra", {}) - if "reftest_screenshots" in extra: - screenshots = extra["reftest_screenshots"] - if len(screenshots) == 3: - message += ("\nREFTEST IMAGE 1 (TEST): data:image/png;base64,%s\n" - "REFTEST IMAGE 2 (REFERENCE): data:image/png;base64,%s") % ( - screenshots[0]["screenshot"], - screenshots[2]["screenshot"]) - elif len(screenshots) == 1: - message += "\nREFTEST IMAGE: data:image/png;base64,%s" \ - % screenshots[0]["screenshot"] + message += screenshot_msg failure_line = "TEST-UNEXPECTED-%s | %s | %s\n" % ( data["status"], test_id, message) if data["expected"] not in ("PASS", "OK"): expected_msg = "expected %s | " % data["expected"] else: expected_msg = "" info_line = "TEST-INFO %s%s\n" % (expected_msg, duration_msg) return failure_line + info_line sections = ["TEST-%s" % data['status'], test_id] if duration_msg: sections.append(duration_msg) rv.append(' | '.join(sections) + '\n') + if screenshot_msg: + rv.append(screenshot_msg[1:]) return "".join(rv) def suite_end(self, data): start_time = self.suite_start_time time = int((data["time"] - start_time) / 1000) return "SUITE-END | took %is\n" % time
--- a/testing/mozharness/mozharness/mozilla/tooltool.py +++ b/testing/mozharness/mozharness/mozilla/tooltool.py @@ -13,16 +13,17 @@ TooltoolErrorList = PythonErrorList + [{ TOOLTOOL_SERVERS = [ 'https://tooltool.mozilla-releng.net/', ] _here = os.path.abspath(os.path.dirname(__file__)) _external_tools_path = os.path.normpath(os.path.join(_here, '..', '..', 'external_tools')) + class TooltoolMixin(object): """Mixin class for handling tooltool manifests. To use a tooltool server other than the Mozilla server, override config['tooltool_servers']. To specify a different authentication file than that used in releng automation,override config['tooltool_authentication_file']; set it to None to not pass any authentication information (OK for public files) """ @@ -86,16 +87,20 @@ class TooltoolMixin(object): else: cmd.extend(['fetch', '-m', manifest, '-o']) if cache: cmd.extend(['--cache-dir' if self.topsrcdir else '-c', cache]) toolchains = os.environ.get('MOZ_TOOLCHAINS') if toolchains: + if not self.topsrcdir: + raise Exception( + 'MOZ_TOOLCHAINS is not supported for tasks without ' + 'a source checkout.') cmd.extend(toolchains.split()) timeout = self.config.get('tooltool_timeout', 10 * 60) self.retry( self.run_command, args=(cmd, ), kwargs={'cwd': output_dir,
--- a/testing/talos/talos/gecko_profile.py +++ b/testing/talos/talos/gecko_profile.py @@ -135,18 +135,17 @@ class GeckoProfile(object): "prefetchMaxSymbolsPerLib": 3, # Default symbol lookup directories "defaultApp": "FIREFOX", "defaultOs": "WINDOWS", # Paths to .SYM files, expressed internally as a # mapping of app or platform names to directories # Note: App & OS names from requests are converted # to all-uppercase internally - "symbolPaths": self.symbol_paths, - "platformsRequiringSymbols": ["Windows", "Microsoft"] + "symbolPaths": self.symbol_paths }) if self.browser_config['symbols_path']: if mozfile.is_url(self.browser_config['symbols_path']): symbolicator.integrate_symbol_zip_from_url( self.browser_config['symbols_path'] ) elif os.path.isfile(self.browser_config['symbols_path']):
--- a/testing/talos/talos/profiler/symbolication.py +++ b/testing/talos/talos/profiler/symbolication.py @@ -134,20 +134,18 @@ class ProfileSymbolicator: if platform.system() == "Darwin": return OSXSymbolDumper() elif platform.system() == "Linux": return LinuxSymbolDumper() except SymbolError: return None def integrate_symbol_zip_from_url(self, symbol_zip_url): - if platform.system() not in self.options['platformsRequiringSymbols']\ - or self.have_integrated(symbol_zip_url): + if self.have_integrated(symbol_zip_url): return - LogMessage("Retrieving symbol zip from {symbol_zip_url}...".format( symbol_zip_url=symbol_zip_url)) try: io = urllib2.urlopen(symbol_zip_url, None, 30) with zipfile.ZipFile(cStringIO.StringIO(io.read())) as zf: self.integrate_symbol_zip(zf) self._create_file_if_not_exists(self._marker_file(symbol_zip_url)) except IOError:
--- a/testing/web-platform/mach_commands.py +++ b/testing/web-platform/mach_commands.py @@ -51,16 +51,19 @@ class WebPlatformTestsRunnerSetup(Mozbui kwargs["ca_cert_path"] = os.path.join(cert_root, "cacert.pem") if kwargs["host_key_path"] is None: kwargs["host_key_path"] = os.path.join(cert_root, "web-platform.test.key") if kwargs["host_cert_path"] is None: kwargs["host_cert_path"] = os.path.join(cert_root, "web-platform.test.pem") + if kwargs["log_mach_screenshot"] is None: + kwargs["log_mach_screenshot"] = True + kwargs["capture_stdio"] = True return kwargs def kwargs_firefox(self, kwargs): from wptrunner import wptcommandline kwargs = self.kwargs_common(kwargs)
--- a/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini +++ b/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini @@ -1,5 +1,5 @@ [event-global-extra.window.html] - prefs: [dom.webcomponents.shadowdom.enabled:true] + prefs: [dom.webcomponents.shadowdom.enabled:true, dom.window.event.enabled:true] [Listener from a different global] expected: FAIL
--- a/testing/web-platform/meta/dom/events/event-global.html.ini +++ b/testing/web-platform/meta/dom/events/event-global.html.ini @@ -1,2 +1,2 @@ [event-global.html] - prefs: [dom.webcomponents.shadowdom.enabled:true] + prefs: [dom.webcomponents.shadowdom.enabled:true, dom.window.event.enabled:true]
--- a/testing/web-platform/meta/dom/interfaces.html.ini +++ b/testing/web-platform/meta/dom/interfaces.html.ini @@ -169,16 +169,17 @@ [Range interface: existence and properties of interface prototype object] expected: FAIL [interfaces.html?include=Node] [interfaces.html?exclude=Node] + prefs: [dom.window.event.enabled:true] [Document interface: attribute origin] expected: FAIL [Document interface: new Document() must inherit property "origin" with the proper type] expected: FAIL [Document interface: xmlDoc must inherit property "origin" with the proper type]
--- a/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-remove-track.html.ini @@ -1,4 +1,5 @@ [track-remove-track.html] + prefs: [dom.window.event.enabled:true] [Tests that the 'removetrack' event is NOT fired for inband TextTrack on a failed load.] expected: FAIL
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp +++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp @@ -791,16 +791,22 @@ AntiTrackingCommon::IsFirstPartyStorageA "pretending our channel isn't a tracking channel")); return true; } if (CheckContentBlockingAllowList(aChannel)) { return true; } + // Not a tracker. + if (!aChannel->GetIsTrackingResource()) { + LOG(("Our channel isn't a tracking channel")); + return true; + } + nsIPrincipal* parentPrincipal = loadInfo->TopLevelStorageAreaPrincipal(); if (!parentPrincipal) { LOG(("No top-level storage area principal at hand")); // parentPrincipal can be null if the parent window is not the top-level // window. if (loadInfo->TopLevelPrincipal()) { LOG(("Parent window is the top-level window, bail out early")); @@ -810,22 +816,16 @@ AntiTrackingCommon::IsFirstPartyStorageA parentPrincipal = toplevelPrincipal; if (NS_WARN_IF(!parentPrincipal)) { LOG(("No triggering principal, this shouldn't be happening! Bail out early")); // Why we are here?!? return true; } } - // Not a tracker. - if (!aChannel->GetIsTrackingResource()) { - LOG(("Our channel isn't a tracking channel")); - return true; - } - // Let's see if we have to grant the access for this particular channel. nsCOMPtr<nsIURI> trackingURI; rv = aChannel->GetURI(getter_AddRefs(trackingURI)); if (NS_WARN_IF(NS_FAILED(rv))) { LOG(("Failed to get the channel URI")); return true; }
--- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -36,17 +36,16 @@ ChromeUtils.import("resource://gre/modul ChromeUtils.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { AddonManager: "resource://gre/modules/AddonManager.jsm", AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm", AddonSettings: "resource://gre/modules/addons/AddonSettings.jsm", AppConstants: "resource://gre/modules/AppConstants.jsm", AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm", - ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm", ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm", ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm", ExtensionStorageIDB: "resource://gre/modules/ExtensionStorageIDB.jsm", ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm", ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm", FileSource: "resource://gre/modules/L10nRegistry.jsm", L10nRegistry: "resource://gre/modules/L10nRegistry.jsm", Log: "resource://gre/modules/Log.jsm", @@ -107,21 +106,19 @@ const { XPCOMUtils.defineLazyGetter(this, "console", ExtensionCommon.getConsole); XPCOMUtils.defineLazyGetter(this, "LocaleData", () => ExtensionCommon.LocaleData); const {sharedData} = Services.ppmm; // The userContextID reserved for the extension storage (its purpose is ensuring that the IndexedDB -// storage used by the browser.storage.local API is not directly accessible from the extension code). -XPCOMUtils.defineLazyGetter(this, "WEBEXT_STORAGE_USER_CONTEXT_ID", () => { - return ContextualIdentityService.getDefaultPrivateIdentity( - "userContextIdInternal.webextStorageLocal").userContextId; -}); +// storage used by the browser.storage.local API is not directly accessible from the extension code, +// it is defined and reserved as "userContextIdInternal.webextStorageLocal" in ContextualIdentityService.jsm). +const WEBEXT_STORAGE_USER_CONTEXT_ID = -1 >>> 0; // The maximum time to wait for extension child shutdown blockers to complete. const CHILD_SHUTDOWN_TIMEOUT_MS = 8000; /** * Classify an individual permission from a webextension manifest * as a host/origin permission, an api permission, or a regular permission. * @@ -1829,25 +1826,16 @@ class Extension extends ExtensionData { // Select the storage.local backend if it is already known, // and start the data migration if needed. if (this.hasPermission("storage")) { if (!ExtensionStorageIDB.isBackendEnabled) { this.setSharedData("storageIDBBackend", false); } else if (ExtensionStorageIDB.isMigratedExtension(this)) { this.setSharedData("storageIDBBackend", true); this.setSharedData("storageIDBPrincipal", ExtensionStorageIDB.getStoragePrincipal(this)); - } else { - // If the extension has to migrate backend, ensure that the data migration - // starts once Firefox is idle after the extension has been started. - this.once("ready", () => ChromeUtils.idleDispatch(() => { - if (this.hasShutdown) { - return; - } - ExtensionStorageIDB.selectBackend({extension: this}); - })); } } // The "startup" Management event sent on the extension instance itself // is emitted just before the Management "startup" event, // and it is used to run code that needs to be executed before // any of the "startup" listeners. this.emit("startup", this);
--- a/toolkit/components/extensions/ExtensionStorageIDB.jsm +++ b/toolkit/components/extensions/ExtensionStorageIDB.jsm @@ -5,29 +5,26 @@ "use strict"; this.EXPORTED_SYMBOLS = ["ExtensionStorageIDB"]; ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/IndexedDB.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { - ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm", ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm", getTrimmedString: "resource://gre/modules/ExtensionTelemetry.jsm", Services: "resource://gre/modules/Services.jsm", OS: "resource://gre/modules/osfile.jsm", }); // The userContextID reserved for the extension storage (its purpose is ensuring that the IndexedDB -// storage used by the browser.storage.local API is not directly accessible from the extension code). -XPCOMUtils.defineLazyGetter(this, "WEBEXT_STORAGE_USER_CONTEXT_ID", () => { - return ContextualIdentityService.getDefaultPrivateIdentity( - "userContextIdInternal.webextStorageLocal").userContextId; -}); +// storage used by the browser.storage.local API is not directly accessible from the extension code, +// it is defined and reserved as "userContextIdInternal.webextStorageLocal" in ContextualIdentityService.jsm). +const WEBEXT_STORAGE_USER_CONTEXT_ID = -1 >>> 0; const IDB_NAME = "webExtensions-storage-local"; const IDB_DATA_STORENAME = "storage-local-data"; const IDB_VERSION = 1; const IDB_MIGRATE_RESULT_HISTOGRAM = "WEBEXT_STORAGE_LOCAL_IDB_MIGRATE_RESULT_COUNT"; // Whether or not the installed extensions should be migrated to the storage.local IndexedDB backend. const BACKEND_ENABLED_PREF = "extensions.webextensions.ExtensionStorageIDB.enabled";
--- a/toolkit/moz.configure +++ b/toolkit/moz.configure @@ -1062,19 +1062,18 @@ def launcher(value, target): if enabled: return True set_config('MOZ_LAUNCHER_PROCESS', launcher) set_define('MOZ_LAUNCHER_PROCESS', launcher) # Prio # ============================================================== -@depends(c_compiler, target) -def libprio(info, target): +@depends(c_compiler) +def libprio(info): if info: - # TODO - re-enable Windows when bug 1489691 is fixed. - # Note that we will probably never support MSVC however. - if info.type in ('msvc',) or target.os in ('WINNT',): + # MSVC is not supported by libprio. + if info.type in ('msvc',): return None return True set_config('MOZ_LIBPRIO', libprio)
--- a/tools/lint/docs/create.rst +++ b/tools/lint/docs/create.rst @@ -21,18 +21,17 @@ be python code alongside the definition, Here's a trivial example: no-eval.yml .. code-block:: EvalLinter: description: Ensures the string eval doesn't show up. - include: - - "**/*.js" + extensions: ['js'] type: string payload: eval Now ``no-eval.yml`` gets passed into :func:`LintRoller.read`. Linter Types ------------ @@ -70,18 +69,18 @@ this case the signature for lint functio Linter Definition ----------------- Each ``.yml`` file must have at least one linter defined in it. Here are the supported keys: * description - A brief description of the linter's purpose (required) * type - One of 'string', 'regex' or 'external' (required) * payload - The actual linting logic, depends on the type (required) -* include - A list of glob patterns that must be matched (optional) -* exclude - A list of glob patterns that must not be matched (optional) +* include - A list of file paths that will be considered (optional) +* exclude - A list of file paths or glob patterns that must not be matched (optional) * extensions - A list of file extensions to be considered (optional) * setup - A function that sets up external dependencies (optional) * support-files - A list of glob patterns matching configuration files (optional) In addition to the above, some ``.yml`` files correspond to a single lint rule. For these, the following additional keys may be specified: * message - A string to print on infraction (optional) @@ -160,18 +159,18 @@ let's call the file ``flake8_lint.py``: return results Now here is the linter definition that would call it: .. code-block:: yml flake8: description: Python linter - include: - - '**/*.py' + include: ['.'] + extensions: ['py'] type: external payload: py.flake8:lint support-files: - '**/.flake8' Notice the payload has two parts, delimited by ':'. The first is the module path, which ``mozlint`` will attempt to import. The second is the object path within that module (e.g, the name of a function to call). It is up to consumers @@ -195,18 +194,18 @@ Either way, to reduce the burden on user automated bootstrapping of all their dependencies. To help with this, ``mozlint`` allows linters to define a ``setup`` config, which has the same path object format as an external payload. For example: .. code-block:: yml flake8: description: Python linter - include: - - '**/*.py' + include: ['.'] + extensions: ['py'] type: external payload: py.flake8:lint setup: py.flake8:setup The setup function takes a single argument, the root of the repository being linted. In the case of ``flake8``, it might look like: .. code-block:: python
--- a/tools/lint/eslint/__init__.py +++ b/tools/lint/eslint/__init__.py @@ -5,16 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import json import os import signal import sys sys.path.append(os.path.join(os.path.dirname(__file__), "eslint")) import setup_helper +from mozbuild.nodeutil import find_node_executable from mozprocess import ProcessHandler from mozlint import result ESLINT_ERROR_MESSAGE = """ An error occurred running eslint. Please check the following error messages: @@ -47,29 +48,25 @@ def lint(paths, config, binary=None, fix module_path = setup_helper.get_project_root() # Valid binaries are: # - Any provided by the binary argument. # - Any pointed at by the ESLINT environmental variable. # - Those provided by |mach lint --setup|. if not binary: - binary = os.environ.get('ESLINT', None) - - if not binary: - binary = os.path.join(module_path, "node_modules", ".bin", "eslint") - if not os.path.isfile(binary): - binary = None + binary, _ = find_node_executable() if not binary: print(ESLINT_NOT_FOUND_MESSAGE) return 1 extra_args = lintargs.get('extra_args') or [] cmd_args = [binary, + os.path.join(module_path, "node_modules", "eslint", "bin", "eslint.js"), # Enable the HTML plugin. # We can't currently enable this in the global config file # because it has bad interactions with the SublimeText # ESLint plugin (bug 1229874). '--plugin', 'html', # This keeps ext as a single argument. '--ext', '[{}]'.format(','.join(config['extensions'])), '--format', 'json',
--- a/tools/lint/eslint/setup_helper.py +++ b/tools/lint/eslint/setup_helper.py @@ -9,49 +9,49 @@ import json import os import platform import re from mozfile.mozfile import remove as mozfileremove import subprocess import sys import shutil import tempfile -from distutils.version import LooseVersion +from distutils.version import LooseVersion, StrictVersion +from mozbuild.nodeutil import (find_node_executable, find_npm_executable, + NPM_MIN_VERSION, NODE_MIN_VERSION) sys.path.append(os.path.join( os.path.dirname(__file__), "..", "..", "..", "third_party", "python", "which")) -import which - -NODE_MIN_VERSION = "8.9.1" -NPM_MIN_VERSION = "5.5.1" NODE_MACHING_VERSION_NOT_FOUND_MESSAGE = """ -nodejs is out of date. You currently have node v%s but v%s is required. -Please update nodejs from https://nodejs.org and try again. +Could not find Node.js executable later than %s. + +Executing `mach bootstrap --no-system-changes` should +install a compatible version in ~/.mozbuild on most platforms. """.strip() NPM_MACHING_VERSION_NOT_FOUND_MESSAGE = """ -npm is out of date. You currently have npm v%s but v%s is required. -You can usually update npm with: +Could not find npm executable later than %s. -npm i -g npm +Executing `mach bootstrap --no-system-changes` should +install a compatible version in ~/.mozbuild on most platforms. """.strip() NODE_NOT_FOUND_MESSAGE = """ nodejs is either not installed or is installed to a non-standard path. -Please install nodejs from https://nodejs.org and try again. -Valid installation paths: +Executing `mach bootstrap --no-system-changes` should +install a compatible version in ~/.mozbuild on most platforms. """.strip() NPM_NOT_FOUND_MESSAGE = """ Node Package Manager (npm) is either not installed or installed to a -non-standard path. Please install npm from https://nodejs.org (it comes as an -option in the node installation) and try again. +non-standard path. -Valid installation paths: +Executing `mach bootstrap --no-system-changes` should +install a compatible version in ~/.mozbuild on most platforms. """.strip() VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$") CARET_VERSION_RANGE_RE = re.compile(r"^\^((\d+)\.\d+\.\d+)$") project_root = None @@ -83,28 +83,28 @@ def eslint_setup(should_clobber=False): node_modules_path = os.path.join(project_root, "node_modules") print("Clobbering node_modules...") if sys.platform.startswith('win') and have_winrm(): process = subprocess.Popen(['winrm', '-rf', node_modules_path]) process.wait() else: mozfileremove(node_modules_path) - npm_path, version = get_node_or_npm_path("npm") + npm_path, version = find_npm_executable() if not npm_path: return 1 extra_parameters = ["--loglevel=error"] package_lock_json_path = os.path.join(get_project_root(), "package-lock.json") package_lock_json_tmp_path = os.path.join(tempfile.gettempdir(), "package-lock.json.tmp") # If we have an npm version newer than 5.8.0, just use 'ci', as that's much # simpler and does exactly what we want. - npm_is_older_version = version < LooseVersion("5.8.0") + npm_is_older_version = version < StrictVersion("5.8.0").version if npm_is_older_version: cmd = [npm_path, "install"] shutil.copy2(package_lock_json_path, package_lock_json_tmp_path) else: cmd = [npm_path, "ci"] cmd.extend(extra_parameters) @@ -267,84 +267,16 @@ def get_possible_node_paths_win(): return list({ "%s\\nodejs" % os.environ.get("SystemDrive"), os.path.join(os.environ.get("ProgramFiles"), "nodejs"), os.path.join(os.environ.get("PROGRAMW6432"), "nodejs"), os.path.join(os.environ.get("PROGRAMFILES"), "nodejs") }) -def simple_which(filename, path=None): - exts = [".cmd", ".exe", ""] if platform.system() == "Windows" else [""] - - for ext in exts: - try: - return which.which(filename + ext, path) - except which.WhichError: - pass - - # If we got this far, we didn't find it with any of the extensions, so - # just return. - return None - - -def which_path(filename): - """ - Return the nodejs or npm path. - """ - # Look in the system path first. - path = simple_which(filename) - if path is not None: - return path - - if platform.system() == "Windows": - # If we didn't find it fallback to the non-system paths. - path = simple_which(filename, get_possible_node_paths_win()) - elif filename == "node": - path = simple_which("nodejs") - - return path - - -def get_node_or_npm_path(filename, minversion=None): - node_or_npm_path = which_path(filename) - - if not node_or_npm_path: - if filename in ('node', 'nodejs'): - print(NODE_NOT_FOUND_MESSAGE) - elif filename == "npm": - print(NPM_NOT_FOUND_MESSAGE) - - if platform.system() == "Windows": - app_paths = get_possible_node_paths_win() - - for p in app_paths: - print(" - %s" % p) - elif platform.system() == "Darwin": - print(" - /usr/local/bin/{}".format(filename)) - elif platform.system() == "Linux": - print(" - /usr/bin/{}".format(filename)) - - return None, None - - version_str = get_version(node_or_npm_path).lstrip('v') - - version = LooseVersion(version_str) - - if not minversion or version > minversion: - return node_or_npm_path, version - - if filename == "npm": - print(NPM_MACHING_VERSION_NOT_FOUND_MESSAGE % (version_str.strip(), minversion)) - else: - print(NODE_MACHING_VERSION_NOT_FOUND_MESSAGE % (version_str.strip(), minversion)) - - return None, None - - def get_version(path): try: version_str = subprocess.check_output([path, "--version"], stderr=subprocess.STDOUT) return version_str except (subprocess.CalledProcessError, OSError): return None @@ -388,23 +320,30 @@ def get_project_root(): return project_root def get_eslint_module_path(): return os.path.join(get_project_root(), "tools", "lint", "eslint") def check_node_executables_valid(): - # eslint requires at least node 6.9.1 - node_path = get_node_or_npm_path("node", LooseVersion(NODE_MIN_VERSION)) + node_path, version = find_node_executable() if not node_path: + print(NODE_NOT_FOUND_MESSAGE) + return False + if not version: + print(NODE_MACHING_VERSION_NOT_FOUND_MESSAGE % NODE_MIN_VERSION) return False - npm_path = get_node_or_npm_path("npm", LooseVersion(NPM_MIN_VERSION)) + npm_path, version = find_npm_executable() if not npm_path: + print(NPM_NOT_FOUND_MESSAGE) + return False + if not version: + print(NPM_MACHING_VERSION_NOT_FOUND_MESSAGE % NPM_MIN_VERSION) return False return True def have_winrm(): # `winrm -h` should print 'winrm version ...' and exit 1 try:
--- a/tools/lint/flake8.yml +++ b/tools/lint/flake8.yml @@ -35,16 +35,17 @@ flake8: - testing/marionette/puppeteer - testing/mochitest - testing/mozbase - testing/mozharness/configs - testing/mozharness/mozfile - testing/mozharness/mozharness/mozilla/l10n/locales.py - testing/mozharness/mozharness/mozilla/mar.py - testing/mozharness/mozharness/mozilla/testing/ + - testing/mozharness/mozharness/mozilla/tooltool.py - testing/mozharness/mozinfo - testing/mozharness/scripts - testing/raptor - testing/remotecppunittests.py - testing/runcppunittests.py - testing/talos/ - testing/xpcshell - toolkit/components/telemetry
--- a/tools/lint/test-disable.yml +++ b/tools/lint/test-disable.yml @@ -1,15 +1,14 @@ --- no-comment-disable: description: > "Use 'disabled=<reason>' to disable a test instead of a comment" - include: - - "**/*.ini" + include: ['.'] exclude: - "**/application.ini" - "**/l10n.ini" - dom/canvas/test/webgl-conf/mochitest-errata.ini - testing/mozbase/manifestparser/tests - testing/web-platform - third_party - xpcom/tests/unit/data
--- a/uriloader/exthandler/ExternalHelperAppChild.cpp +++ b/uriloader/exthandler/ExternalHelperAppChild.cpp @@ -42,19 +42,16 @@ ExternalHelperAppChild::OnDataAvailable( { if (NS_FAILED(mStatus)) return mStatus; static uint32_t const kCopyChunkSize = 128 * 1024; uint32_t toRead = std::min<uint32_t>(count, kCopyChunkSize); nsCString data; - if (NS_WARN_IF(!data.SetCapacity(toRead, fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } while (count) { nsresult rv = NS_ReadInputStreamToString(input, data, toRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (NS_WARN_IF(!SendOnDataAvailable(data, offset, toRead))) {
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp @@ -325,18 +325,17 @@ nsOSHelperAppService::GetTypeAndDescript getter_AddRefs(mimeTypes), cBuf, &netscapeFormat, &more); if (NS_FAILED(rv)) { return rv; } nsAutoString extensions; - nsString entry; - entry.SetCapacity(100); + nsAutoStringN<101> entry; nsAString::const_iterator majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd, descriptionStart, descriptionEnd; do { CopyASCIItoUTF16(cBuf, buf); // read through, building up an entry. If we finish an entry, check for // a match and return out of the loop if we match @@ -491,18 +490,17 @@ nsOSHelperAppService::GetExtensionsAndDe nsresult rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes), cBuf, &netscapeFormat, &more); if (NS_FAILED(rv)) { return rv; } nsAutoString extensions; - nsString entry; - entry.SetCapacity(100); + nsAutoStringN<101> entry; nsAString::const_iterator majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd, descriptionStart, descriptionEnd; do { CopyASCIItoUTF16(cBuf, buf); // read through, building up an entry. If we finish an entry, check for // a match and return out of the loop if we match @@ -942,20 +940,19 @@ nsOSHelperAppService::GetHandlerAndDescr nsCOMPtr<nsILineInputStream> mailcap (do_QueryInterface(mailcapFile, &rv)); if (NS_FAILED(rv)) { LOG(("Interface trouble in stream land!")); return rv; } - nsString entry, buffer; - nsAutoCString cBuffer; - entry.SetCapacity(128); - cBuffer.SetCapacity(80); + nsAutoStringN<129> entry; + nsAutoStringN<81> buffer; + nsAutoCStringN<81> cBuffer; rv = mailcap->ReadLine(cBuffer, &more); if (NS_FAILED(rv)) { mailcapFile->Close(); return rv; } do { // return on end-of-file in the loop