mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Merge autoland to mozilla-central. a=merge
This commit is contained in:
commit
fb8877ceea
@ -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"
|
||||
|
41
.cron.yml
41
.cron.yml
@ -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
24
Cargo.lock
generated
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
}
|
||||
});
|
@ -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]
|
||||
|
@ -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 },
|
||||
|
@ -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",
|
||||
]
|
||||
|
@ -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;
|
@ -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("");
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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"],
|
||||
|
@ -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 ***/
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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}"`);
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
*/
|
||||
|
37
docs/bug-mgmt/guides/bug-pipeline.rst
Normal file
37
docs/bug-mgmt/guides/bug-pipeline.rst
Normal 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>`_
|
@ -45,6 +45,7 @@ categories:
|
||||
testing_doc:
|
||||
- testing/testing-policy
|
||||
- testing/ci-configs
|
||||
- testing/chrome-tests
|
||||
- testing/marionette
|
||||
- testing/geckodriver
|
||||
- testing/test-verification
|
||||
|
@ -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/
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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)) ||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]
|
||||
|
45
docshell/test/navigation/browser_bug1757458.js
Normal file
45
docshell/test/navigation/browser_bug1757458.js
Normal 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'."
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
32
docshell/test/navigation/file_bug1758664.html
Normal file
32
docshell/test/navigation/file_bug1758664.html
Normal 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>
|
@ -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]
|
||||
|
6
docshell/test/navigation/redirect_to_blank.sjs
Normal file
6
docshell/test/navigation/redirect_to_blank.sjs
Normal 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);
|
||||
}
|
21
docshell/test/navigation/test_bug1758664.html
Normal file
21
docshell/test/navigation/test_bug1758664.html
Normal 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>
|
@ -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;
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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::
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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});
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()});
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
|
@ -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}));
|
||||
|
@ -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^
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user