Merge autoland to mozilla-central. a=merge

This commit is contained in:
smolnar 2022-03-10 19:22:39 +02:00
commit fb8877ceea
398 changed files with 6682 additions and 4944 deletions

View File

@ -55,7 +55,7 @@ rev = "c51b63595a27a6ef45161012323e0261475c10c9"
[source."https://github.com/mozilla-spidermonkey/jsparagus"]
git = "https://github.com/mozilla-spidermonkey/jsparagus"
replace-with = "vendored-sources"
rev = "a1a6ba41f0c610ebe751639f25f037474ca52941"
rev = "eee6b112b1c29a663cbe316bc8f46b5c062269fe"
[source."https://github.com/kinetiknz/mio-named-pipes"]
git = "https://github.com/kinetiknz/mio-named-pipes"

View File

@ -29,10 +29,8 @@ jobs:
- mozilla-central
when:
by-project:
# Please update the `searchfox-index` job `mozilla-central` `when`
# times as well if updating the times here.
# `l10n-bumper` job should also have enough time to finish before
# this job runs
# `l10n-bumper` job should have enough time to finish before this
# job runs
mozilla-central: [{hour: 10, minute: 0}, {hour: 22, minute: 0}]
# No default
@ -113,37 +111,32 @@ jobs:
# For more context on this job, see:
# https://github.com/mozsearch/mozsearch-mozilla#how-searchfoxorg-stays-up-to-date
# Note that searchfox now runs on-push for mozilla-central, but continues
# to use cron jobs for all other branches
- name: searchfox-index
job:
type: decision-task
treeherder-symbol: Searchfox
target-tasks-method: searchfox_index
run-on-projects:
- mozilla-central
- mozilla-beta
- mozilla-release
- mozilla-esr91
- elm
# For all non m-c jobs we just run once daily matching the 10 UTC
# nightly which is designed to align with searchfox's AWS cron
# jobs (for legacy reasons) rather than trying to align with
# specific builds. (Ex: mozilla-beta has a "daily-releases" job
# that currently runs 3 times a week.)
#
# Note that for branches that don't change a lot this will result
# in a redundant job being scheduled each day rather than reusing
# the previous day's job. This is only beneficial in the sense
# that there's no risk of expiration for artifacts and is a
# trade-off to avoid stale indices. Bug 1686981 tracks fixing
# this.
when:
by-project:
# We want to run at both of the times the nightly runs.
mozilla-central: [{hour: 10, minute: 0}, {hour: 22, minute: 0}]
# For all other jobs we just run once daily matching the 10 UTC
# nightly which is designed to align with searchfox's AWS cron
# jobs (for legacy reasons) rather than trying to align with
# specific builds. (Ex: mozilla-beta has a "daily-releases" job
# that currently runs 3 times a week.)
#
# Note that for branches that don't change a lot this will result
# in a redundant job being scheduled each day rather than reusing
# the previous day's job. This is only beneficial in the sense
# that there's no risk of expiration for artifacts and is a
# trade-off to avoid stale indices. Bug 1686981 tracks fixing
# this.
mozilla-beta: [{hour: 10, minute: 0}]
mozilla-release: [{hour: 10, minute: 0}]
mozilla-esr91: [{hour: 10, minute: 0}]
elm: [{hour: 10, minute: 0}]
- {hour: 10, minute: 0}
- name: linux64-clang-trunk-perf
job:

24
Cargo.lock generated
View File

@ -611,7 +611,6 @@ version = "0.1.0"
dependencies = [
"nserror",
"nsstring",
"rental",
"rust_cascade",
"thin-vec",
"xpcom",
@ -632,12 +631,9 @@ dependencies = [
"crossbeam-utils 0.8.6",
"cstr",
"log",
"malloc_size_of_derive",
"memmap2 0.3.1",
"moz_task",
"nserror",
"nsstring",
"rental",
"rkv",
"rust_cascade",
"sha2",
@ -2608,7 +2604,7 @@ version = "0.3.100"
[[package]]
name = "jsparagus"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"jsparagus-ast",
"jsparagus-emitter",
@ -2622,7 +2618,7 @@ dependencies = [
[[package]]
name = "jsparagus-ast"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"bumpalo",
"indexmap",
@ -2631,7 +2627,7 @@ dependencies = [
[[package]]
name = "jsparagus-emitter"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"bumpalo",
"byteorder",
@ -2644,7 +2640,7 @@ dependencies = [
[[package]]
name = "jsparagus-generated-parser"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"bumpalo",
"jsparagus-ast",
@ -2654,12 +2650,12 @@ dependencies = [
[[package]]
name = "jsparagus-json-log"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
[[package]]
name = "jsparagus-parser"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"arrayvec 0.5.2",
"bumpalo",
@ -2671,7 +2667,7 @@ dependencies = [
[[package]]
name = "jsparagus-scope"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"indexmap",
"jsparagus-ast",
@ -2681,7 +2677,7 @@ dependencies = [
[[package]]
name = "jsparagus-stencil"
version = "0.1.0"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=a1a6ba41f0c610ebe751639f25f037474ca52941#a1a6ba41f0c610ebe751639f25f037474ca52941"
source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eee6b112b1c29a663cbe316bc8f46b5c062269fe#eee6b112b1c29a663cbe316bc8f46b5c062269fe"
dependencies = [
"jsparagus-ast",
]
@ -4431,9 +4427,9 @@ checksum = "8a654c5bda722c699be6b0fe4c0d90de218928da5b724c3e467fc48865c37263"
[[package]]
name = "rust_cascade"
version = "0.6.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a5b9bba8f5b985e4923dadd273a987f83669083f3355d65c699e02b9d3d854d"
checksum = "d09c17a9310f1eb79a67d307adffa7fa1c5943eaadcc21d4fb7f611536d66c4f"
dependencies = [
"byteorder",
"digest",

View File

@ -294,7 +294,7 @@ bool AccessibleWrap::GetSelectionBounds(int32_t* aStartOffset,
return false;
}
void AccessibleWrap::PivotTo(int32_t aGranularity, bool aForward,
bool AccessibleWrap::PivotTo(int32_t aGranularity, bool aForward,
bool aInclusive) {
a11y::Pivot pivot(RootAccessible());
TraversalRule rule(aGranularity);
@ -326,7 +326,11 @@ void AccessibleWrap::PivotTo(int32_t aGranularity, bool aForward,
SessionAccessibility::GetInstanceFor(result);
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
}
return true;
}
return false;
}
void AccessibleWrap::ExploreByTouch(float aX, float aY) {

View File

@ -37,7 +37,7 @@ class AccessibleWrap : public LocalAccessible {
virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual void PivotTo(int32_t aGranularity, bool aForward, bool aInclusive);
virtual bool PivotTo(int32_t aGranularity, bool aForward, bool aInclusive);
virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset,
int32_t aEndOffset, bool aForward, bool aSelect);

View File

@ -113,15 +113,16 @@ bool RemoteAccessibleWrap::GetSelectionBounds(int32_t* aStartOffset,
return Proxy()->SelectionBoundsAt(0, unused, aStartOffset, aEndOffset);
}
void RemoteAccessibleWrap::PivotTo(int32_t aGranularity, bool aForward,
bool RemoteAccessibleWrap::PivotTo(int32_t aGranularity, bool aForward,
bool aInclusive) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
AccessibleWrap::PivotTo(aGranularity, aForward, aInclusive);
return;
return AccessibleWrap::PivotTo(aGranularity, aForward, aInclusive);
}
Unused << Proxy()->Document()->GetPlatformExtension()->SendPivot(
Proxy()->ID(), aGranularity, aForward, aInclusive);
return true;
}
void RemoteAccessibleWrap::NavigateText(int32_t aGranularity,

View File

@ -61,7 +61,7 @@ class RemoteAccessibleWrap : public AccessibleWrap {
virtual bool GetSelectionBounds(int32_t* aStartOffset,
int32_t* aEndOffset) override;
virtual void PivotTo(int32_t aGranularity, bool aForward,
virtual bool PivotTo(int32_t aGranularity, bool aForward,
bool aInclusive) override;
virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset,

View File

@ -25,6 +25,7 @@
#include "mozilla/widget/GeckoViewSupport.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/StaticPrefs_accessibility.h"
#ifdef DEBUG
# include <android/log.h>
@ -90,6 +91,10 @@ void SessionAccessibility::Init() {
Settings::Init();
}
bool SessionAccessibility::IsCacheEnabled() {
return StaticPrefs::accessibility_cache_enabled_AtStartup();
}
mozilla::jni::Object::LocalRef SessionAccessibility::GetNodeInfo(int32_t aID) {
java::GeckoBundle::GlobalRef ret = nullptr;
RefPtr<SessionAccessibility> self(this);
@ -129,6 +134,22 @@ void SessionAccessibility::Click(int32_t aID) {
FORWARD_ACTION_TO_ACCESSIBLE(DoAction, 0);
}
bool SessionAccessibility::CachedPivot(int32_t aID, int32_t aGranularity,
bool aForward, bool aInclusive) {
RefPtr<SessionAccessibility> self(this);
bool ret = false;
nsAppShell::SyncRunEvent(
[this, self, aID, aGranularity, aForward, aInclusive, &ret] {
if (RootAccessibleWrap* rootAcc = GetRoot()) {
if (AccessibleWrap* acc = rootAcc->FindAccessibleById(aID)) {
ret = acc->PivotTo(aGranularity, aForward, aInclusive);
}
}
});
return ret;
}
void SessionAccessibility::Pivot(int32_t aID, int32_t aGranularity,
bool aForward, bool aInclusive) {
FORWARD_ACTION_TO_ACCESSIBLE(PivotTo, aGranularity, aForward, aInclusive);

View File

@ -47,10 +47,13 @@ class SessionAccessibility final
// Native implementations
using Base::AttachNative;
using Base::DisposeNative;
bool IsCacheEnabled();
jni::Object::LocalRef GetNodeInfo(int32_t aID);
void SetText(int32_t aID, jni::String::Param aText);
void Click(int32_t aID);
void Pivot(int32_t aID, int32_t aGranularity, bool aForward, bool aInclusive);
bool CachedPivot(int32_t aID, int32_t aGranularity, bool aForward,
bool aInclusive);
void ExploreByTouch(int32_t aID, float aX, float aY);
void NavigateText(int32_t aID, int32_t aGranularity, int32_t aStartOffset,
int32_t aEndOffset, bool aForward, bool aSelect);

View File

@ -750,17 +750,28 @@
}
}
} else if (draggedTab) {
let newIndex = this._getDropIndex(event, false);
let newTabs = [];
for (let tab of movingTabs) {
let newTab = gBrowser.adoptTab(tab, newIndex++, tab == draggedTab);
newTabs.push(newTab);
// Move the tabs. To avoid multiple tab-switches in the original window,
// the selected tab should be adopted last.
let dropIndex = this._getDropIndex(event, false);
let newIndex = dropIndex;
let selectedIndex = -1;
for (let i = 0; i < movingTabs.length; ++i) {
let tab = movingTabs[i];
if (tab.selected) {
selectedIndex = i;
} else {
gBrowser.adoptTab(tab, newIndex++, tab == draggedTab);
}
}
if (selectedIndex >= 0) {
let tab = movingTabs[selectedIndex];
gBrowser.adoptTab(tab, dropIndex + selectedIndex, tab == draggedTab);
}
// Restore tab selection
gBrowser.addRangeToMultiSelectedTabs(
newTabs[0],
newTabs[newTabs.length - 1]
gBrowser.tabs[dropIndex],
gBrowser.tabs[dropIndex + movingTabs.length - 1]
);
} else {
// Pass true to disallow dropping javascript: or data: urls

View File

@ -72,3 +72,47 @@ add_task(async function test() {
BrowserTestUtils.closeWindow(newWindow);
BrowserTestUtils.removeTab(tab4);
});
add_task(async function test_laziness() {
const params = { createLazyBrowser: true };
const url = "http://mochi.test:8888/?";
const tab1 = BrowserTestUtils.addTab(gBrowser, url + "1", params);
const tab2 = BrowserTestUtils.addTab(gBrowser, url + "2");
const tab3 = BrowserTestUtils.addTab(gBrowser, url + "3", params);
await BrowserTestUtils.switchTab(gBrowser, tab2);
await triggerClickOn(tab1, { ctrlKey: true });
await triggerClickOn(tab3, { ctrlKey: true });
is(gBrowser.selectedTab, tab2, "Tab2 is selected");
is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
ok(tab1.multiselected, "Tab1 is multiselected");
ok(tab2.multiselected, "Tab2 is multiselected");
ok(tab3.multiselected, "Tab3 is multiselected");
ok(!tab1.linkedPanel, "Tab1 is lazy");
ok(tab2.linkedPanel, "Tab2 is not lazy");
ok(!tab3.linkedPanel, "Tab3 is lazy");
const win2 = await BrowserTestUtils.openNewBrowserWindow();
const gBrowser2 = win2.gBrowser;
is(gBrowser2.tabs.length, 1, "Second window has 1 tab");
await dragAndDrop(tab2, gBrowser2.tabs[0], false, win2);
await TestUtils.waitForCondition(
() => gBrowser2.tabs.length == 4,
"Moved tabs into second window"
);
is(gBrowser2.tabs[1].linkedBrowser.currentURI.spec, url + "1");
is(gBrowser2.tabs[2].linkedBrowser.currentURI.spec, url + "2");
is(gBrowser2.tabs[3].linkedBrowser.currentURI.spec, url + "3");
is(gBrowser2.selectedTab, gBrowser2.tabs[2], "Tab2 is selected");
is(gBrowser2.multiSelectedTabsCount, 3, "Three multiselected tabs");
ok(gBrowser2.tabs[1].multiselected, "Tab1 is multiselected");
ok(gBrowser2.tabs[2].multiselected, "Tab2 is multiselected");
ok(gBrowser2.tabs[3].multiselected, "Tab3 is multiselected");
ok(!gBrowser2.tabs[1].linkedPanel, "Tab1 is lazy");
ok(gBrowser2.tabs[2].linkedPanel, "Tab2 is not lazy");
ok(!gBrowser2.tabs[3].linkedPanel, "Tab3 is lazy");
await BrowserTestUtils.closeWindow(win2);
});

View File

@ -4,11 +4,7 @@
"use strict";
const EXPORTED_SYMBOLS = [
"KeywordTree",
"ONBOARDING_CHOICE",
"UrlbarQuickSuggest",
];
const EXPORTED_SYMBOLS = ["ONBOARDING_CHOICE", "UrlbarQuickSuggest"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
@ -66,7 +62,7 @@ const ONBOARDING_URI =
const SUGGESTION_SCORE = 0.2;
/**
* Fetches the suggestions data from RemoteSettings and builds the tree
* Fetches the suggestions data from RemoteSettings and builds the structures
* to provide suggestions for UrlbarProviderQuickSuggest.
*/
class Suggestions {
@ -127,11 +123,7 @@ class Suggestions {
async query(phrase) {
log.info("Handling query for", phrase);
phrase = phrase.toLowerCase();
let resultID = this._tree.get(phrase);
if (resultID === null) {
return null;
}
let result = this._results.get(resultID);
let result = this._resultsByKeyword.get(phrase);
if (!result) {
return null;
}
@ -335,11 +327,9 @@ class Suggestions {
// Configuration data synced from remote settings.
_config = {};
// Maps from result IDs to the corresponding results.
_results = new Map();
// A tree that maps keywords to a result.
_tree = new KeywordTree();
// Maps from keywords to their corresponding results. Keywords are unique in
// the underlying data, so a keyword will only ever map to one result.
_resultsByKeyword = new Map();
/**
* Queues a task to ensure our remote settings client is initialized or torn
@ -365,8 +355,8 @@ class Suggestions {
}
/**
* Queues a task to (re)create the results map and keyword tree from the
* remote settings data plus any other work that needs to be done on sync.
* Queues a task to populate the results map from the remote settings data
* plus any other work that needs to be done on sync.
*
* @param {object} [event]
* The event object passed to the "sync" event listener if you're calling
@ -396,8 +386,7 @@ class Suggestions {
log.debug("Got configuration:", configArray);
this._config = configArray?.[0]?.configuration || {};
this._results = new Map();
this._tree = new KeywordTree();
this._resultsByKeyword.clear();
for (let record of data) {
let { buffer } = await this._rs.attachments.download(record, {
@ -410,17 +399,16 @@ class Suggestions {
}
/**
* Adds a list of result objects to the results map and keyword tree. This
* method is also used by tests to set up mock suggestions.
* Adds a list of result objects to the results map. This method is also used
* by tests to set up mock suggestions.
*
* @param {array} results
* Array of result objects.
*/
_addResults(results) {
for (let result of results) {
this._results.set(result.id, result);
for (let keyword of result.keywords) {
this._tree.set(keyword, result.id);
this._resultsByKeyword.set(keyword, result);
}
}
}
@ -447,166 +435,4 @@ class Suggestions {
}
}
// Token used as a key to store results within the Map, cannot be used
// within a keyword.
const RESULT_KEY = "^";
/**
* This is an implementation of a Map based Tree. We can store
* multiple keywords that point to a single term, for example:
*
* tree.add("headphones", "headphones");
* tree.add("headph", "headphones");
* tree.add("earphones", "headphones");
*
* tree.get("headph") == "headphones"
*
* The tree can store multiple prefixes to a term efficiently
* so ["hea", "head", "headp", "headph", "headpho", ...] wont lead
* to duplication in memory. The tree will only return a result
* for keywords that have been explcitly defined and not attempt
* to guess based on prefix.
*
* Once a tree have been build, it can be flattened with `.flatten`
* the tree can then be serialised and deserialised with `.toJSON`
* and `.fromJSON`.
*/
class KeywordTree {
constructor() {
this.tree = new Map();
}
/*
* Set a keyword for a result.
*/
set(keyword, id) {
if (keyword.includes(RESULT_KEY)) {
throw new Error(`"${RESULT_KEY}" is reserved`);
}
let tree = this.tree;
for (let x = 0, c = ""; (c = keyword.charAt(x)); x++) {
let child = tree.get(c) || new Map();
tree.set(c, child);
tree = child;
}
tree.set(RESULT_KEY, id);
}
/**
* Get the result for a given phrase.
*
* @param {string} query
* The query string.
* @returns {*}
* The matching result in the tree or null if there isn't a match.
*/
get(query) {
query = query.trimStart() + RESULT_KEY;
let node = this.tree;
let phrase = "";
while (phrase.length < query.length) {
// First, assume the tree isn't flattened and try to look up the next char
// in the query.
let key = query[phrase.length];
let child = node.get(key);
if (!child) {
// Not found, so fall back to looking through all of the node's keys.
key = null;
for (let childKey of node.keys()) {
let childPhrase = phrase + childKey;
if (childPhrase == query.substring(0, childPhrase.length)) {
key = childKey;
break;
}
}
if (!key) {
return null;
}
child = node.get(key);
}
node = child;
phrase += key;
}
if (phrase.length != query.length) {
return null;
}
// At this point, `node` is the found result.
return node;
}
/*
* We flatten the tree by combining consecutive single branch keywords
* with the same results into a longer keyword. so ["a", ["b", ["c"]]]
* becomes ["abc"], we need to be careful that the result matches so
* if a prefix search for "hello" only starts after 2 characters it will
* be flattened to ["he", ["llo"]].
*/
flatten() {
this._flatten("", this.tree, null);
}
/**
* Recursive flatten() helper.
*
* @param {string} key
* The key for `node` in `parent`.
* @param {Map} node
* The currently visited node.
* @param {Map} parent
* The parent of `node`, or null if `node` is the root.
*/
_flatten(key, node, parent) {
// Flatten the node's children. We need to store node.entries() in an array
// rather than iterating over them directly because _flatten() can modify
// them during iteration.
for (let [childKey, child] of [...node.entries()]) {
if (childKey != RESULT_KEY) {
this._flatten(childKey, child, node);
}
}
// If the node has a single child, then replace the node in `parent` with
// the child.
if (node.size == 1 && parent) {
parent.delete(key);
let childKey = [...node.keys()][0];
parent.set(key + childKey, node.get(childKey));
}
}
/*
* Turn a tree into a serialisable JSON object.
*/
toJSONObject(map = this.tree) {
let tmp = {};
for (let [key, val] of map) {
if (val instanceof Map) {
tmp[key] = this.toJSONObject(val);
} else {
tmp[key] = val;
}
}
return tmp;
}
/*
* Build a tree from a serialisable JSON object that was built
* with `toJSON`.
*/
fromJSON(json) {
this.tree = this.JSONObjectToMap(json);
}
JSONObjectToMap(obj) {
let map = new Map();
for (let key of Object.keys(obj)) {
if (typeof obj[key] == "object") {
map.set(key, this.JSONObjectToMap(obj[key]));
} else {
map.set(key, obj[key]);
}
}
return map;
}
}
let UrlbarQuickSuggest = new Suggestions();

View File

@ -1,240 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
XPCOMUtils.defineLazyModuleGetters(this, {
KeywordTree: "resource:///modules/UrlbarQuickSuggest.jsm",
UrlbarQuickSuggest: "resource:///modules/UrlbarQuickSuggest.jsm",
});
let data = [
{
term: "helzo foo",
keywords: ["hel", "helz", "helzo", "helzo f", "helzo fo", "helzo foo"],
},
{
term: "helzo bar",
keywords: ["helzo b", "helzo ba", "helzo bar"],
},
// test10 and test11 must be in the following order to test bug 1697678.
{ term: "test10", keywords: ["kids sn", "kids sneakers"] },
{ term: "test11", keywords: ["ki", "kin", "kind", "kindl", "kindle"] },
// test12 and test13 must be in the following order to test bug 1698534.
// Searching for "websi" should match test12 because search strings must match
// keywords exactly, no prefix matching.
{ term: "test12", keywords: ["websi"] },
{ term: "test13", keywords: ["webs", "websit"] },
];
let dataByTerm = data.reduce((map, record) => {
map[record.term] = record;
return map;
}, {});
function basicChecks(tree) {
let tests = [
{
query: "nomatch",
term: null,
},
{
query: "he",
term: null,
},
{
query: "hel",
term: "helzo foo",
fullKeyword: "helzo",
},
{
query: "helzo",
term: "helzo foo",
fullKeyword: "helzo",
},
{
query: "helzo ",
term: null,
},
{
query: "helzo f",
term: "helzo foo",
fullKeyword: "helzo foo",
},
{
query: "helzo foo",
term: "helzo foo",
fullKeyword: "helzo foo",
},
{
query: "helzo b",
term: "helzo bar",
fullKeyword: "helzo bar",
},
{
query: "helzo bar",
term: "helzo bar",
fullKeyword: "helzo bar",
},
{
query: "f",
term: null,
},
{
query: "fo",
term: null,
},
{
query: "foo",
term: null,
},
{
query: "ki",
term: "test11",
fullKeyword: "kindle",
},
{
query: "kin",
term: "test11",
fullKeyword: "kindle",
},
{
query: "kind",
term: "test11",
fullKeyword: "kindle",
},
{
query: "kid",
term: null,
},
{
query: "kids",
term: null,
},
{
query: "kids ",
term: null,
},
{
query: "kids s",
term: null,
},
{
query: "kids sn",
term: "test10",
fullKeyword: "kids sneakers",
},
{
query: "web",
term: null,
},
{
query: "webs",
term: "test13",
fullKeyword: "websit",
},
{
query: "websi",
term: "test12",
fullKeyword: "websi",
},
{
query: "websit",
term: "test13",
fullKeyword: "websit",
},
{
query: "website",
term: null,
},
];
for (let test of tests) {
info("Checking " + JSON.stringify(test));
let { query, term, fullKeyword } = test;
Assert.equal(tree.get(query), term);
if (term) {
Assert.equal(
UrlbarQuickSuggest.getFullKeyword(query, dataByTerm[term].keywords),
fullKeyword
);
}
}
}
function createTree() {
let tree = new KeywordTree();
for (let { term, keywords } of data) {
keywords.forEach(keyword => tree.set(keyword, term));
}
return tree;
}
add_task(async function test_basic() {
basicChecks(createTree());
});
add_task(async function test_deserialisation() {
let str = JSON.stringify(createTree().toJSONObject());
let newTree = new KeywordTree();
newTree.fromJSON(JSON.parse(str));
basicChecks(newTree);
});
add_task(async function test_flatten() {
let tree = createTree();
tree.flatten();
Assert.deepEqual(
{
hel: {
"^": "helzo foo",
z: {
"^": "helzo foo",
o: {
"^": "helzo foo",
" ": {
f: {
"^": "helzo foo",
o: { "^": "helzo foo", "o^": "helzo foo" },
},
b: {
"^": "helzo bar",
a: { "^": "helzo bar", "r^": "helzo bar" },
},
},
},
},
},
ki: {
"^": "test11",
n: {
"^": "test11",
d: { "^": "test11", l: { "^": "test11", "e^": "test11" } },
},
"ds sn": { "^": "test10", "eakers^": "test10" },
},
webs: { i: { "^": "test12", "t^": "test13" }, "^": "test13" },
},
tree.toJSONObject()
);
basicChecks(tree);
});
add_task(async function test_reserved() {
let tree = createTree();
try {
tree.set("helzo^foo");
Assert.ok(false, "Should thrown when using reserved characters");
} catch (e) {
Assert.ok(true, "Did throw when using reserved characters");
}
});

View File

@ -5,7 +5,6 @@ firefox-appdir = browser
[test_quicksuggest.js]
[test_quicksuggest_bestMatch.js]
[test_quicksuggest_keywordtree.js]
[test_quicksuggest_merino.js]
[test_quicksuggest_migrate_v1.js]
[test_quicksuggest_migrate_v2.js]

View File

@ -45,7 +45,10 @@ let AVAILABLE_PIP_OVERRIDES;
},
netflix: {
"https://*.netflix.com/*": { keyboardControls: ~KEYBOARD_CONTROLS.SEEK },
"https://*.netflix.com/*": {
keyboardControls: ~KEYBOARD_CONTROLS.SEEK,
videoWrapperScriptPath: "video-wrappers/netflix.js",
},
"https://*.netflix.com/browse*": { policy: TOGGLE_POLICIES.HIDDEN },
"https://*.netflix.com/latest*": { policy: TOGGLE_POLICIES.HIDDEN },
"https://*.netflix.com/Kids*": { policy: TOGGLE_POLICIES.HIDDEN },

View File

@ -29,6 +29,7 @@ FINAL_TARGET_FILES.features["pictureinpicture@mozilla.org"]["lib"] += [
FINAL_TARGET_FILES.features["pictureinpicture@mozilla.org"]["video-wrappers"] += [
"video-wrappers/mock-wrapper.js",
"video-wrappers/netflix.js",
"video-wrappers/primeVideo.js",
"video-wrappers/youtube.js",
]

View File

@ -0,0 +1,54 @@
/* 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/. */
"use strict";
class PictureInPictureVideoWrapper {
constructor() {
let netflixPlayerAPI = window.wrappedJSObject.netflix.appContext.state.playerApp.getAPI()
.videoPlayer;
let sessionId = netflixPlayerAPI.getAllPlayerSessionIds()[0];
this.player = netflixPlayerAPI.getVideoPlayerBySessionId(sessionId);
}
play() {
this.player.play();
}
pause() {
this.player.pause();
}
setCaptionContainerObserver(video, updateCaptionsFunction) {
let container = document.querySelector(".watch-video");
if (container) {
updateCaptionsFunction("");
const callback = function(mutationsList, observer) {
let textNodeList = container
.querySelector(".player-timedtext")
?.querySelectorAll("span");
if (!textNodeList || textNodeList.length < 1) {
updateCaptionsFunction("");
return;
}
updateCaptionsFunction(
Array.from(textNodeList, x => x.textContent).join("\n")
);
};
// immediately invoke the callback function to add subtitles to the PiP window
callback([1], null);
let captionsObserver = new MutationObserver(callback);
captionsObserver.observe(container, {
attributes: false,
childList: true,
subtree: true,
});
}
}
}
this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper;

View File

@ -23,7 +23,7 @@ class PictureInPictureVideoWrapper {
// eslint-disable-next-line no-unused-vars
for (const mutation of mutationsList) {
let textNodeList = container
.querySelector(".ytp-caption-window-bottom")
.querySelector(".captions-text")
?.querySelectorAll(".caption-visual-line");
if (!textNodeList) {
updateCaptionsFunction("");

View File

@ -655,11 +655,10 @@ menupopup::part(drop-indicator) {
}
/* ENABLE DEVTOOLS POPUP */
#enable-devtools-popup {
%if defined(XP_MACOSX) || defined(XP_WIN)
font-size: 1.18em;
%endif
@media (-moz-platform: macos), (-moz-platform: windows) {
#enable-devtools-popup {
font-size: 1.18em;
}
}
#sharing-tabs-warning-panel > hbox[type="window"] > vbox > label > #sharing-warning-screen-panel-header,

View File

@ -6,16 +6,22 @@
#ctrlTab-panel {
appearance: none;
%ifdef XP_MACOSX
-moz-window-shadow: none;
%endif
--panel-background: hsla(0,0%,40%,.85);
--panel-color: white;
--panel-border-color: transparent;
--panel-padding: 20px 10px 10px;
%ifndef XP_MACOSX
font-weight: bold;
%endif
}
@media (-moz-platform: macos) {
#ctrlTab-panel {
-moz-window-shadow: none;
}
}
@media not (-moz-platform: macos) {
#ctrlTab-panel {
font-weight: bold;
}
}
.ctrlTab-preview,

View File

@ -424,8 +424,7 @@
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
%ifdef XP_MACOSX
@media (min-resolution: 1.1dppx) {
@media (-moz-platform: macos) and (min-resolution: 1.1dppx) {
.translation-icon {
list-style-image: url(chrome://browser/skin/translation-16@2x.png);
-moz-image-region: rect(0px, 32px, 32px, 0px);
@ -435,7 +434,6 @@
-moz-image-region: rect(0px, 64px, 32px, 32px);
}
}
%endif
/* UPDATE */
.popup-notification-icon[popupid="update-available"],

View File

@ -7,64 +7,67 @@
injected into the sidebar. */
@media (-moz-proton-places-tooltip) {
.places-tooltip-title {
font-weight: 600;
%ifdef XP_MACOSX
font-size: 1.1em;
%endif
/* Clip after 2 lines, this should ideally use a different method like -webkit-line-clamp or
overflow: hidden; to get ellipsis support but XUL tooltip sizing makes that difficult. */
max-height: 2.5em;
overflow-y: clip;
}
.places-tooltip-uri {
color: color-mix(in srgb, currentColor 50%, transparent);
}
#places-tooltip-insecure-icon {
/* Using the same broken-lock icon as the main identity-block styles. */
list-style-image: url(chrome://global/skin/icons/security-broken.svg);
-moz-context-properties: fill;
fill: currentColor;
width: 1em;
height: 1em;
margin-inline-start: 0;
margin-inline-end: .2em;
}
%if defined(XP_MACOSX) || defined(XP_WIN)
@media not (prefers-contrast) {
.places-tooltip {
--places-tooltip-shadow-size: 6px;
appearance: none;
background: transparent;
border: none;
/* The tooltip has internal padding to allow for the
box-shadow to not get clipped. The negative margin
here keeps the tooltip aligned horizontally with the
anchor, and should match the padding-inline-start of
the tooltip. */
padding: var(--places-tooltip-shadow-size);
.places-tooltip-title {
font-weight: 600;
/* Clip after 2 lines, this should ideally use a different method like -webkit-line-clamp or
overflow: hidden; to get ellipsis support but XUL tooltip sizing makes that difficult. */
max-height: 2.5em;
overflow-y: clip;
}
.places-tooltip[position] {
margin-inline-start: calc(-1 * var(--places-tooltip-shadow-size));
margin-block-start: calc(-1 * var(--places-tooltip-shadow-size));
}
.places-tooltip-box {
padding: 3px 5px;
background: var(--arrowpanel-background);
color: var(--arrowpanel-color);
border: 1px solid transparent;
border-radius: 4px;
box-shadow: 0 2px var(--places-tooltip-shadow-size) rgba(58,57,68,.2);
@media (-moz-platform: macos) {
.places-tooltip-title {
font-size: 1.1em;
}
}
.places-tooltip-uri {
color: var(--panel-description-color);
color: color-mix(in srgb, currentColor 50%, transparent);
}
#places-tooltip-insecure-icon {
/* Using the same broken-lock icon as the main identity-block styles. */
list-style-image: url(chrome://global/skin/icons/security-broken.svg);
-moz-context-properties: fill;
fill: currentColor;
width: 1em;
height: 1em;
margin-inline-start: 0;
margin-inline-end: .2em;
}
@media (-moz-platform: macos), (-moz-platform: windows) {
@media not (prefers-contrast) {
.places-tooltip {
--places-tooltip-shadow-size: 6px;
appearance: none;
background: transparent;
border: none;
/* The tooltip has internal padding to allow for the
box-shadow to not get clipped. The negative margin
here keeps the tooltip aligned horizontally with the
anchor, and should match the padding-inline-start of
the tooltip. */
padding: var(--places-tooltip-shadow-size);
}
.places-tooltip[position] {
margin-inline-start: calc(-1 * var(--places-tooltip-shadow-size));
margin-block-start: calc(-1 * var(--places-tooltip-shadow-size));
}
.places-tooltip-box {
padding: 3px 5px;
background: var(--arrowpanel-background);
color: var(--arrowpanel-color);
border: 1px solid transparent;
border-radius: 4px;
box-shadow: 0 2px var(--places-tooltip-shadow-size) rgba(58,57,68,.2);
}
.places-tooltip-uri {
color: var(--panel-description-color);
}
}
}
}
%endif
} /*** END proton ***/

View File

@ -16,27 +16,27 @@
border-bottom: 1px solid var(--sidebar-border-color);
}
%ifndef MOZ_WIDGET_GTK
% We don't let the splitter overlap the sidebar on Linux since the sidebar's
% scrollbar is too narrow on Linux.
.sidebar-splitter {
appearance: none;
border: 0 solid;
border-inline-end-width: 1px;
border-color: var(--sidebar-border-color);
min-width: 1px;
width: 4px;
background-image: none !important;
background-color: transparent;
margin-inline-start: -4px;
position: relative;
}
@media not (-moz-platform: linux) {
/* We don't let the splitter overlap the sidebar on Linux since the sidebar's
scrollbar is too narrow on Linux. */
.sidebar-splitter {
appearance: none;
border: 0 solid;
border-inline-end-width: 1px;
border-color: var(--sidebar-border-color);
min-width: 1px;
width: 4px;
background-image: none !important;
background-color: transparent;
margin-inline-start: -4px;
position: relative;
}
#sidebar-box[positionend] + .sidebar-splitter {
border-inline-width: 1px 0;
margin-inline: 0 -4px;
#sidebar-box[positionend] + .sidebar-splitter {
border-inline-width: 1px 0;
margin-inline: 0 -4px;
}
}
%endif
#sidebar-throbber[loading="true"] {
list-style-image: url("chrome://global/skin/icons/loading.png");

View File

@ -273,9 +273,6 @@ toolbar {
list-style-image: url("chrome://global/skin/icons/print.svg");
}
%ifdef XP_MACOSX
#restore-button,
%endif
#fullscreen-button {
list-style-image: url("chrome://browser/skin/fullscreen.svg");
}

View File

@ -105,6 +105,26 @@ check_and_add_gcc_warning("-Wenum-compare-conditional")
# catches unintentional switch case fallthroughs
check_and_add_gcc_warning("-Wimplicit-fallthrough", cxx_compiler)
# Enable some ObjC diagnostics that are only relevant when targeting macOS:
with only_when(depends(target)(lambda t: t.kernel == "Darwin")):
# catch redeclaration of ObjC method parameter name
check_and_add_gcc_warning("-Wduplicate-method-arg")
# catch multiple declarations of ObjC method found
check_and_add_gcc_warning("-Wduplicate-method-match")
# catch ObjC method with no return type specified
check_and_add_gcc_warning("-Wmissing-method-return-type")
# catch implicit conversions between ObjC BOOL and int
check_and_add_gcc_warning("-Wobjc-signed-char-bool")
# catch semicolon before ObjC method body
check_and_add_gcc_warning("-Wsemicolon-before-method-body")
# catch ObjC method parameter type not matching super class method
check_and_add_gcc_warning("-Wsuper-class-method-mismatch")
# catches expressions used as a null pointer constant
# XXX: at the time of writing, the version of clang used on the OS X test
# machines has a bug that causes it to reject some valid files if both

View File

@ -43,7 +43,7 @@ def get_crashreports(directory, name=None):
"$MOZ_FETCHES_DIR is not set in the environment"
)
stackwalk_binary = os.path.join(
fetches_dir, "minidump_stackwalk", "minidump_stackwalk"
fetches_dir, "minidump-stackwalk", "minidump-stackwalk"
)
if sys.platform == "win32":
stackwalk_binary += ".exe"
@ -131,6 +131,7 @@ if __name__ == "__main__":
env = os.environ.copy()
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
env["XPCOM_DEBUG_BREAK"] = "warn"
# We disable sandboxing to make writing profiling data actually work
# Bug 1553850 considers fixing this.

View File

@ -74,7 +74,7 @@ path:other-licenses/bsdiff/
path:other-licenses/nsis/Contrib/CityHash/cityhash/
path:toolkit/mozapps/update/updater
# for the minidump_stackwalk toolchain task
# for the minidump-stackwalk toolchain task
path:toolkit/crashreporter
path:tools/crashreporter/
path:mfbt

View File

@ -87,12 +87,11 @@ EditingSession.prototype = {
* @return {Number} The property index in the rule.
*/
getPropertyIndex: function(name, rule = this._rules[0]) {
const elementStyleRule = this._rules[0];
if (!elementStyleRule.declarations.length) {
if (!rule.declarations.length) {
return -1;
}
return elementStyleRule.declarations.findIndex(p => p.name === name);
return rule.declarations.findIndex(p => p.name === name);
},
/**

View File

@ -348,4 +348,20 @@ add_task(async function() {
"Pref was changed"
);
ok(true, "Eager evaluation element is no longer displayed");
// reset the preference
await pushPref(EAGER_EVALUATION_PREF, true);
});
// Test that the console instant evaluation is updated on page navigation
add_task(async function() {
const start_uri = "data:text/html, Start uri";
const new_uri = "data:text/html, Test console refresh instant value";
const hud = await openNewTabAndConsole(start_uri);
setInputValue(hud, "globalThis.location.href");
await waitForEagerEvaluationResult(hud, `"${start_uri}"`);
await navigateTo(new_uri);
await waitForEagerEvaluationResult(hud, `"${new_uri}"`);
});

View File

@ -414,6 +414,9 @@ class WebConsoleUI {
* object by the page itself.
*/
async handleNavigated({ hasNativeConsoleAPI }) {
// Updates instant evaluation on page navigation
this.wrapper.dispatchUpdateInstantEvaluationResultForCurrentExpression();
// Wait for completion of any async dispatch before notifying that the console
// is fully updated after a page reload
await this.wrapper.waitAsyncDispatches();

View File

@ -290,6 +290,10 @@ class WebConsoleWrapper {
store.dispatch(actions.evaluateExpression(expression));
}
dispatchUpdateInstantEvaluationResultForCurrentExpression() {
store.dispatch(actions.updateInstantEvaluationResultForCurrentExpression());
}
/**
* Returns a Promise that resolves once any async dispatch is finally dispatched.
*/

View File

@ -0,0 +1,37 @@
Bug pipeline
============
For Firefox quality, Mozilla has different processes to report defects. In parallel, over the years, Mozilla developed many tools around bug management.
.. mermaid::
graph TD
classDef tool fill:#f96;
Community --> B(bugzilla.mozilla.org)
QA --> B
Foxfooding --> B
Fuzzing --> B
SA[Static/Dynamic analysis] --> B
P[Performance monitoring] --> B
Y[Test automation] --> B
Z[Crash detection] --> B
B --> C{Bug update}
C --> D[Metadata improvements]
C --> E[Component triage]
C --> F[Test case verification]
F --> BM{{Bugmon}}:::tool
F --> MR{{Mozregression}}:::tool
D --> AN{{Autonag}}:::tool
E --> BB{{bugbug}}:::tool
D --> BB
More details
------------
* :ref:`Fuzzing`
* `Autonag <https://wiki.mozilla.org/Release_Management/autonag#Introduction>`_ - `Source <https://github.com/mozilla/relman-auto-nag/>`_
* `Bugbug <https://github.com/mozilla/bugbug>`_ - `Blog post about triage <https://hacks.mozilla.org/2019/04/teaching-machines-to-triage-firefox-bugs/>`_ / `Blog post about CI <https://hacks.mozilla.org/2020/07/testing-firefox-more-efficiently-with-machine-learning/>`_
* `Bugmon <https://hacks.mozilla.org/2021/01/analyzing-bugzilla-testcases-with-bugmon/>`_ - `Source <https://github.com/MozillaSecurity/bugmon>`_
* `Mozregression <https://mozilla.github.io/mozregression/>`_ - `Source <https://github.com/mozilla/mozregression>`_

View File

@ -45,6 +45,7 @@ categories:
testing_doc:
- testing/testing-policy
- testing/ci-configs
- testing/chrome-tests
- testing/marionette
- testing/geckodriver
- testing/test-verification

View File

@ -8,7 +8,8 @@ Debugging A Minidump
The
`minidump <http://msdn.microsoft.com/en-us/library/windows/desktop/ms680369%28v=vs.85%29.aspx>`__
file format contains data about a crash on Windows. It is used by
`Breakpad <https://wiki.mozilla.org/Breakpad>`__ and also by various
`rust-minidump <https://github.com/luser/rust-minidump>`__,
`Breakpad <https://wiki.mozilla.org/Breakpad>`__, and also by various
Windows debugging tools. Each minidump includes the following data.
- Details about the exception which led to the crash.
@ -28,6 +29,21 @@ Minidumps are not available to everyone. For details on how to gain
access and where to find minidump files for crash reports, consult the
:ref:`crash report documentation <Understanding Crash Reports>`
Using rust-minidump's tooling
-----------------------------------
Most of our crash-reporting infrastructure is based on rust-minidump.
The primary tool for this is the
`minidump-stackwalk <https://github.com/luser/rust-minidump/tree/master/minidump-stackwalk>`__
CLI application, which includes extensive user documentation.
That documentation includes a
`dedicated section <https://github.com/luser/rust-minidump/tree/master/minidump-stackwalk#analyzing-firefox-minidumps>`__
on locally analyzing Firefox crashreports and minidumps.
If you're looking for minidump_dump, it's included as part of
minidump-stackwalk.
Using the MS Visual Studio debugger
-----------------------------------
@ -125,11 +141,6 @@ need to build the tool for ARM and run it under QEMU.
Using other tools to inspect minidump data
------------------------------------------
Breakpad includes a tool called ``minidump_dump`` built alongside
``minidump_stackwalk`` which will verbosely print the contents of a
minidump. This can sometimes be useful for finding specific information
that is not exposed on crash-stats.
Ted has a few tools that can be built against an already-built copy of
Breakpad to do more targeted inspection. All of these tools assume you
have checked out their source in a directory next to the breakpad
@ -137,9 +148,9 @@ checkout, and that you have built Breakpad in an objdir named
``obj-breakpad`` at the same level.
- `stackwalk-http <https://hg.mozilla.org/users/tmielczarek_mozilla.com/stackwalk-http/>`__
is a version of minidump_stackwalk that can fetch symbols over HTTP,
and also has the Mozilla symbol server URL baked in. If you run it
like ``stackwalk /path/to/dmp /tmp/syms`` it will print the stack
is a version of the breakpad's minidump_stackwalk that can fetch symbols
over HTTP, and also has the Mozilla symbol server URL baked in. If you
run it like ``stackwalk /path/to/dmp /tmp/syms`` it will print the stack
trace and save the symbols it downloaded in ``/tmp/syms``. Note that
symbols are only uploaded to the symbol server for nightly and
release builds, not per-change builds.
@ -177,8 +188,8 @@ Getting a stack trace from a crashed B2G process
#. Build and install
`google-breakpad <https://code.google.com/p/google-breakpad/>`__.
#. Use the
`minidump_stackwalk <https://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide>`__
breakpad tool to get the stack trace.
`minidump-stackwalk <https://github.com/luser/rust-minidump/tree/master/minidump-stackwalk>`__
tool to get the stack trace.
.. code:: bash
@ -188,4 +199,4 @@ Getting a stack trace from a crashed B2G process
$ adb pull /data/b2g/mozilla/*.default/minidump/*.dmp .
$ls *.dmp
71788789-197e-d769-67167423-4e7aef32.dmp
$ minidump_stackwalk 71788789-197e-d769-67167423-4e7aef32.dmp objdir-debug/dist/crashreporter-symbols/
$ minidump-stackwalk 71788789-197e-d769-67167423-4e7aef32.dmp objdir-debug/dist/crashreporter-symbols/

View File

@ -576,13 +576,12 @@ CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
UniquePtr<LoadingSessionHistoryInfo>
CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
LoadingSessionHistoryInfo* aInfo, nsIChannel* aOldChannel,
nsIChannel* aNewChannel) {
LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel) {
MOZ_ASSERT(aInfo);
MOZ_ASSERT(aNewChannel);
SessionHistoryInfo newInfo = SessionHistoryInfo(
aOldChannel, aNewChannel, aInfo->mInfo.LoadType(),
aNewChannel, aInfo->mInfo.LoadType(),
aInfo->mInfo.GetPartitionedPrincipalToInherit(), aInfo->mInfo.GetCsp());
for (size_t i = 0; i < mLoadingEntries.Length(); ++i) {

View File

@ -127,8 +127,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
nsDocShellLoadState* aLoadState, nsIChannel* aChannel);
UniquePtr<LoadingSessionHistoryInfo> ReplaceLoadingSessionHistoryEntryForLoad(
LoadingSessionHistoryInfo* aInfo, nsIChannel* aOldChannel,
nsIChannel* aNewChannel);
LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel);
already_AddRefed<Promise> Print(nsIPrintSettings* aPrintSettings,
ErrorResult& aRv);

View File

@ -791,11 +791,6 @@ nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
("nsDocShell[%p]: loading %s with flags 0x%08x", this,
aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
// Always clear mCheckingSessionHistory. MaybeHandleSubframeHistory uses it
// internally when querying session history information from the parent
// process.
mCheckingSessionHistory = false;
if ((!aLoadState->LoadIsFromSessionHistory() &&
!LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
LOAD_FLAGS_REPLACE_HISTORY)) ||

View File

@ -82,14 +82,14 @@ SessionHistoryInfo::SessionHistoryInfo(
}
SessionHistoryInfo::SessionHistoryInfo(
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aLoadType,
nsIChannel* aChannel, uint32_t aLoadType,
nsIPrincipal* aPartitionedPrincipalToInherit,
nsIContentSecurityPolicy* aCsp) {
aNewChannel->GetURI(getter_AddRefs(mURI));
aChannel->GetURI(getter_AddRefs(mURI));
mLoadType = aLoadType;
nsCOMPtr<nsILoadInfo> loadInfo;
aNewChannel->GetLoadInfo(getter_AddRefs(loadInfo));
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
loadInfo->GetResultPrincipalURI(getter_AddRefs(mResultPrincipalURI));
loadInfo->GetTriggeringPrincipal(
@ -100,16 +100,16 @@ SessionHistoryInfo::SessionHistoryInfo(
mSharedState.Get()->mPartitionedPrincipalToInherit =
aPartitionedPrincipalToInherit;
mSharedState.Get()->mCsp = aCsp;
aNewChannel->GetContentType(mSharedState.Get()->mContentType);
aOldChannel->GetOriginalURI(getter_AddRefs(mOriginalURI));
aChannel->GetContentType(mSharedState.Get()->mContentType);
aChannel->GetOriginalURI(getter_AddRefs(mOriginalURI));
uint32_t loadFlags;
aNewChannel->GetLoadFlags(&loadFlags);
aChannel->GetLoadFlags(&loadFlags);
mLoadReplace = !!(loadFlags & nsIChannel::LOAD_REPLACE);
MaybeUpdateTitleFromURI();
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel)) {
if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
mReferrerInfo = httpChannel->GetReferrerInfo();
}
}

View File

@ -48,8 +48,7 @@ class SessionHistoryInfo {
nsIPrincipal* aPartitionedPrincipalToInherit,
nsIContentSecurityPolicy* aCsp,
const nsACString& aContentType);
SessionHistoryInfo(nsIChannel* aOldChannel, nsIChannel* aNewChannel,
uint32_t aLoadType,
SessionHistoryInfo(nsIChannel* aChannel, uint32_t aLoadType,
nsIPrincipal* aPartitionedPrincipalToInherit,
nsIContentSecurityPolicy* aCsp);

View File

@ -7,7 +7,9 @@ support-files =
bug343515_pg3_1_1.html
bug343515_pg3_2.html
blank.html
redirect_to_blank.sjs
[browser_bug1757458.js]
[browser_test_bfcache_eviction.js]
[browser_bug343515.js]
[browser_test-content-chromeflags.js]

View File

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function() {
let testPage =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"view-source:http://example.com"
) + "redirect_to_blank.sjs";
let testPage2 = "data:text/html,<div>Second page</div>";
await BrowserTestUtils.withNewTab({ gBrowser, url: testPage }, async function(
browser
) {
await ContentTask.spawn(browser, [], async () => {
Assert.ok(
content.document.getElementById("viewsource").localName == "body",
"view-source document's body should have id='viewsource'."
);
content.document
.getElementById("viewsource")
.setAttribute("onunload", "/* disable bfcache*/");
});
BrowserTestUtils.loadURI(browser, testPage2);
await BrowserTestUtils.browserLoaded(browser);
let pageShownPromise = BrowserTestUtils.waitForContentEvent(
browser,
"pageshow",
true
);
browser.browsingContext.goBack();
await pageShownPromise;
await ContentTask.spawn(browser, [], async () => {
Assert.ok(
content.document.getElementById("viewsource").localName == "body",
"view-source document's body should have id='viewsource'."
);
});
});
});

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<script>
var onIframeOnload = function() {
var iframe = window.document.getElementById('applicationIframe');
opener.is(iframe.contentWindow.location.search, "?iframe", "Should have loaded the iframe");
window.close();
opener.SimpleTest.finish();
}
var onPageOnload = function() {
if (location.search == "?iframe") {
return;
}
if(!window.name) {
window.name = 'file_bug1758664.html';
window.location.reload();
return;
}
var iframe = window.document.getElementById('applicationIframe');
iframe.addEventListener('load', onIframeOnload);
iframe.src = "file_bug1758664.html?iframe";
}
window.document.addEventListener("DOMContentLoaded", onPageOnload);
</script>
</head>
<body>
<iframe id="applicationIframe"></iframe>
</body>
</html>

View File

@ -106,6 +106,9 @@ support-files =
cache_control_max_age_3600.sjs
[test_bug1750973.html]
support-files = file_bug1750973.html
[test_bug1758664.html]
support-files = file_bug1758664.html
skip-if = !sessionHistoryInParent # the old implementation behaves inconsistently
[test_bug270414.html]
[test_bug278916.html]
[test_bug279495.html]

View File

@ -0,0 +1,6 @@
function handleRequest(request, response) {
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Cache-Control", "no-store", false);
response.setStatusLine("1.1", 302, "Found");
response.setHeader("Location", "blank.html", false);
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Bug 1758664</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script>
SimpleTest.waitForExplicitFinish();
function test() {
window.open("file_bug1758664.html");
}
</script>
</head>
<body onload="test()">
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
</body>
</html>

View File

@ -2521,7 +2521,7 @@ nsDOMWindowUtils::GetVisitedDependentComputedStyle(
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_STATE(window && aElement);
nsCOMPtr<nsPIDOMWindowInner> innerWindow = window->GetCurrentInnerWindow();
NS_ENSURE_STATE(window);
NS_ENSURE_STATE(innerWindow);
nsCOMPtr<nsICSSDeclaration> decl;
{

View File

@ -488,7 +488,7 @@ class nsOuterWindowProxy : public MaybeCrossOriginObject<js::Wrapper> {
const char* className(JSContext* cx,
JS::Handle<JSObject*> wrapper) const override;
void finalize(JSFreeOp* fop, JSObject* proxy) const override;
void finalize(JS::GCContext* gcx, JSObject* proxy) const override;
size_t objectMoved(JSObject* proxy, JSObject* old) const override;
bool isCallable(JSObject* obj) const override { return false; }
@ -556,7 +556,7 @@ const char* nsOuterWindowProxy::className(JSContext* cx,
return "Window";
}
void nsOuterWindowProxy::finalize(JSFreeOp* fop, JSObject* proxy) const {
void nsOuterWindowProxy::finalize(JS::GCContext* gcx, JSObject* proxy) const {
nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy);
if (outerWindow) {
outerWindow->ClearWrapper(proxy);

View File

@ -2792,7 +2792,7 @@ bool ConvertJSValueToByteString(BindingCallContext& cx, JS::Handle<JS::Value> v,
return true;
}
void FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj) {
void FinalizeGlobal(JS::GCContext* aGcx, JSObject* aObj) {
MOZ_ASSERT(JS::GetClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
mozilla::dom::DestroyProtoAndIfaceCache(aObj);
}
@ -2825,25 +2825,20 @@ bool EnumerateGlobal(JSContext* aCx, JS::HandleObject aObj,
bool IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
uint32_t aNonExposedGlobals) {
MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
MOZ_ASSERT(
(aNonExposedGlobals & ~(GlobalNames::Window | GlobalNames::BackstagePass |
GlobalNames::DedicatedWorkerGlobalScope |
GlobalNames::SharedWorkerGlobalScope |
GlobalNames::ServiceWorkerGlobalScope |
GlobalNames::WorkerDebuggerGlobalScope |
GlobalNames::WorkletGlobalScope |
GlobalNames::AudioWorkletGlobalScope |
GlobalNames::PaintWorkletGlobalScope)) == 0,
"Unknown non-exposed global type");
MOZ_ASSERT((aNonExposedGlobals &
~(GlobalNames::Window | GlobalNames::DedicatedWorkerGlobalScope |
GlobalNames::SharedWorkerGlobalScope |
GlobalNames::ServiceWorkerGlobalScope |
GlobalNames::WorkerDebuggerGlobalScope |
GlobalNames::WorkletGlobalScope |
GlobalNames::AudioWorkletGlobalScope |
GlobalNames::PaintWorkletGlobalScope)) == 0,
"Unknown non-exposed global type");
const char* name = JS::GetClass(aGlobal)->name;
if ((aNonExposedGlobals & GlobalNames::Window) && !strcmp(name, "Window")) {
return true;
}
if ((aNonExposedGlobals & GlobalNames::BackstagePass) &&
!strcmp(name, "BackstagePass")) {
if ((aNonExposedGlobals & GlobalNames::Window) &&
(!strcmp(name, "Window") || !strcmp(name, "BackstagePass"))) {
return true;
}

View File

@ -2815,7 +2815,7 @@ class GetCCParticipant<T, true> {
static constexpr nsCycleCollectionParticipant* Get() { return nullptr; }
};
void FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj);
void FinalizeGlobal(JS::GCContext* aGcx, JSObject* aObj);
bool ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
JS::Handle<jsid> aId, bool* aResolvedp);

View File

@ -2120,7 +2120,7 @@ class CGGetWrapperCacheHook(CGAbstractClassHook):
)
def finalizeHook(descriptor, hookName, freeOp, obj):
def finalizeHook(descriptor, hookName, gcx, obj):
finalize = "JS::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj
if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
finalize += fill(
@ -2148,7 +2148,7 @@ def finalizeHook(descriptor, hookName, freeOp, obj):
if descriptor.wrapperCache:
finalize += "ClearWrapper(self, self, %s);\n" % obj
if descriptor.isGlobal():
finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (freeOp, obj)
finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (gcx, obj)
finalize += fill(
"""
if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) {
@ -2168,7 +2168,7 @@ class CGClassFinalizeHook(CGAbstractClassHook):
"""
def __init__(self, descriptor):
args = [Argument("JSFreeOp*", "fop"), Argument("JSObject*", "obj")]
args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "obj")]
CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, "void", args)
def generate_code(self):
@ -15637,7 +15637,7 @@ class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
class CGDOMJSProxyHandler_finalize(ClassMethod):
def __init__(self, descriptor):
args = [Argument("JSFreeOp*", "fop"), Argument("JSObject*", "proxy")]
args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "proxy")]
ClassMethod.__init__(
self, "finalize", "void", args, virtual=True, override=True, const=True
)

View File

@ -106,14 +106,13 @@ namespace GlobalNames {
// interfaces, not of the global names used to refer to them in IDL [Exposed]
// annotations.
static const uint32_t Window = 1u << 0;
static const uint32_t BackstagePass = 1u << 1;
static const uint32_t DedicatedWorkerGlobalScope = 1u << 2;
static const uint32_t SharedWorkerGlobalScope = 1u << 3;
static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
static const uint32_t WorkletGlobalScope = 1u << 6;
static const uint32_t AudioWorkletGlobalScope = 1u << 7;
static const uint32_t PaintWorkletGlobalScope = 1u << 8;
static const uint32_t DedicatedWorkerGlobalScope = 1u << 1;
static const uint32_t SharedWorkerGlobalScope = 1u << 2;
static const uint32_t ServiceWorkerGlobalScope = 1u << 3;
static const uint32_t WorkerDebuggerGlobalScope = 1u << 4;
static const uint32_t WorkletGlobalScope = 1u << 5;
static const uint32_t AudioWorkletGlobalScope = 1u << 6;
static const uint32_t PaintWorkletGlobalScope = 1u << 7;
} // namespace GlobalNames
struct PrefableDisablers {

View File

@ -138,7 +138,7 @@ class RemoteObjectProxyBase : public js::BaseProxyHandler,
template <class Native, const CrossOriginProperties& P>
class RemoteObjectProxy : public RemoteObjectProxyBase {
public:
void finalize(JSFreeOp* aFop, JSObject* aProxy) const final {
void finalize(JS::GCContext* aGcx, JSObject* aProxy) const final {
auto native = static_cast<Native*>(GetNative(aProxy));
RefPtr<Native> self(dont_AddRef(native));
}

View File

@ -43,7 +43,7 @@ NS_INTERFACE_MAP_END
static SimpleGlobalObject* GetSimpleGlobal(JSObject* global);
static void SimpleGlobal_finalize(JSFreeOp* fop, JSObject* obj) {
static void SimpleGlobal_finalize(JS::GCContext* gcx, JSObject* obj) {
SimpleGlobalObject* globalObject = GetSimpleGlobal(obj);
if (globalObject) {
globalObject->ClearWrapper(obj);

View File

@ -53,46 +53,42 @@ static ImageBitmapFormat GetImageBitmapFormatFromPlanarYCbCrData(
layers::PlanarYCbCrData const* aData) {
MOZ_ASSERT(aData);
auto ySize = aData->YDataSize();
auto cbcrSize = aData->CbCrDataSize();
media::Interval<uintptr_t> YInterval(
uintptr_t(aData->mYChannel),
uintptr_t(aData->mYChannel) + aData->mYSize.height * aData->mYStride),
CbInterval(uintptr_t(aData->mCbChannel),
uintptr_t(aData->mCbChannel) +
aData->mCbCrSize.height * aData->mCbCrStride),
CrInterval(uintptr_t(aData->mCrChannel),
uintptr_t(aData->mCrChannel) +
aData->mCbCrSize.height * aData->mCbCrStride);
uintptr_t(aData->mYChannel) + ySize.height * aData->mYStride),
CbInterval(
uintptr_t(aData->mCbChannel),
uintptr_t(aData->mCbChannel) + cbcrSize.height * aData->mCbCrStride),
CrInterval(
uintptr_t(aData->mCrChannel),
uintptr_t(aData->mCrChannel) + cbcrSize.height * aData->mCbCrStride);
if (aData->mYSkip == 0 && aData->mCbSkip == 0 &&
aData->mCrSkip == 0) { // Possibly three planes.
if (!YInterval.Intersects(CbInterval) &&
!CbInterval.Intersects(CrInterval)) { // Three planes.
if (aData->mYSize.height == aData->mCbCrSize.height) {
if (aData->mYSize.width == aData->mCbCrSize.width) {
switch (aData->mChromaSubsampling) {
case ChromaSubsampling::FULL:
return ImageBitmapFormat::YUV444P;
}
if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
case ChromaSubsampling::HALF_WIDTH:
return ImageBitmapFormat::YUV422P;
}
} else if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height) {
if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
case ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
return ImageBitmapFormat::YUV420P;
}
default:
break;
}
}
} else if (aData->mYSkip == 0 && aData->mCbSkip == 1 &&
aData->mCrSkip == 1) { // Possibly two planes.
} else if (aData->mYSkip == 0 && aData->mCbSkip == 1 && aData->mCrSkip == 1 &&
aData->mChromaSubsampling ==
ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) { // Possibly two
// planes.
if (!YInterval.Intersects(CbInterval) &&
aData->mCbChannel == aData->mCrChannel - 1) { // Two planes.
if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV420SP_NV12; // Y-Cb-Cr
}
return ImageBitmapFormat::YUV420SP_NV12; // Y-Cb-Cr
} else if (!YInterval.Intersects(CrInterval) &&
aData->mCrChannel == aData->mCbChannel - 1) { // Two planes.
if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
return ImageBitmapFormat::YUV420SP_NV21; // Y-Cr-Cb
}
return ImageBitmapFormat::YUV420SP_NV21; // Y-Cr-Cb
}
}

View File

@ -526,6 +526,11 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
return nullptr;
}
JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
if (realm && JS::GetDebuggerObservesWasm(realm)) {
r->SetSkipWasmCaching();
}
RefPtr<FetchObserver> observer;
if (aInit.mObserve.WasPassed()) {
observer = new FetchObserver(aGlobal, signalImpl);

View File

@ -825,7 +825,8 @@ nsresult FetchDriver::HttpFetch(
if (mRequest->GetIntegrity().IsEmpty()) {
MOZ_ASSERT(!FetchUtil::WasmAltDataType.IsEmpty());
nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
if (cic && StaticPrefs::javascript_options_wasm_caching()) {
if (cic && StaticPrefs::javascript_options_wasm_caching() &&
!mRequest->SkipWasmCaching()) {
cic->PreferAlternativeDataType(
FetchUtil::WasmAltDataType, nsLiteralCString(WASM_CONTENT_TYPE),
nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::

View File

@ -52,6 +52,7 @@ SafeRefPtr<InternalRequest> InternalRequest::GetRequestConstructorCopy(
copy->mContentPolicyTypeOverridden = mContentPolicyTypeOverridden;
copy->mPreferredAlternativeDataType = mPreferredAlternativeDataType;
copy->mSkipWasmCaching = mSkipWasmCaching;
return copy;
}
@ -134,6 +135,7 @@ InternalRequest::InternalRequest(const InternalRequest& aOther,
mMozErrors(aOther.mMozErrors),
mFragment(aOther.mFragment),
mSkipServiceWorker(aOther.mSkipServiceWorker),
mSkipWasmCaching(aOther.mSkipWasmCaching),
mSynchronous(aOther.mSynchronous),
mUnsafeRequest(aOther.mUnsafeRequest),
mUseURLCredentials(aOther.mUseURLCredentials),

View File

@ -209,6 +209,10 @@ class InternalRequest final : public AtomicSafeRefCounted<InternalRequest> {
void SetSkipServiceWorker() { mSkipServiceWorker = true; }
bool SkipWasmCaching() const { return mSkipWasmCaching; }
void SetSkipWasmCaching() { mSkipWasmCaching = true; }
bool IsSynchronous() const { return mSynchronous; }
RequestMode Mode() const { return mMode; }
@ -409,6 +413,7 @@ class InternalRequest final : public AtomicSafeRefCounted<InternalRequest> {
bool mMozErrors = false;
nsCString mFragment;
bool mSkipServiceWorker = false;
bool mSkipWasmCaching = false;
bool mSynchronous = false;
bool mUnsafeRequest = false;
bool mUseURLCredentials = false;

View File

@ -612,7 +612,7 @@ bool IndexedDatabaseManager::ExperimentalFeaturesEnabled(JSContext* aCx,
// actually going through IndexedDatabaseManager.
// See Bug 1198093 comment 14 for detailed explanation.
MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
if (IsNonExposedGlobal(aCx, aGlobal, GlobalNames::BackstagePass)) {
if (!strcmp(JS::GetClass(aGlobal)->name, "BackstagePass")) {
MOZ_ASSERT(NS_IsMainThread());
static bool featureRetrieved = false;
if (!featureRetrieved) {

View File

@ -262,22 +262,19 @@ PlanarYCbCrData ConstructPlanarYCbCrData(const VideoInfo& aInfo,
PlanarYCbCrData data;
data.mYChannel = Y.mData;
data.mYSize = IntSize(Y.mWidth, Y.mHeight);
data.mYStride = Y.mStride;
data.mYSkip = Y.mSkip;
data.mCbChannel = Cb.mData;
data.mCrChannel = Cr.mData;
data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
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.mPictureRect = aPicture;
data.mStereoMode = aInfo.mStereoMode;
data.mYUVColorSpace = aBuffer.mYUVColorSpace;
data.mColorDepth = aBuffer.mColorDepth;
data.mColorRange = aBuffer.mColorRange;
data.mChromaSubsampling = aBuffer.mChromaSubsampling;
return data;
}

View File

@ -421,6 +421,7 @@ class VideoData : public MediaData {
typedef gfx::ColorDepth ColorDepth;
typedef gfx::ColorRange ColorRange;
typedef gfx::YUVColorSpace YUVColorSpace;
typedef gfx::ChromaSubsampling ChromaSubsampling;
typedef layers::ImageContainer ImageContainer;
typedef layers::Image Image;
typedef layers::PlanarYCbCrImage PlanarYCbCrImage;
@ -445,6 +446,7 @@ class VideoData : public MediaData {
YUVColorSpace mYUVColorSpace = YUVColorSpace::Identity;
ColorDepth mColorDepth = ColorDepth::COLOR_8;
ColorRange mColorRange = ColorRange::LIMITED;
ChromaSubsampling mChromaSubsampling = ChromaSubsampling::FULL;
};
// Constructs a VideoData object. If aImage is nullptr, creates a new Image

View File

@ -57,12 +57,35 @@ LazyLogModule gMediaTrackGraphLog("MediaTrackGraph");
#endif // LOG
#define LOG(type, msg) MOZ_LOG(gMediaTrackGraphLog, type, msg)
namespace {
/**
* A hash table containing the graph instances, one per document.
*
* The key is a hash of nsPIDOMWindowInner, see `WindowToHash`.
* A hash table containing the graph instances, one per Window ID,
* sample rate, and device ID combination.
*/
static nsTHashMap<nsUint32HashKey, MediaTrackGraphImpl*> gGraphs;
class GraphKey final {
public:
GraphKey(uint64_t aWindowID, TrackRate aSampleRate,
CubebUtils::AudioDeviceID aOutputDeviceID)
: mWindowID(aWindowID),
mSampleRate(aSampleRate),
mOutputDeviceID(aOutputDeviceID) {}
GraphKey(const GraphKey&) = default;
~GraphKey() = default;
bool operator==(const GraphKey& b) const {
return mWindowID == b.mWindowID && mSampleRate == b.mSampleRate &&
mOutputDeviceID == b.mOutputDeviceID;
}
PLDHashNumber Hash() const {
return HashGeneric(mWindowID, mSampleRate, mOutputDeviceID);
}
private:
uint64_t mWindowID;
TrackRate mSampleRate;
CubebUtils::AudioDeviceID mOutputDeviceID;
};
nsTHashMap<nsGenericHashKey<GraphKey>, MediaTrackGraphImpl*> gGraphs;
} // anonymous namespace
MediaTrackGraphImpl::~MediaTrackGraphImpl() {
MOZ_ASSERT(mTracks.IsEmpty() && mSuspendedTracks.IsEmpty(),
@ -3142,50 +3165,44 @@ void MediaTrackGraphImpl::Destroy() {
mSelfRef = nullptr;
}
static uint32_t WindowToHash(nsPIDOMWindowInner* aWindow, TrackRate aSampleRate,
CubebUtils::AudioDeviceID aOutputDeviceID) {
uint32_t hashkey = 0;
// Internal method has a Window ID parameter so that TestAudioTrackGraph
// GTests can create a graph without a window.
/* static */
MediaTrackGraphImpl* MediaTrackGraphImpl::GetInstanceIfExists(
uint64_t aWindowID, TrackRate aSampleRate,
CubebUtils::AudioDeviceID aOutputDeviceID) {
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
hashkey = AddToHash(hashkey, aWindow);
hashkey = AddToHash(hashkey, aSampleRate);
hashkey = AddToHash(hashkey, aOutputDeviceID);
TrackRate sampleRate =
aSampleRate ? aSampleRate : CubebUtils::PreferredSampleRate();
GraphKey key(aWindowID, sampleRate, aOutputDeviceID);
return hashkey;
return gGraphs.Get(key);
}
// Public method has an nsPIDOMWindowInner* parameter to ensure that the
// window is a real inner Window, not a WindowProxy.
/* static */
MediaTrackGraph* MediaTrackGraph::GetInstanceIfExists(
nsPIDOMWindowInner* aWindow, TrackRate aSampleRate,
CubebUtils::AudioDeviceID aOutputDeviceID) {
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
TrackRate sampleRate =
aSampleRate ? aSampleRate : CubebUtils::PreferredSampleRate();
uint32_t hashkey = WindowToHash(aWindow, sampleRate, aOutputDeviceID);
return gGraphs.Get(hashkey);
return MediaTrackGraphImpl::GetInstanceIfExists(aWindow->WindowID(),
aSampleRate, aOutputDeviceID);
}
MediaTrackGraph* MediaTrackGraph::GetInstance(
MediaTrackGraph::GraphDriverType aGraphDriverRequested,
nsPIDOMWindowInner* aWindow, TrackRate aSampleRate,
CubebUtils::AudioDeviceID aOutputDeviceID) {
/* static */
MediaTrackGraphImpl* MediaTrackGraphImpl::GetInstance(
GraphDriverType aGraphDriverRequested, uint64_t aWindowID,
TrackRate aSampleRate, CubebUtils::AudioDeviceID aOutputDeviceID,
nsISerialEventTarget* aMainThread) {
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
TrackRate sampleRate =
aSampleRate ? aSampleRate : CubebUtils::PreferredSampleRate();
MediaTrackGraphImpl* graph = static_cast<MediaTrackGraphImpl*>(
GetInstanceIfExists(aWindow, sampleRate, aOutputDeviceID));
MediaTrackGraphImpl* graph =
GetInstanceIfExists(aWindowID, sampleRate, aOutputDeviceID);
if (!graph) {
nsISerialEventTarget* mainThread;
if (aWindow) {
mainThread =
aWindow->AsGlobal()->AbstractMainThreadFor(TaskCategory::Other);
} else {
// Uncommon case, only for some old configuration of webspeech.
mainThread = GetMainThreadSerialEventTarget();
}
GraphRunType runType = DIRECT_DRIVER;
if (aGraphDriverRequested != OFFLINE_THREAD_DRIVER &&
(StaticPrefs::dom_audioworklet_enabled() ||
@ -3200,18 +3217,27 @@ MediaTrackGraph* MediaTrackGraph::GetInstance(
uint32_t channelCount =
std::min<uint32_t>(8, CubebUtils::MaxNumberOfChannels());
graph = new MediaTrackGraphImpl(aGraphDriverRequested, runType, sampleRate,
channelCount, aOutputDeviceID, mainThread);
uint32_t hashkey = WindowToHash(aWindow, sampleRate, aOutputDeviceID);
gGraphs.InsertOrUpdate(hashkey, graph);
channelCount, aOutputDeviceID, aMainThread);
GraphKey key(aWindowID, sampleRate, aOutputDeviceID);
gGraphs.InsertOrUpdate(key, graph);
LOG(LogLevel::Debug,
("Starting up MediaTrackGraph %p for window %p", graph, aWindow));
("Starting up MediaTrackGraph %p for window 0x%" PRIx64, graph,
aWindowID));
}
return graph;
}
/* static */
MediaTrackGraph* MediaTrackGraph::GetInstance(
GraphDriverType aGraphDriverRequested, nsPIDOMWindowInner* aWindow,
TrackRate aSampleRate, CubebUtils::AudioDeviceID aOutputDeviceID) {
return MediaTrackGraphImpl::GetInstance(
aGraphDriverRequested, aWindow->WindowID(), aSampleRate, aOutputDeviceID,
aWindow->EventTargetFor(TaskCategory::Other));
}
MediaTrackGraph* MediaTrackGraph::CreateNonRealtimeInstance(
TrackRate aSampleRate, nsPIDOMWindowInner* aWindow) {
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");

View File

@ -119,7 +119,14 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
GraphRunType aRunTypeRequested,
TrackRate aSampleRate, uint32_t aChannelCount,
CubebUtils::AudioDeviceID aOutputDeviceID,
nsISerialEventTarget* aWindow);
nsISerialEventTarget* aMainThread);
static MediaTrackGraphImpl* GetInstance(
GraphDriverType aGraphDriverRequested, uint64_t aWindowID,
TrackRate aSampleRate, CubebUtils::AudioDeviceID aOutputDeviceID,
nsISerialEventTarget* aMainThread);
static MediaTrackGraphImpl* GetInstanceIfExists(
uint64_t aWindowID, TrackRate aSampleRate,
CubebUtils::AudioDeviceID aOutputDeviceID);
// Intended only for assertions, either on graph thread or not running (in
// which case we must be on the main thread).

View File

@ -19,7 +19,7 @@ static bool SetImageToBlackPixel(layers::PlanarYCbCrImage* aImage) {
data.mCbChannel = blackPixel + 1;
data.mCrChannel = blackPixel + 2;
data.mYStride = data.mCbCrStride = 1;
data.mPicSize = data.mYSize = data.mCbCrSize = gfx::IntSize(1, 1);
data.mPictureRect = gfx::IntRect(0, 0, 1, 1);
data.mYUVColorSpace = gfx::YUVColorSpace::BT601;
// This could be made FULL once bug 1568745 is complete. A black pixel being
// 0x00, 0x80, 0x80

View File

@ -52,35 +52,30 @@ already_AddRefed<Image> VideoFrame::CreateBlackImage(
return nullptr;
}
int len = ((aSize.width * aSize.height) * 3 / 2);
gfx::IntSize cbcrSize((aSize.width + 1) / 2, (aSize.height + 1) / 2);
int yLen = aSize.width * aSize.height;
int cbcrLen = cbcrSize.width * cbcrSize.height;
// Generate a black image.
auto frame = MakeUnique<uint8_t[]>(len);
int y = aSize.width * aSize.height;
auto frame = MakeUnique<uint8_t[]>(yLen + 2 * cbcrLen);
// Fill Y plane.
memset(frame.get(), 0x10, y);
memset(frame.get(), 0x10, yLen);
// Fill Cb/Cr planes.
memset(frame.get() + y, 0x80, (len - y));
const uint8_t lumaBpp = 8;
const uint8_t chromaBpp = 4;
memset(frame.get() + yLen, 0x80, 2 * cbcrLen);
layers::PlanarYCbCrData data;
data.mYChannel = frame.get();
data.mYSize = gfx::IntSize(aSize.width, aSize.height);
data.mYStride = (int32_t)(aSize.width * lumaBpp / 8.0);
data.mCbCrStride = (int32_t)(aSize.width * chromaBpp / 8.0);
data.mCbChannel = frame.get() + aSize.height * data.mYStride;
data.mCrChannel = data.mCbChannel + aSize.height * data.mCbCrStride / 2;
data.mCbCrSize = gfx::IntSize(aSize.width / 2, aSize.height / 2);
data.mPicX = 0;
data.mPicY = 0;
data.mPicSize = gfx::IntSize(aSize.width, aSize.height);
data.mYStride = aSize.width;
data.mCbCrStride = cbcrSize.width;
data.mCbChannel = frame.get() + yLen;
data.mCrChannel = data.mCbChannel + cbcrLen;
data.mPictureRect = gfx::IntRect(0, 0, aSize.width, aSize.height);
data.mStereoMode = StereoMode::MONO;
data.mYUVColorSpace = gfx::YUVColorSpace::BT601;
// This could be made FULL once bug 1568745 is complete. A black pixel being
// 0x00, 0x80, 0x80
data.mColorRange = gfx::ColorRange::LIMITED;
data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
// Copies data, so we can free data.
if (!image->CopyData(data)) {

View File

@ -960,6 +960,8 @@ already_AddRefed<VideoData> ChromiumCDMParent::CreateVideoFrame(
b.mPlanes[2].mStride = aFrame.mVPlane().mStride();
b.mPlanes[2].mSkip = 0;
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
// We unfortunately can't know which colorspace the video is using at this
// stage.
b.mYUVColorSpace =

View File

@ -124,25 +124,27 @@ TEST(TestAudioTrackGraph, DifferentDeviceIDs)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* g1 = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph* g1 = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
/*OutputDeviceID*/ nullptr);
/*OutputDeviceID*/ nullptr, GetMainThreadSerialEventTarget());
MediaTrackGraph* g2 = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph* g2 = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1));
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
GetMainThreadSerialEventTarget());
MediaTrackGraph* g1_2 = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph* g1_2 = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
/*OutputDeviceID*/ nullptr);
/*OutputDeviceID*/ nullptr, GetMainThreadSerialEventTarget());
MediaTrackGraph* g2_2 = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph* g2_2 = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1));
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
GetMainThreadSerialEventTarget());
EXPECT_NE(g1, g2) << "Different graphs due to different device ids";
EXPECT_EQ(g1, g1_2) << "Same graphs for same device ids";
@ -174,10 +176,11 @@ TEST(TestAudioTrackGraph, SetOutputDeviceID)
// Set the output device id in GetInstance method confirm that it is the one
// used in cubeb_stream_init.
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(2));
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(2),
GetMainThreadSerialEventTarget());
// Dummy track to make graph rolling. Add it and remove it to remove the
// graph from the global hash table and let it shutdown.
@ -200,9 +203,10 @@ TEST(TestAudioTrackGraph, NotifyDeviceStarted)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
RefPtr<SourceMediaTrack> dummySource;
Unused << WaitFor(Invoke([&] {
@ -231,9 +235,10 @@ TEST(TestAudioTrackGraph, ErrorCallback)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -298,9 +303,10 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack)
// Start on a system clock driver, then switch to full-duplex in one go. If we
// did output-then-full-duplex we'd risk a second NotifyWhenDeviceStarted
// resolving early after checking the first audio driver only.
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -389,8 +395,9 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput)
// as unexected discontinuities in the test.
const TrackRate rate = 48000;
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr, rate, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, rate, nullptr,
GetMainThreadSerialEventTarget());
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -543,9 +550,10 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -652,9 +660,10 @@ TEST(TestAudioTrackGraph, SetRequestedInputChannelCount)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
// Open a 2-channel input stream.
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -733,9 +742,10 @@ TEST(TestAudioTrackGraph, SwitchingDriverIfMaxChannelCountChanged)
auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
Unused << unforcer;
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
// Request a new input channel count and expect to have a new stream.
auto setNewChannelCount = [&](const RefPtr<AudioProcessingTrack>& aTrack,
@ -888,9 +898,10 @@ TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
// Set the input channel count of AudioInputProcessing, which will force
// MediaTrackGraph to re-evaluate input device, when the MediaTrackGraph is
@ -974,9 +985,10 @@ TEST(TestAudioTrackGraph, StartAudioDeviceBeforeStartingAudioProcessing)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
// Create a duplex AudioCallbackDriver
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -1042,9 +1054,10 @@ TEST(TestAudioTrackGraph, StopAudioProcessingBeforeStoppingAudioDevice)
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr,
GetMainThreadSerialEventTarget());
// Create a duplex AudioCallbackDriver
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
@ -1120,14 +1133,15 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
cubeb->SetStreamStartFreezeEnabled(true);
/* Primary graph: Create the graph. */
MediaTrackGraph* primary =
MediaTrackGraph::GetInstance(MediaTrackGraph::SYSTEM_THREAD_DRIVER,
/*window*/ nullptr, aInputRate, nullptr);
MediaTrackGraph* primary = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER,
/*Window ID*/ 1, aInputRate, nullptr, GetMainThreadSerialEventTarget());
/* Partner graph: Create the graph. */
MediaTrackGraph* partner = MediaTrackGraph::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*window*/ nullptr, aOutputRate,
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1));
MediaTrackGraph* partner = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, aOutputRate,
/*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
GetMainThreadSerialEventTarget());
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;

View File

@ -56,20 +56,20 @@ class MediaDataEncoderTest : public testing::Test {
int16_t mColorStep = 4;
void Init(const gfx::IntSize& aSize) {
mYUV.mPicSize = aSize;
mYUV.mPictureRect = gfx::IntRect(0, 0, aSize.width, aSize.height);
mYUV.mYStride = aSize.width;
mYUV.mYSize = aSize;
mYUV.mCbCrStride = aSize.width / 2;
mYUV.mCbCrSize = gfx::IntSize(aSize.width / 2, aSize.height / 2);
size_t bufferSize = mYUV.mYStride * mYUV.mYSize.height +
mYUV.mCbCrStride * mYUV.mCbCrSize.height +
mYUV.mCbCrStride * mYUV.mCbCrSize.height;
mYUV.mCbCrStride = (aSize.width + 1) / 2;
mYUV.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
auto ySize = mYUV.YDataSize();
auto cbcrSize = mYUV.CbCrDataSize();
size_t bufferSize =
mYUV.mYStride * ySize.height + 2 * mYUV.mCbCrStride * cbcrSize.height;
mBuffer = MakeUnique<uint8_t[]>(bufferSize);
std::fill_n(mBuffer.get(), bufferSize, 0x7F);
mYUV.mYChannel = mBuffer.get();
mYUV.mCbChannel = mYUV.mYChannel + mYUV.mYStride * mYUV.mYSize.height;
mYUV.mCrChannel =
mYUV.mCbChannel + mYUV.mCbCrStride * mYUV.mCbCrSize.height;
mYUV.mCbChannel = mYUV.mYChannel + mYUV.mYStride * ySize.height;
mYUV.mCrChannel = mYUV.mCbChannel + mYUV.mCbCrStride * cbcrSize.height;
mYUV.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
mRecycleBin = new layers::BufferRecycleBin();
}
@ -119,8 +119,8 @@ class MediaDataEncoderTest : public testing::Test {
}
void Draw(const size_t aIndex) {
DrawChessboard(mYUV.mYChannel, mYUV.mYSize.width, mYUV.mYSize.height,
aIndex << 1);
auto ySize = mYUV.YDataSize();
DrawChessboard(mYUV.mYChannel, ySize.width, ySize.height, aIndex << 1);
int16_t color = mYUV.mCbChannel[0] + mColorStep;
if (color > 255 || color < 0) {
mColorStep = -mColorStep;

View File

@ -46,7 +46,7 @@ Image* YUVBufferGenerator::CreateI420Image() {
PlanarYCbCrImage* image =
new RecyclingPlanarYCbCrImage(new BufferRecycleBin());
PlanarYCbCrData data;
data.mPicSize = mImageSize;
data.mPictureRect = gfx::IntRect(0, 0, mImageSize.width, mImageSize.height);
const uint32_t yPlaneSize = mImageSize.width * mImageSize.height;
const uint32_t halfWidth = (mImageSize.width + 1) / 2;
@ -56,8 +56,6 @@ Image* YUVBufferGenerator::CreateI420Image() {
// Y plane.
uint8_t* y = mSourceBuffer.Elements();
data.mYChannel = y;
data.mYSize.width = mImageSize.width;
data.mYSize.height = mImageSize.height;
data.mYStride = mImageSize.width;
data.mYSkip = 0;
@ -73,10 +71,9 @@ Image* YUVBufferGenerator::CreateI420Image() {
// CrCb plane vectors.
data.mCbCrStride = halfWidth;
data.mCbCrSize.width = halfWidth;
data.mCbCrSize.height = halfHeight;
data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
data.mYUVColorSpace = DefaultColorSpace(data.mYSize);
data.mYUVColorSpace = DefaultColorSpace(mImageSize);
image->CopyData(data);
return image;
@ -85,17 +82,13 @@ Image* YUVBufferGenerator::CreateI420Image() {
Image* YUVBufferGenerator::CreateNV12Image() {
NVImage* image = new NVImage();
PlanarYCbCrData data;
data.mPicSize = mImageSize;
data.mPictureRect = gfx::IntRect(0, 0, mImageSize.width, mImageSize.height);
const uint32_t yPlaneSize = mImageSize.width * mImageSize.height;
const uint32_t halfWidth = (mImageSize.width + 1) / 2;
const uint32_t halfHeight = (mImageSize.height + 1) / 2;
// Y plane.
uint8_t* y = mSourceBuffer.Elements();
data.mYChannel = y;
data.mYSize.width = mImageSize.width;
data.mYSize.height = mImageSize.height;
data.mYStride = mImageSize.width;
data.mYSkip = 0;
@ -111,8 +104,7 @@ Image* YUVBufferGenerator::CreateNV12Image() {
// 4:2:0.
data.mCbCrStride = mImageSize.width;
data.mCbCrSize.width = halfWidth;
data.mCbCrSize.height = halfHeight;
data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
image->SetData(data);
return image;
@ -121,17 +113,13 @@ Image* YUVBufferGenerator::CreateNV12Image() {
Image* YUVBufferGenerator::CreateNV21Image() {
NVImage* image = new NVImage();
PlanarYCbCrData data;
data.mPicSize = mImageSize;
data.mPictureRect = gfx::IntRect(0, 0, mImageSize.width, mImageSize.height);
const uint32_t yPlaneSize = mImageSize.width * mImageSize.height;
const uint32_t halfWidth = (mImageSize.width + 1) / 2;
const uint32_t halfHeight = (mImageSize.height + 1) / 2;
// Y plane.
uint8_t* y = mSourceBuffer.Elements();
data.mYChannel = y;
data.mYSize.width = mImageSize.width;
data.mYSize.height = mImageSize.height;
data.mYStride = mImageSize.width;
data.mYSkip = 0;
@ -147,10 +135,9 @@ Image* YUVBufferGenerator::CreateNV21Image() {
// 4:2:0.
data.mCbCrStride = mImageSize.width;
data.mCbCrSize.width = halfWidth;
data.mCbCrSize.height = halfHeight;
data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
data.mYUVColorSpace = DefaultColorSpace(data.mYSize);
data.mYUVColorSpace = DefaultColorSpace(mImageSize);
image->SetData(data);
return image;

View File

@ -59,20 +59,16 @@ already_AddRefed<Image> RemoteImageHolder::DeserializeImage(
}
PlanarYCbCrData pData;
pData.mYSize = descriptor.ySize();
pData.mYStride = descriptor.yStride();
pData.mCbCrSize = descriptor.cbCrSize();
pData.mCbCrStride = descriptor.cbCrStride();
// default mYSkip, mCbSkip, mCrSkip because not held in YCbCrDescriptor
pData.mYSkip = pData.mCbSkip = pData.mCrSkip = 0;
gfx::IntRect display = descriptor.display();
pData.mPicX = display.X();
pData.mPicY = display.Y();
pData.mPicSize = display.Size();
pData.mPictureRect = descriptor.display();
pData.mStereoMode = descriptor.stereoMode();
pData.mColorDepth = descriptor.colorDepth();
pData.mYUVColorSpace = descriptor.yUVColorSpace();
pData.mColorRange = descriptor.colorRange();
pData.mChromaSubsampling = descriptor.chromaSubsampling();
pData.mYChannel = ImageDataSerializer::GetYChannel(buffer, descriptor);
pData.mCbChannel = ImageDataSerializer::GetCbChannel(buffer, descriptor);
pData.mCrChannel = ImageDataSerializer::GetCrChannel(buffer, descriptor);

View File

@ -48,7 +48,7 @@ static void SetImageToGreenPixel(PlanarYCbCrImage* aImage) {
data.mCbChannel = greenPixel + 1;
data.mCrChannel = greenPixel + 2;
data.mYStride = data.mCbCrStride = 1;
data.mPicSize = data.mYSize = data.mCbCrSize = gfx::IntSize(1, 1);
data.mPictureRect = gfx::IntRect(0, 0, 1, 1);
data.mYUVColorSpace = gfx::YUVColorSpace::BT601;
aImage->CopyData(data);
}

View File

@ -167,6 +167,8 @@ RefPtr<MediaDataDecoder::DecodePromise> AOMDecoder::ProcessDecode(
b.mPlanes[2].mSkip = 0;
if (img->fmt == AOM_IMG_FMT_I420 || img->fmt == AOM_IMG_FMT_I42016) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;

View File

@ -67,6 +67,7 @@ already_AddRefed<MediaData> BlankVideoDataCreator::Create(
buffer.mPlanes[2].mWidth = (mFrameWidth + 1) / 2;
buffer.mPlanes[2].mSkip = 0;
buffer.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
buffer.mYUVColorSpace = gfx::YUVColorSpace::BT601;
return VideoData::CreateAndCopyData(mInfo, mImageContainer, aSample->mOffset,

View File

@ -237,6 +237,12 @@ already_AddRefed<VideoData> DAV1DDecoder::ConstructImage(
b.mPlanes[2].mHeight = (aPicture.p.h + ss_ver) >> ss_ver;
b.mPlanes[2].mWidth = (aPicture.p.w + ss_hor) >> ss_hor;
if (ss_ver) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
} else if (ss_hor) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH;
}
// Timestamp, duration and offset used here are wrong.
// We need to take those values from the decoder. Latest
// dav1d version allows for that.

View File

@ -171,6 +171,12 @@ RefPtr<MediaDataDecoder::DecodePromise> TheoraDecoder::ProcessDecode(
b.mPlanes[2].mWidth = mTheoraInfo.frame_width >> hdec;
b.mPlanes[2].mSkip = 0;
if (vdec) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
} else if (hdec) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH;
}
b.mYUVColorSpace =
DefaultColorSpace({mTheoraInfo.frame_width, mTheoraInfo.frame_height});

View File

@ -164,6 +164,8 @@ RefPtr<MediaDataDecoder::DecodePromise> VPXDecoder::ProcessDecode(
b.mPlanes[2].mSkip = 0;
if (img->fmt == VPX_IMG_FMT_I420) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;

View File

@ -56,6 +56,7 @@ void GMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame) {
b.mPlanes[i].mSkip = 0;
}
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
b.mYUVColorSpace =
DefaultColorSpace({decodedFrame->Width(), decodedFrame->Height()});

View File

@ -172,20 +172,22 @@ static jni::ByteBuffer::LocalRef ConvertI420ToNV12Buffer(
const PlanarYCbCrImage* image = aSample->mImage->AsPlanarYCbCrImage();
MOZ_ASSERT(image);
const PlanarYCbCrData* yuv = image->GetData();
size_t ySize = yuv->mYStride * yuv->mYSize.height;
size_t size = ySize + (yuv->mCbCrStride * yuv->mCbCrSize.height * 2);
if (!aYUVBuffer || aYUVBuffer->Capacity() < size) {
aYUVBuffer = MakeRefPtr<MediaByteBuffer>(size);
aYUVBuffer->SetLength(size);
auto ySize = yuv->YDataSize();
auto cbcrSize = yuv->CbCrDataSize();
size_t yLength = yuv->mYStride * ySize.height;
size_t length = yLength + (yuv->mCbCrStride * cbcrSize.height * 2);
if (!aYUVBuffer || aYUVBuffer->Capacity() < length) {
aYUVBuffer = MakeRefPtr<MediaByteBuffer>(length);
aYUVBuffer->SetLength(length);
} else {
MOZ_ASSERT(aYUVBuffer->Length() >= size);
MOZ_ASSERT(aYUVBuffer->Length() >= length);
}
if (libyuv::I420ToNV12(yuv->mYChannel, yuv->mYStride, yuv->mCbChannel,
yuv->mCbCrStride, yuv->mCrChannel, yuv->mCbCrStride,
aYUVBuffer->Elements(), yuv->mYStride,
aYUVBuffer->Elements() + ySize, yuv->mCbCrStride * 2,
yuv->mYSize.width, yuv->mYSize.height) != 0) {
aYUVBuffer->Elements() + yLength, yuv->mCbCrStride * 2,
ySize.width, ySize.height) != 0) {
return nullptr;
}

View File

@ -412,6 +412,7 @@ void AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage,
buffer.mPlanes[2].mHeight = (height + 1) / 2;
buffer.mPlanes[2].mSkip = 0;
buffer.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
buffer.mYUVColorSpace = mColorSpace;
buffer.mColorRange = mColorRange;

View File

@ -518,6 +518,11 @@ CVPixelBufferRef AppleVTEncoder::CreateCVPixelBuffer(const Image* aSource) {
OSType format = MapPixelFormat(mConfig.mSourcePixelFormat).ref();
size_t numPlanes = NumberOfPlanes(mConfig.mSourcePixelFormat);
const PlanarYCbCrImage::Data* yuv = image->GetData();
if (!yuv) {
return nullptr;
}
auto ySize = yuv->YDataSize();
auto cbcrSize = yuv->CbCrDataSize();
void* addresses[3] = {};
size_t widths[3] = {};
size_t heights[3] = {};
@ -525,20 +530,20 @@ CVPixelBufferRef AppleVTEncoder::CreateCVPixelBuffer(const Image* aSource) {
switch (numPlanes) {
case 3:
addresses[2] = yuv->mCrChannel;
widths[2] = yuv->mCbCrSize.width;
heights[2] = yuv->mCbCrSize.height;
widths[2] = cbcrSize.width;
heights[2] = cbcrSize.height;
strides[2] = yuv->mCbCrStride;
[[fallthrough]];
case 2:
addresses[1] = yuv->mCbChannel;
widths[1] = yuv->mCbCrSize.width;
heights[1] = yuv->mCbCrSize.height;
widths[1] = cbcrSize.width;
heights[1] = cbcrSize.height;
strides[1] = yuv->mCbCrStride;
[[fallthrough]];
case 1:
addresses[0] = yuv->mYChannel;
widths[0] = yuv->mYSize.width;
heights[0] = yuv->mYSize.height;
widths[0] = ySize.width;
heights[0] = ySize.height;
strides[0] = yuv->mYStride;
break;
default:
@ -548,9 +553,9 @@ CVPixelBufferRef AppleVTEncoder::CreateCVPixelBuffer(const Image* aSource) {
CVPixelBufferRef buffer = nullptr;
image->AddRef(); // Grip input buffers.
CVReturn rv = CVPixelBufferCreateWithPlanarBytes(
kCFAllocatorDefault, yuv->mPicSize.width, yuv->mPicSize.height, format,
nullptr /* dataPtr */, 0 /* dataSize */, numPlanes, addresses, widths,
heights, strides, ReleaseImage /* releaseCallback */,
kCFAllocatorDefault, yuv->mPictureRect.width, yuv->mPictureRect.height,
format, nullptr /* dataPtr */, 0 /* dataSize */, numPlanes, addresses,
widths, heights, strides, ReleaseImage /* releaseCallback */,
image /* releaseRefCon */, nullptr /* pixelBufferAttributes */, &buffer);
if (rv == kCVReturnSuccess) {
return buffer;

View File

@ -501,20 +501,8 @@ static bool IsYUV420Sampling(const AVPixelFormat& aFormat) {
}
layers::TextureClient*
FFmpegVideoDecoder<LIBAV_VER>::AllocateTextueClientForImage(
FFmpegVideoDecoder<LIBAV_VER>::AllocateTextureClientForImage(
struct AVCodecContext* aCodecContext, PlanarYCbCrImage* aImage) {
layers::PlanarYCbCrData data =
CreateEmptyPlanarYCbCrData(aCodecContext, mInfo);
// Allocate a shmem buffer for image.
if (!aImage->CreateEmptyBuffer(data)) {
return nullptr;
}
return aImage->GetTextureClient(mImageAllocator);
}
layers::PlanarYCbCrData
FFmpegVideoDecoder<LIBAV_VER>::CreateEmptyPlanarYCbCrData(
struct AVCodecContext* aCodecContext, const VideoInfo& aInfo) {
MOZ_ASSERT(
IsColorFormatSupportedForUsingCustomizedBuffer(aCodecContext->pix_fmt));
@ -544,42 +532,39 @@ FFmpegVideoDecoder<LIBAV_VER>::CreateEmptyPlanarYCbCrData(
// plane Cb/Cr
// width 1280 height 720 -> adjusted-width 1280 adjusted-height 736
layers::PlanarYCbCrData data;
auto paddedYSize =
const auto yDims =
gfx::IntSize{aCodecContext->coded_width, aCodecContext->coded_height};
auto paddedYSize = yDims;
mLib->avcodec_align_dimensions(aCodecContext, &paddedYSize.width,
&paddedYSize.height);
data.mYSize = gfx::IntSize{paddedYSize.Width(), paddedYSize.Height()};
data.mYStride = data.mYSize.Width() * bytesPerChannel;
data.mYStride = paddedYSize.Width() * bytesPerChannel;
MOZ_ASSERT(
IsColorFormatSupportedForUsingCustomizedBuffer(aCodecContext->pix_fmt));
const auto yDims =
gfx::IntSize{aCodecContext->coded_width, aCodecContext->coded_height};
auto uvDims = yDims;
if (IsYUV420Sampling(aCodecContext->pix_fmt)) {
uvDims.width = (uvDims.width + 1) / 2;
uvDims.height = (uvDims.height + 1) / 2;
data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
}
auto paddedCbCrSize = uvDims;
mLib->avcodec_align_dimensions(aCodecContext, &paddedCbCrSize.width,
&paddedCbCrSize.height);
data.mCbCrSize =
gfx::IntSize{paddedCbCrSize.Width(), paddedCbCrSize.Height()};
data.mCbCrStride = data.mCbCrSize.Width() * bytesPerChannel;
data.mCbCrStride = paddedCbCrSize.Width() * bytesPerChannel;
// Setting other attributes
data.mPicSize = gfx::IntSize{aCodecContext->width, aCodecContext->height};
const gfx::IntRect picture =
aInfo.ScaledImageRect(data.mPicSize.Width(), data.mPicSize.Height());
data.mPicX = picture.x;
data.mPicY = picture.y;
data.mStereoMode = aInfo.mStereoMode;
data.mPictureRect = gfx::IntRect(
mInfo.ScaledImageRect(aCodecContext->width, aCodecContext->height)
.TopLeft(),
gfx::IntSize(aCodecContext->width, aCodecContext->height));
data.mStereoMode = mInfo.mStereoMode;
if (aCodecContext->colorspace != AVCOL_SPC_UNSPECIFIED) {
data.mYUVColorSpace =
TransferAVColorSpaceToYUVColorSpace(aCodecContext->colorspace);
} else {
data.mYUVColorSpace = aInfo.mColorSpace ? *aInfo.mColorSpace
: DefaultColorSpace(data.mPicSize);
data.mYUVColorSpace = mInfo.mColorSpace
? *mInfo.mColorSpace
: DefaultColorSpace(data.mPictureRect.Size());
}
data.mColorDepth = GetColorDepth(aCodecContext->pix_fmt);
data.mColorRange = aCodecContext->color_range == AVCOL_RANGE_JPEG
@ -588,10 +573,16 @@ FFmpegVideoDecoder<LIBAV_VER>::CreateEmptyPlanarYCbCrData(
FFMPEG_LOGV(
"Created plane data, YSize=(%d, %d), CbCrSize=(%d, %d), "
"CroppedYSize=(%d, %d), CroppedCbCrSize=(%d, %d), ColorDepth=%hhu",
data.mYSize.Width(), data.mYSize.Height(), data.mCbCrSize.Width(),
data.mCbCrSize.Height(), data.mPicSize.Width(), data.mPicSize.Height(),
uvDims.Width(), uvDims.Height(), static_cast<uint8_t>(data.mColorDepth));
return data;
paddedYSize.Width(), paddedYSize.Height(), paddedCbCrSize.Width(),
paddedCbCrSize.Height(), data.YPictureSize().Width(),
data.YPictureSize().Height(), data.CbCrPictureSize().Width(),
data.CbCrPictureSize().Height(), static_cast<uint8_t>(data.mColorDepth));
// Allocate a shmem buffer for image.
if (!aImage->CreateEmptyBuffer(data, paddedYSize, paddedCbCrSize)) {
return nullptr;
}
return aImage->GetTextureClient(mImageAllocator);
}
int FFmpegVideoDecoder<LIBAV_VER>::GetVideoBuffer(
@ -655,7 +646,7 @@ int FFmpegVideoDecoder<LIBAV_VER>::GetVideoBuffer(
}
RefPtr<layers::TextureClient> texture =
AllocateTextueClientForImage(aCodecContext, image);
AllocateTextureClientForImage(aCodecContext, image);
if (!texture) {
FFMPEG_LOG("Failed to allocate a texture client");
return AVERROR(EINVAL);
@ -999,6 +990,7 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER>::CreateImage(
|| mCodecContext->pix_fmt == AV_PIX_FMT_YUV422P12LE
#endif
) {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH;
b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height;
if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV422P10LE) {
@ -1010,6 +1002,7 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER>::CreateImage(
}
#endif
} else {
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
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) {

View File

@ -107,12 +107,9 @@ class FFmpegVideoDecoder<LIBAV_VER>
}
#if LIBAVCODEC_VERSION_MAJOR >= 57 && LIBAVUTIL_VERSION_MAJOR >= 56
layers::TextureClient* AllocateTextueClientForImage(
layers::TextureClient* AllocateTextureClientForImage(
struct AVCodecContext* aCodecContext, layers::PlanarYCbCrImage* aImage);
layers::PlanarYCbCrData CreateEmptyPlanarYCbCrData(
struct AVCodecContext* aCodecContext, const VideoInfo& aInfo);
gfx::IntSize GetAlignmentVideoFrameSize(struct AVCodecContext* aCodecContext,
int32_t aWidth,
int32_t aHeight) const;
@ -177,7 +174,7 @@ class FFmpegVideoDecoder<LIBAV_VER>
// its internal decoding queue.
//
// When an image is removed from mAllocatedImages it's recycled
// for a new frame by AllocateTextueClientForImage() in
// for a new frame by AllocateTextureClientForImage() in
// FFmpegVideoDecoder::GetVideoBuffer().
nsTHashSet<RefPtr<ImageBufferWrapper>> mAllocatedImages;
#endif

View File

@ -937,6 +937,8 @@ already_AddRefed<VideoData> MediaDataHelper::CreateYUV420VideoData(
b.mPlanes[2].mStride = (stride + 1) / 2;
b.mPlanes[2].mSkip = 0;
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
VideoInfo info(*mTrackInfo->GetAsVideoInfo());
auto maybeColorSpace = info.mColorSpace;

View File

@ -278,8 +278,10 @@ already_AddRefed<IMFSample> WMFMediaDataEncoder<T>::ConvertToNV12InputSample(
const PlanarYCbCrImage* image = aData->mImage->AsPlanarYCbCrImage();
MOZ_ASSERT(image);
const PlanarYCbCrData* yuv = image->GetData();
size_t yLength = yuv->mYStride * yuv->mYSize.height;
size_t length = yLength + (yuv->mCbCrStride * yuv->mCbCrSize.height * 2);
auto ySize = yuv->YDataSize();
auto cbcrSize = yuv->CbCrDataSize();
size_t yLength = yuv->mYStride * ySize.height;
size_t length = yLength + (yuv->mCbCrStride * cbcrSize.height * 2);
RefPtr<IMFSample> input;
HRESULT hr = mEncoder->CreateInputSample(&input, length);
@ -295,12 +297,11 @@ already_AddRefed<IMFSample> WMFMediaDataEncoder<T>::ConvertToNV12InputSample(
LockBuffer lockBuffer(buffer);
NS_ENSURE_TRUE(SUCCEEDED(lockBuffer.Result()), nullptr);
bool ok =
libyuv::I420ToNV12(yuv->mYChannel, yuv->mYStride, yuv->mCbChannel,
yuv->mCbCrStride, yuv->mCrChannel, yuv->mCbCrStride,
lockBuffer.Data(), yuv->mYStride,
lockBuffer.Data() + yLength, yuv->mCbCrStride * 2,
yuv->mYSize.width, yuv->mYSize.height) == 0;
bool ok = libyuv::I420ToNV12(
yuv->mYChannel, yuv->mYStride, yuv->mCbChannel,
yuv->mCbCrStride, yuv->mCrChannel, yuv->mCbCrStride,
lockBuffer.Data(), yuv->mYStride, lockBuffer.Data() + yLength,
yuv->mCbCrStride * 2, ySize.width, ySize.height) == 0;
NS_ENSURE_TRUE(ok, nullptr);
hr = input->SetSampleTime(UsecsToHNs(aData->mTime.ToMicroseconds()));

View File

@ -682,6 +682,8 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
b.mPlanes[2].mSkip = 1;
}
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
// YuvColorSpace
b.mYUVColorSpace =
mColorSpace.refOr(DefaultColorSpace({videoWidth, videoHeight}));

View File

@ -25,7 +25,7 @@
subsuite = media
skip-if =
(os == "win" && processor == "aarch64") # aarch64 due to 1536604
os == "linux" && (asan || debug) # Bug 1668452/1476870: common fatal error (shutdown hang) on asan/debug
os == "linux" && (asan || debug) # Bug 1476870: common fatal error (shutdown hang) on asan/debug
support-files =
16bit_wave_extrametadata.wav
16bit_wave_extrametadata.wav^headers^

View File

@ -259,17 +259,14 @@ static void AllocateSolidColorFrame(layers::PlanarYCbCrData& aData, int aWidth,
memset(frame + yLen + cbLen, aCr, crLen);
aData.mYChannel = frame;
aData.mYSize = IntSize(aWidth, aHeight);
aData.mYStride = aWidth;
aData.mCbCrStride = aWidth >> 1;
aData.mCbChannel = frame + yLen;
aData.mCrChannel = aData.mCbChannel + cbLen;
aData.mCbCrSize = IntSize(aWidth >> 1, aHeight >> 1);
aData.mPicX = 0;
aData.mPicY = 0;
aData.mPicSize = IntSize(aWidth, aHeight);
aData.mPictureRect = IntRect(0, 0, aWidth, aHeight);
aData.mStereoMode = StereoMode::MONO;
aData.mYUVColorSpace = gfx::YUVColorSpace::BT601;
aData.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
}
static void ReleaseFrame(layers::PlanarYCbCrData& aData) {

View File

@ -496,18 +496,14 @@ int MediaEngineRemoteVideoSource::DeliverFrame(
layers::PlanarYCbCrData data;
data.mYChannel = const_cast<uint8_t*>(buffer->DataY());
data.mYSize = gfx::IntSize(buffer->width(), buffer->height());
data.mYStride = buffer->StrideY();
MOZ_ASSERT(buffer->StrideU() == buffer->StrideV());
data.mCbCrStride = buffer->StrideU();
data.mCbChannel = const_cast<uint8_t*>(buffer->DataU());
data.mCrChannel = const_cast<uint8_t*>(buffer->DataV());
data.mCbCrSize =
gfx::IntSize((buffer->width() + 1) / 2, (buffer->height() + 1) / 2);
data.mPicX = 0;
data.mPicY = 0;
data.mPicSize = gfx::IntSize(buffer->width(), buffer->height());
data.mPictureRect = gfx::IntRect(0, 0, buffer->width(), buffer->height());
data.mYUVColorSpace = gfx::YUVColorSpace::BT601;
data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
RefPtr<layers::PlanarYCbCrImage> image =
mImageContainer->CreatePlanarYCbCrImage();

View File

@ -29,11 +29,11 @@ class ImageBuffer : public webrtc::VideoFrameBuffer {
}
const layers::PlanarYCbCrData* data = image->GetData();
rtc::scoped_refptr<webrtc::I420BufferInterface> buf =
webrtc::WrapI420Buffer(data->mPicSize.width, data->mPicSize.height,
data->mYChannel, data->mYStride,
data->mCbChannel, data->mCbCrStride,
data->mCrChannel, data->mCbCrStride,
rtc::KeepRefUntilDone(image.get()));
webrtc::WrapI420Buffer(
data->mPictureRect.width, data->mPictureRect.height,
data->mYChannel, data->mYStride, data->mCbChannel,
data->mCbCrStride, data->mCrChannel, data->mCbCrStride,
rtc::KeepRefUntilDone(image.get()));
return buf;
}

View File

@ -289,14 +289,13 @@ static already_AddRefed<VideoData> CreateVideoDataFromWebrtcVideoFrame(
PlanarYCbCrData yCbCrData;
yCbCrData.mYChannel = const_cast<uint8_t*>(i420->DataY());
yCbCrData.mYSize = gfx::IntSize(i420->width(), i420->height());
yCbCrData.mYStride = i420->StrideY();
yCbCrData.mCbChannel = const_cast<uint8_t*>(i420->DataU());
yCbCrData.mCrChannel = const_cast<uint8_t*>(i420->DataV());
yCbCrData.mCbCrSize = gfx::IntSize(i420->ChromaWidth(), i420->ChromaHeight());
MOZ_ASSERT(i420->StrideU() == i420->StrideV());
yCbCrData.mCbCrStride = i420->StrideU();
yCbCrData.mPicSize = gfx::IntSize(i420->width(), i420->height());
yCbCrData.mPictureRect = gfx::IntRect(0, 0, i420->width(), i420->height());
yCbCrData.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
RefPtr<PlanarYCbCrImage> image =
new RecyclingPlanarYCbCrImage(new BufferRecycleBin());

View File

@ -1518,20 +1518,17 @@ class MediaPipelineReceiveVideo::PipelineListener
PlanarYCbCrData yuvData;
yuvData.mYChannel = const_cast<uint8_t*>(i420->DataY());
yuvData.mYSize = IntSize(i420->width(), i420->height());
yuvData.mYStride = i420->StrideY();
MOZ_ASSERT(i420->StrideU() == i420->StrideV());
yuvData.mCbCrStride = i420->StrideU();
yuvData.mCbChannel = const_cast<uint8_t*>(i420->DataU());
yuvData.mCrChannel = const_cast<uint8_t*>(i420->DataV());
yuvData.mCbCrSize =
IntSize((i420->width() + 1) >> 1, (i420->height() + 1) >> 1);
yuvData.mPicX = 0;
yuvData.mPicY = 0;
yuvData.mPicSize = IntSize(i420->width(), i420->height());
yuvData.mPictureRect = IntRect(0, 0, i420->width(), i420->height());
yuvData.mStereoMode = StereoMode::MONO;
// This isn't the best default.
yuvData.mYUVColorSpace = gfx::YUVColorSpace::BT601;
yuvData.mChromaSubsampling =
gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
if (!yuvImage->CopyData(yuvData)) {
MOZ_ASSERT(false);

View File

@ -59,7 +59,10 @@ MIDIPermissionRequest::GetTypes(nsIArray** aTypes) {
NS_IMETHODIMP
MIDIPermissionRequest::Cancel() {
mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
mPromise->MaybeRejectWithSecurityError(
"WebMIDI requires a site permission add-on to activate — see "
"https://extensionworkshop.com/documentation/publish/"
"site-permission-add-on/ for details.");
return NS_OK;
}

View File

@ -4317,31 +4317,17 @@ SplitNodeResult HTMLEditor::SplitNodeWithTransaction(
!ignoredError.Failed(),
"OnStartToHandleTopLevelEditSubAction() failed, but ignored");
// XXX Unfortunately, storing offset of the split point in
// SplitNodeTransaction is necessary for now. We should fix this
// in a follow up bug.
Unused << aStartOfRightNode.Offset();
RefPtr<SplitNodeTransaction> transaction =
SplitNodeTransaction::Create(*this, aStartOfRightNode);
nsresult rv = DoTransactionInternal(transaction);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DoTransactionInternal() failed");
nsCOMPtr<nsIContent> newLeftContent = transaction->GetNewLeftContent();
NS_WARNING_ASSERTION(newLeftContent, "Failed to create a new left node");
if (newLeftContent) {
// XXX Some other transactions manage range updater by themselves.
// Why doesn't SplitNodeTransaction do it?
DebugOnly<nsresult> rvIgnored = RangeUpdaterRef().SelAdjSplitNode(
*aStartOfRightNode.ContainerAsContent(), aStartOfRightNode.Offset(),
*newLeftContent, SplitNodeDirection::LeftNodeIsNewOne);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"RangeUpdater::SelAdjSplitNode() failed, but ignored");
nsCOMPtr<nsIContent> newContent = transaction->GetNewContent();
nsCOMPtr<nsIContent> splitContent = transaction->GetSplitContent();
if (MOZ_LIKELY(NS_SUCCEEDED(rv) && newContent && splitContent)) {
TopLevelEditSubActionDataRef().DidSplitContent(
*this, *aStartOfRightNode.ContainerAsContent(), *newLeftContent,
*this, *splitContent, *newContent,
SplitNodeDirection::LeftNodeIsNewOne);
}
@ -4353,10 +4339,9 @@ SplitNodeResult HTMLEditor::SplitNodeWithTransaction(
return SplitNodeResult(rv);
}
MOZ_ASSERT(newLeftContent);
MOZ_ASSERT(aStartOfRightNode.GetContainerAsContent());
return SplitNodeResult(std::move(newLeftContent),
aStartOfRightNode.ContainerAsContent(),
MOZ_ASSERT(newContent);
MOZ_ASSERT(splitContent);
return SplitNodeResult(std::move(newContent), std::move(splitContent),
SplitNodeDirection::LeftNodeIsNewOne);
}
@ -4459,6 +4444,9 @@ SplitNodeResult HTMLEditor::SplitNodeDeepWithTransaction(
SplitNodeResult HTMLEditor::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
nsIContent& aNewNode) {
// Ensure computing the offset if it's intialized with a child content node.
Unused << aStartOfRightNode.Offset();
// XXX Perhaps, aStartOfRightNode may be invalid if this is a redo
// operation after modifying DOM node with JS.
if (MOZ_UNLIKELY(NS_WARN_IF(!aStartOfRightNode.IsInContentNode()))) {
@ -4666,6 +4654,12 @@ SplitNodeResult HTMLEditor::DoSplitNode(const EditorDOMPoint& aStartOfRightNode,
return SplitNodeResult(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
}
DebugOnly<nsresult> rvIgnored = RangeUpdaterRef().SelAdjSplitNode(
*aStartOfRightNode.ContainerAsContent(), aStartOfRightNode.Offset(),
aNewNode, SplitNodeDirection::LeftNodeIsNewOne);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"RangeUpdater::SelAdjSplitNode() failed, but ignored");
return SplitNodeResult(&aNewNode, aStartOfRightNode.ContainerAsContent(),
SplitNodeDirection::LeftNodeIsNewOne);
}
@ -4780,7 +4774,9 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
nsIContent& aContentToRemove) {
MOZ_ASSERT(IsEditActionDataAvailable());
const uint32_t removingNodeLength = aContentToRemove.Length();
const uint32_t removingContentLength = aContentToRemove.Length();
const Maybe<uint32_t> removingContentIndex =
aContentToRemove.ComputeIndexInParentNode();
// Remember all selection points.
// XXX Do we need to restore all types of selections by ourselves? Normal
@ -4823,13 +4819,13 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
atRemovingNode.Offset() < savingRange.mStartOffset &&
savingRange.mStartOffset <= atNodeToKeep.Offset()) {
savingRange.mStartContainer = &aContentToRemove;
savingRange.mStartOffset = removingNodeLength;
savingRange.mStartOffset = removingContentLength;
}
if (savingRange.mEndContainer == atNodeToKeep.GetContainer() &&
atRemovingNode.Offset() < savingRange.mEndOffset &&
savingRange.mEndOffset <= atNodeToKeep.Offset()) {
savingRange.mEndContainer = &aContentToRemove;
savingRange.mEndOffset = removingNodeLength;
savingRange.mEndOffset = removingContentLength;
}
}
@ -4839,21 +4835,24 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
}
// OK, ready to do join now.
// If it's a text node, just shuffle around some text.
if (aContentToKeep.IsText() && aContentToRemove.IsText()) {
nsAutoString rightText;
nsAutoString leftText;
aContentToKeep.AsText()->GetData(rightText);
aContentToRemove.AsText()->GetData(leftText);
leftText += rightText;
IgnoredErrorResult ignoredError;
DoSetText(MOZ_KnownLive(*aContentToKeep.AsText()), leftText, ignoredError);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
nsresult rv = [&]() MOZ_CAN_RUN_SCRIPT {
// If it's a text node, just shuffle around some text.
if (aContentToKeep.IsText() && aContentToRemove.IsText()) {
nsAutoString rightText;
nsAutoString leftText;
aContentToKeep.AsText()->GetData(rightText);
aContentToRemove.AsText()->GetData(leftText);
leftText += rightText;
IgnoredErrorResult ignoredError;
DoSetText(MOZ_KnownLive(*aContentToKeep.AsText()), leftText,
ignoredError);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"EditorBase::DoSetText() failed, but ignored");
return NS_OK;
}
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"EditorBase::DoSetText() failed, but ignored");
} else {
// Otherwise it's an interior node, so shuffle around the children.
nsCOMPtr<nsINodeList> childNodes = aContentToRemove.ChildNodes();
MOZ_ASSERT(childNodes);
@ -4881,12 +4880,28 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
firstChild = std::move(childNode);
}
}
}
return NS_OK;
}();
// Delete the extra node.
aContentToRemove.Remove();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
if (NS_SUCCEEDED(rv)) {
aContentToRemove.Remove();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
if (MOZ_LIKELY(removingContentIndex.isSome())) {
DebugOnly<nsresult> rvIgnored = RangeUpdaterRef().SelAdjJoinNodes(
EditorRawDOMPoint(&aContentToKeep, std::min(removingContentLength,
aContentToKeep.Length())),
aContentToRemove, *removingContentIndex,
JoinNodesDirection::LeftNodeIntoRightNode);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"RangeUpdater::SelAdjJoinNodes() failed, but ignored");
}
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
return rv;
}
const bool allowedTransactionsToChangeSelection =
@ -4920,14 +4935,14 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
if (savedRange.mStartContainer == &aContentToRemove) {
savedRange.mStartContainer = &aContentToKeep;
} else if (savedRange.mStartContainer == &aContentToKeep) {
savedRange.mStartOffset += removingNodeLength;
savedRange.mStartOffset += removingContentLength;
}
// Check to see if we joined nodes where selection ends.
if (savedRange.mEndContainer == &aContentToRemove) {
savedRange.mEndContainer = &aContentToKeep;
} else if (savedRange.mEndContainer == &aContentToKeep) {
savedRange.mEndOffset += removingNodeLength;
savedRange.mEndOffset += removingContentLength;
}
const RefPtr<nsRange> newRange = nsRange::Create(
@ -4954,8 +4969,8 @@ nsresult HTMLEditor::DoJoinNodes(nsIContent& aContentToKeep,
if (allowedTransactionsToChangeSelection) {
// Editor wants us to set selection at join point.
DebugOnly<nsresult> rvIgnored =
SelectionRef().CollapseInLimiter(&aContentToKeep, removingNodeLength);
DebugOnly<nsresult> rvIgnored = SelectionRef().CollapseInLimiter(
&aContentToKeep, removingContentLength);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}

View File

@ -125,10 +125,6 @@ nsresult JoinNodesTransaction::DoTransactionInternal(
const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
const OwningNonNull<nsIContent> removingContent = *mRemovedContent;
const OwningNonNull<nsIContent> keepingContent = *mKeepingContent;
// FYI: ComputeIndexInParentNode() never returns Nothing here because it's not
// being removed (i.e., it's in the parent child node chain).
const uint32_t removingContentOffset =
*removingContent->ComputeIndexInParentNode();
nsresult rv;
// Let's try to get actual joined point with the tacker.
EditorDOMPoint joinNodesPoint(mKeepingContent, 0u);
@ -137,13 +133,6 @@ nsresult JoinNodesTransaction::DoTransactionInternal(
&joinNodesPoint);
rv = htmlEditor->DoJoinNodes(keepingContent, removingContent);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::DoJoinNodes() failed");
DebugOnly<nsresult> rvIgnored =
htmlEditor->RangeUpdaterRef().SelAdjJoinNodes(
CreateJoinedPoint<EditorRawDOMPoint>(), removingContent,
removingContentOffset, JoinNodesDirection::LeftNodeIntoRightNode);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"RangeUpdater::SelAdjJoinNodes() failed, but ignored");
}
// Adjust join node offset to the actual offset where the original first
// content of the right node is.

View File

@ -5,9 +5,12 @@
#include "SplitNodeTransaction.h"
#include "EditorDOMPoint.h" // for EditorRawDOMPoint
#include "HTMLEditHelpers.h" // for SplitNodeResult
#include "HTMLEditor.h" // for HTMLEditor
#include "HTMLEditUtils.h"
#include "mozilla/EditorDOMPoint.h" // for RangeBoundary, EditorRawDOMPoint
#include "mozilla/HTMLEditor.h" // for HTMLEditor
#include "SelectionState.h" // for AutoTrackDOMPoint and RangeUpdater
#include "mozilla/Logging.h"
#include "mozilla/ToString.h"
#include "mozilla/dom/Selection.h"
@ -39,7 +42,13 @@ template <typename PT, typename CT>
SplitNodeTransaction::SplitNodeTransaction(
HTMLEditor& aHTMLEditor,
const EditorDOMPointBase<PT, CT>& aStartOfRightContent)
: mHTMLEditor(&aHTMLEditor), mStartOfRightContent(aStartOfRightContent) {
: mHTMLEditor(&aHTMLEditor),
mSplitContent(aStartOfRightContent.GetContainerAsContent()),
mSplitOffset(aStartOfRightContent.Offset()) {
// printf("SplitNodeTransaction size: %zu\n", sizeof(SplitNodeTransaction));
static_assert(sizeof(SplitNodeTransaction) <= 64,
"Transaction classes may be created a lot and may be alive "
"long so that keep the foot print smaller as far as possible");
MOZ_DIAGNOSTIC_ASSERT(aStartOfRightContent.IsInContentNode());
MOZ_DIAGNOSTIC_ASSERT(HTMLEditUtils::IsSplittableNode(
*aStartOfRightContent.ContainerAsContent()));
@ -47,23 +56,26 @@ SplitNodeTransaction::SplitNodeTransaction(
std::ostream& operator<<(std::ostream& aStream,
const SplitNodeTransaction& aTransaction) {
aStream << "{ mStartOfRightContent=" << aTransaction.mStartOfRightContent;
aStream << ", mNewLeftContent=" << aTransaction.mNewLeftContent.get();
if (aTransaction.mNewLeftContent) {
aStream << " (" << *aTransaction.mNewLeftContent << ")";
aStream << "{ mParentNode=" << aTransaction.mParentNode.get();
if (aTransaction.mParentNode) {
aStream << " (" << *aTransaction.mParentNode << ")";
}
aStream << ", mContainerParentNode="
<< aTransaction.mContainerParentNode.get();
if (aTransaction.mContainerParentNode) {
aStream << " (" << *aTransaction.mContainerParentNode << ")";
aStream << ", mNewContent=" << aTransaction.mNewContent.get();
if (aTransaction.mNewContent) {
aStream << " (" << *aTransaction.mNewContent << ")";
}
aStream << ", mHTMLEditor=" << aTransaction.mHTMLEditor.get() << " }";
aStream << ", mSplitContent=" << aTransaction.mSplitContent.get();
if (aTransaction.mSplitContent) {
aStream << " (" << *aTransaction.mSplitContent << ")";
}
aStream << ", mSplitOffset=" << aTransaction.mSplitOffset
<< ", mHTMLEditor=" << aTransaction.mHTMLEditor.get() << " }";
return aStream;
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase,
mHTMLEditor, mStartOfRightContent,
mContainerParentNode, mNewLeftContent)
mHTMLEditor, mParentNode, mSplitContent,
mNewContent)
NS_IMPL_ADDREF_INHERITED(SplitNodeTransaction, EditTransactionBase)
NS_IMPL_RELEASE_INHERITED(SplitNodeTransaction, EditTransactionBase)
@ -75,53 +87,38 @@ NS_IMETHODIMP SplitNodeTransaction::DoTransaction() {
("%p SplitNodeTransaction::%s this=%s", this, __FUNCTION__,
ToString(*this).c_str()));
if (NS_WARN_IF(!mHTMLEditor) ||
NS_WARN_IF(!mStartOfRightContent.IsInContentNode())) {
if (MOZ_UNLIKELY(NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(!mSplitContent))) {
return NS_ERROR_NOT_AVAILABLE;
}
MOZ_ASSERT(mStartOfRightContent.IsSetAndValid());
MOZ_ASSERT(mSplitOffset <= mSplitContent->Length());
// Create a new node
ErrorResult error;
IgnoredErrorResult error;
// Don't use .downcast directly because AsContent has an assertion we want
nsCOMPtr<nsINode> cloneOfRightContainer =
mStartOfRightContent.GetContainer()->CloneNode(false, error);
if (error.Failed()) {
nsCOMPtr<nsINode> newNode = mSplitContent->CloneNode(false, error);
if (MOZ_UNLIKELY(error.Failed())) {
NS_WARNING("nsINode::CloneNode() failed");
return error.StealNSResult();
}
if (NS_WARN_IF(!cloneOfRightContainer)) {
if (MOZ_UNLIKELY(NS_WARN_IF(!newNode))) {
return NS_ERROR_UNEXPECTED;
}
mNewLeftContent = cloneOfRightContainer->AsContent();
mContainerParentNode = mStartOfRightContent.GetContainerParent();
if (!mContainerParentNode) {
NS_WARNING("Right container was an orphan node");
mNewContent = newNode->AsContent();
mParentNode = mSplitContent->GetParentNode();
if (!mParentNode) {
NS_WARNING("The splitting content was an orphan node");
return NS_ERROR_NOT_AVAILABLE;
}
OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
OwningNonNull<nsIContent> newLeftContent = *mNewLeftContent;
OwningNonNull<nsINode> containerParentNode = *mContainerParentNode;
EditorDOMPoint startOfRightContent(mStartOfRightContent);
if (RefPtr<Element> startOfRightNode =
startOfRightContent.GetContainerAsElement()) {
nsresult rv = htmlEditor->MarkElementDirty(*startOfRightNode);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return EditorBase::ToGenericNSResult(rv);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::MarkElementDirty() failed, but ignored");
}
SplitNodeResult splitNodeResult =
htmlEditor->DoSplitNode(startOfRightContent, newLeftContent);
if (splitNodeResult.Failed()) {
NS_WARNING("HTMLEditor::DoSplitNode() failed");
return splitNodeResult.Rv();
const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
const OwningNonNull<nsIContent> splittingContent = *mSplitContent;
// MOZ_KnownLive(*mNewContent): it's grabbed by newNode
SplitNodeResult splitNodeResult = DoTransactionInternal(
htmlEditor, splittingContent, MOZ_KnownLive(*mNewContent), mSplitOffset);
if (MOZ_UNLIKELY(splitNodeResult.Failed())) {
NS_WARNING("SplitNodeTransaction::DoTransactionInternal() failed");
return EditorBase::ToGenericNSResult(splitNodeResult.Rv());
}
if (!htmlEditor->AllowsTransactionsToChangeSelection()) {
@ -140,26 +137,58 @@ NS_IMETHODIMP SplitNodeTransaction::DoTransaction() {
return error.StealNSResult();
}
SplitNodeResult SplitNodeTransaction::DoTransactionInternal(
HTMLEditor& aHTMLEditor, nsIContent& aSplittingContent,
nsIContent& aNewContent, uint32_t aSplitOffset) {
if (Element* const splittingElement = Element::FromNode(aSplittingContent)) {
// MOZ_KnownLive(*splittingElement): aSplittingContent should be grabbed by
// the callers.
nsresult rv =
aHTMLEditor.MarkElementDirty(MOZ_KnownLive(*splittingElement));
if (MOZ_UNLIKELY(NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED))) {
return SplitNodeResult(NS_ERROR_EDITOR_DESTROYED);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::MarkElementDirty() failed, but ignored");
}
SplitNodeResult splitNodeResult = aHTMLEditor.DoSplitNode(
EditorDOMPoint(&aSplittingContent,
std::min(aSplitOffset, aSplittingContent.Length())),
aNewContent);
NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
"HTMLEditor::DoSplitNode() failed");
return splitNodeResult;
}
NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() {
MOZ_LOG(GetLogModule(), LogLevel::Info,
("%p SplitNodeTransaction::%s this=%s", this, __FUNCTION__,
ToString(*this).c_str()));
if (NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(!mNewLeftContent) ||
NS_WARN_IF(!mContainerParentNode) ||
NS_WARN_IF(!mStartOfRightContent.IsInContentNode())) {
if (MOZ_UNLIKELY(NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(!mNewContent) ||
NS_WARN_IF(!mParentNode) || NS_WARN_IF(!mSplitContent) ||
NS_WARN_IF(mNewContent->IsBeingRemoved()))) {
return NS_ERROR_NOT_AVAILABLE;
}
// This assumes Do inserted the new node in front of the prior existing node
// XXX Perhaps, we should reset mStartOfRightNode with current first child
// of the right node.
OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
OwningNonNull<nsIContent> containerContent =
*mStartOfRightContent.ContainerAsContent();
OwningNonNull<nsIContent> newLeftContent = *mNewLeftContent;
nsresult rv = htmlEditor->DoJoinNodes(containerContent, newLeftContent);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::DoJoinNodes() failed");
const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
const OwningNonNull<nsIContent> keepingContent = *mSplitContent;
const OwningNonNull<nsIContent> removingContent = *mNewContent;
nsresult rv;
EditorDOMPoint joinedPoint(keepingContent, 0u);
{
AutoTrackDOMPoint trackJoinedPoint(htmlEditor->RangeUpdaterRef(),
&joinedPoint);
rv = htmlEditor->DoJoinNodes(keepingContent, removingContent);
}
if (NS_SUCCEEDED(rv)) {
// Adjust split offset for redo here
mSplitOffset = joinedPoint.Offset();
} else {
NS_WARNING("HTMLEditor::DoJoinNodes() failed");
}
return rv;
}
@ -172,36 +201,19 @@ NS_IMETHODIMP SplitNodeTransaction::RedoTransaction() {
("%p SplitNodeTransaction::%s this=%s", this, __FUNCTION__,
ToString(*this).c_str()));
if (MOZ_UNLIKELY(NS_WARN_IF(!mNewLeftContent) ||
NS_WARN_IF(!mContainerParentNode) ||
NS_WARN_IF(!mStartOfRightContent.IsInContentNode()) ||
NS_WARN_IF(!mHTMLEditor))) {
if (MOZ_UNLIKELY(NS_WARN_IF(!mNewContent) || NS_WARN_IF(!mParentNode) ||
NS_WARN_IF(!mSplitContent) || NS_WARN_IF(!mHTMLEditor))) {
return NS_ERROR_NOT_AVAILABLE;
}
const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
const OwningNonNull<nsIContent> newLeftContent = *mNewLeftContent;
EditorDOMPoint startOfRightContent(mStartOfRightContent);
if (RefPtr<Element> existingElement =
mStartOfRightContent.GetContainerAsElement()) {
nsresult rv = htmlEditor->MarkElementDirty(*existingElement);
if (MOZ_UNLIKELY(NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED))) {
return EditorBase::ToGenericNSResult(rv);
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::MarkElementDirty() failed, but ignored");
}
SplitNodeResult splitNodeResult = htmlEditor->DoSplitNode(
EditorDOMPoint(
startOfRightContent.ContainerAsContent(),
std::min(startOfRightContent.Offset(),
startOfRightContent.ContainerAsContent()->Length())),
newLeftContent);
const OwningNonNull<nsIContent> newContent = *mNewContent;
const OwningNonNull<nsIContent> splittingContent = *mSplitContent;
SplitNodeResult splitNodeResult = DoTransactionInternal(
htmlEditor, splittingContent, newContent, mSplitOffset);
NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
"HTMLEditor::DoSplitNode() failed");
return splitNodeResult.Rv();
"SplitNodeTransaction::DoTransactionInternal() failed");
return EditorBase::ToGenericNSResult(splitNodeResult.Rv());
}
} // namespace mozilla

View File

@ -6,9 +6,9 @@
#ifndef SplitNodeTransaction_h
#define SplitNodeTransaction_h
#include "mozilla/EditorDOMPoint.h" // for RangeBoundary, EditorRawDOMPoint
#include "mozilla/EditTransactionBase.h" // for EditTxn, etc.
#include "nsCOMPtr.h" // for nsCOMPtr
#include "EditTransactionBase.h" // for EditorTransactionBase
#include "nsCOMPtr.h" // for nsCOMPtr
#include "nsCycleCollectionParticipant.h"
#include "nsIContent.h"
#include "nsISupportsImpl.h" // for NS_DECL_ISUPPORTS_INHERITED
@ -17,6 +17,10 @@
namespace mozilla {
class HTMLEditor;
class SplitNodeResult;
template <typename PT, typename CT>
class EditorDOMPointBase;
/**
* A transaction that splits a node into two identical nodes, with the children
@ -30,15 +34,14 @@ class SplitNodeTransaction final : public EditTransactionBase {
public:
/**
* Creates a transaction to create a new node (left node) identical to an
* existing node (right node), and split the contents between the same point
* in both nodes.
* Creates a transaction to create a new node identical to an existing node,
* and split the contents between the same point in both nodes.
*
* @param aHTMLEditor The provider of core editing operations.
* @param aStartOfRightContent The point to split. Its container will be
* the right node, i.e., become the new node's
* next sibling. And the point will be start
* of the right node.
* will be split, and its previous sibling will
* be cloned new node. And the point will be
* start of the right node.
*/
template <typename PT, typename CT>
static already_AddRefed<SplitNodeTransaction> Create(
@ -54,7 +57,13 @@ class SplitNodeTransaction final : public EditTransactionBase {
MOZ_CAN_RUN_SCRIPT NS_IMETHOD RedoTransaction() override;
nsIContent* GetNewLeftContent() const { return mNewLeftContent; }
nsIContent* GetSplitContent() const { return mSplitContent; }
nsIContent* GetNewContent() const { return mNewContent; }
nsINode* GetParentNode() const { return mParentNode; }
// The split offset. At undoing, this is recomputed with tracking the
// first child of mSplitContent.
uint32_t SplitOffset() const { return mSplitOffset; }
friend std::ostream& operator<<(std::ostream& aStream,
const SplitNodeTransaction& aTransaction);
@ -62,17 +71,24 @@ class SplitNodeTransaction final : public EditTransactionBase {
protected:
virtual ~SplitNodeTransaction() = default;
MOZ_CAN_RUN_SCRIPT SplitNodeResult
DoTransactionInternal(HTMLEditor& aHTMLEditor, nsIContent& aSplittingContent,
nsIContent& aNewContent, uint32_t aSplitOffset);
RefPtr<HTMLEditor> mHTMLEditor;
// The container is existing right node (will be split).
// The point referring this is start of the right node after it's split.
EditorDOMPoint mStartOfRightContent;
// The node which should be parent of both mNewContent and mSplitContent.
nsCOMPtr<nsINode> mParentNode;
// The node we create when splitting mExistingRightContent.
nsCOMPtr<nsIContent> mNewLeftContent;
// The node we create when splitting mSplitContent.
nsCOMPtr<nsIContent> mNewContent;
// The parent shared by mExistingRightContent and mNewLeftContent.
nsCOMPtr<nsINode> mContainerParentNode;
// The content node which we split.
nsCOMPtr<nsIContent> mSplitContent;
// The offset where we split in mSplitContent. This is required for doing and
// redoing. Therefore, this is updated when undoing.
uint32_t mSplitOffset;
};
} // namespace mozilla

View File

@ -507,6 +507,27 @@ static inline uint32_t RescalingFactorForColorDepth(ColorDepth aColorDepth) {
return factor;
}
enum class ChromaSubsampling : uint8_t {
FULL,
HALF_WIDTH,
HALF_WIDTH_AND_HEIGHT,
_First = FULL,
_Last = HALF_WIDTH_AND_HEIGHT,
};
template <typename T>
static inline T ChromaSize(const T& aYSize, ChromaSubsampling aSubsampling) {
switch (aSubsampling) {
case ChromaSubsampling::FULL:
return aYSize;
case ChromaSubsampling::HALF_WIDTH:
return T((aYSize.width + 1) / 2, aYSize.height);
case ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
return T((aYSize.width + 1) / 2, (aYSize.height + 1) / 2);
}
MOZ_CRASH("bad ChromaSubsampling");
}
enum class FilterType : int8_t {
BLEND = 0,
TRANSFORM,

View File

@ -895,26 +895,34 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData,
// --
if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip ||
yuvData.mYSize.width < 0 || yuvData.mYSize.height < 0 ||
yuvData.mCbCrSize.width < 0 || yuvData.mCbCrSize.height < 0 ||
auto ySize = yuvData.YDataSize();
auto cbcrSize = yuvData.CbCrDataSize();
if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || ySize.width < 0 ||
ySize.height < 0 || cbcrSize.width < 0 || cbcrSize.height < 0 ||
yuvData.mYStride < 0 || yuvData.mCbCrStride < 0) {
gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
<< yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
<< yuvData.mYSize.width << "," << yuvData.mYSize.height
<< ", " << yuvData.mCbCrSize.width << ","
<< yuvData.mCbCrSize.height << ", " << yuvData.mYStride
<< "," << yuvData.mCbCrStride;
<< ySize.width << "," << ySize.height << ", "
<< cbcrSize.width << "," << cbcrSize.height << ", "
<< yuvData.mYStride << "," << yuvData.mCbCrStride;
return false;
}
gfx::IntSize divisors;
if (!GuessDivisors(yuvData.mYSize, yuvData.mCbCrSize, &divisors)) {
gfxCriticalError() << "GuessDivisors failed:" << yuvData.mYSize.width << ","
<< yuvData.mYSize.height << ", "
<< yuvData.mCbCrSize.width << ","
<< yuvData.mCbCrSize.height;
return false;
switch (yuvData.mChromaSubsampling) {
case gfx::ChromaSubsampling::FULL:
divisors = gfx::IntSize(1, 1);
break;
case gfx::ChromaSubsampling::HALF_WIDTH:
divisors = gfx::IntSize(2, 1);
break;
case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
divisors = gfx::IntSize(2, 2);
break;
default:
gfxCriticalError() << "Unknown chroma subsampling:"
<< int(yuvData.mChromaSubsampling);
return false;
}
// --
@ -937,8 +945,9 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData,
const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D);
const ResetUnpackState reset(mGL);
const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.mYSize.height);
const gfx::IntSize uvTexSize(yuvData.mCbCrStride, yuvData.mCbCrSize.height);
const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height);
const gfx::IntSize uvTexSize(yuvData.mCbCrStride,
yuvData.CbCrDataSize().height);
if (yTexSize != mYuvUploads_YSize || uvTexSize != mYuvUploads_UVSize) {
mYuvUploads_YSize = yTexSize;
@ -978,7 +987,7 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData,
// --
const auto& clipRect = yuvData.GetPictureRect();
const auto& clipRect = yuvData.mPictureRect;
const auto srcOrigin = OriginPos::BottomLeft;
const bool yFlip = (destOrigin != srcOrigin);

View File

@ -210,9 +210,9 @@ bool GLBlitHelper::BlitImage(layers::D3D11YCbCrImage* const srcImage,
const WindowsHandle handles[3] = {(WindowsHandle)data->mHandles[0],
(WindowsHandle)data->mHandles[1],
(WindowsHandle)data->mHandles[2]};
return BlitAngleYCbCr(handles, srcImage->mPictureRect, srcImage->mYSize,
srcImage->mCbCrSize, srcImage->mColorSpace, destSize,
destOrigin);
return BlitAngleYCbCr(handles, srcImage->mPictureRect, srcImage->GetYSize(),
srcImage->GetCbCrSize(), srcImage->mColorSpace,
destSize, destOrigin);
}
// -------------------------------------

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CanvasManagerParent.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/WebGLParent.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/ipc/Endpoint.h"
@ -90,6 +91,10 @@ already_AddRefed<dom::PWebGLParent> CanvasManagerParent::AllocPWebGLParent() {
already_AddRefed<webgpu::PWebGPUParent>
CanvasManagerParent::AllocPWebGPUParent() {
if (!StaticPrefs::dom_webgpu_enabled()) {
return nullptr;
}
return MakeAndAddRef<webgpu::WebGPUParent>();
}

Some files were not shown because too many files have changed in this diff Show More