зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset f3fe6cc42791 (bug 1809094) for bc failures on browser_ext_tabs_autoDiscardable.js.
This commit is contained in:
Родитель
93383a6dc2
Коммит
87bdf668d4
|
@ -154,15 +154,6 @@
|
||||||
gBrowser._tabAttrModified(this, ["attention"]);
|
gBrowser._tabAttrModified(this, ["attention"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
set undiscardable(val) {
|
|
||||||
if (val == this.hasAttribute("undiscardable")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toggleAttribute("undiscardable", val);
|
|
||||||
gBrowser._tabAttrModified(this, ["undiscardable"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
set _visuallySelected(val) {
|
set _visuallySelected(val) {
|
||||||
if (val == (this.getAttribute("visuallyselected") == "true")) {
|
if (val == (this.getAttribute("visuallyselected") == "true")) {
|
||||||
return;
|
return;
|
||||||
|
@ -233,10 +224,6 @@
|
||||||
return this.getAttribute("activemedia-blocked") == "true";
|
return this.getAttribute("activemedia-blocked") == "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
get undiscardable() {
|
|
||||||
return this.hasAttribute("undiscardable");
|
|
||||||
}
|
|
||||||
|
|
||||||
get isEmpty() {
|
get isEmpty() {
|
||||||
// Determines if a tab is "empty", usually used in the context of determining
|
// Determines if a tab is "empty", usually used in the context of determining
|
||||||
// if it's ok to close the tab.
|
// if it's ok to close the tab.
|
||||||
|
|
|
@ -4547,10 +4547,6 @@
|
||||||
}
|
}
|
||||||
modifiedAttrs.push("muted");
|
modifiedAttrs.push("muted");
|
||||||
}
|
}
|
||||||
if (aOtherTab.hasAttribute("undiscardable")) {
|
|
||||||
aOurTab.setAttribute("undiscardable", "true");
|
|
||||||
modifiedAttrs.push("undiscardable");
|
|
||||||
}
|
|
||||||
if (aOtherTab.hasAttribute("soundplaying")) {
|
if (aOtherTab.hasAttribute("soundplaying")) {
|
||||||
aOurTab.setAttribute("soundplaying", "true");
|
aOurTab.setAttribute("soundplaying", "true");
|
||||||
modifiedAttrs.push("soundplaying");
|
modifiedAttrs.push("soundplaying");
|
||||||
|
|
|
@ -747,10 +747,6 @@ class Tab extends TabBase {
|
||||||
return this.nativeTab.soundPlaying;
|
return this.nativeTab.soundPlaying;
|
||||||
}
|
}
|
||||||
|
|
||||||
get autoDiscardable() {
|
|
||||||
return !this.nativeTab.undiscardable;
|
|
||||||
}
|
|
||||||
|
|
||||||
get browser() {
|
get browser() {
|
||||||
return this.nativeTab.linkedBrowser;
|
return this.nativeTab.linkedBrowser;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,12 +150,10 @@ const allAttrs = new Set([
|
||||||
"mutedInfo",
|
"mutedInfo",
|
||||||
"sharingState",
|
"sharingState",
|
||||||
"title",
|
"title",
|
||||||
"autoDiscardable",
|
|
||||||
]);
|
]);
|
||||||
const allProperties = new Set([
|
const allProperties = new Set([
|
||||||
"attention",
|
"attention",
|
||||||
"audible",
|
"audible",
|
||||||
"autoDiscardable",
|
|
||||||
"discarded",
|
"discarded",
|
||||||
"favIconUrl",
|
"favIconUrl",
|
||||||
"hidden",
|
"hidden",
|
||||||
|
@ -421,12 +419,6 @@ this.tabs = class extends ExtensionAPIPersistent {
|
||||||
) {
|
) {
|
||||||
needed.push("audible");
|
needed.push("audible");
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
changed.includes("undiscardable") &&
|
|
||||||
filter.properties.has("autoDiscardable")
|
|
||||||
) {
|
|
||||||
needed.push("autoDiscardable");
|
|
||||||
}
|
|
||||||
if (changed.includes("label") && filter.properties.has("title")) {
|
if (changed.includes("label") && filter.properties.has("title")) {
|
||||||
needed.push("title");
|
needed.push("title");
|
||||||
}
|
}
|
||||||
|
@ -906,9 +898,6 @@ this.tabs = class extends ExtensionAPIPersistent {
|
||||||
if (updateProperties.active) {
|
if (updateProperties.active) {
|
||||||
tabbrowser.selectedTab = nativeTab;
|
tabbrowser.selectedTab = nativeTab;
|
||||||
}
|
}
|
||||||
if (updateProperties.autoDiscardable !== null) {
|
|
||||||
nativeTab.undiscardable = !updateProperties.autoDiscardable;
|
|
||||||
}
|
|
||||||
if (updateProperties.highlighted !== null) {
|
if (updateProperties.highlighted !== null) {
|
||||||
if (updateProperties.highlighted) {
|
if (updateProperties.highlighted) {
|
||||||
if (!nativeTab.selected && !nativeTab.multiselected) {
|
if (!nativeTab.selected && !nativeTab.multiselected) {
|
||||||
|
|
|
@ -139,11 +139,6 @@
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."
|
"description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."
|
||||||
},
|
},
|
||||||
"autoDiscardable": {
|
|
||||||
"type": "boolean",
|
|
||||||
"optional": true,
|
|
||||||
"description": "Whether the tab can be discarded automatically by the browser when resources are low."
|
|
||||||
},
|
|
||||||
"mutedInfo": {
|
"mutedInfo": {
|
||||||
"$ref": "MutedInfo",
|
"$ref": "MutedInfo",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -430,7 +425,6 @@
|
||||||
"enum": [
|
"enum": [
|
||||||
"attention",
|
"attention",
|
||||||
"audible",
|
"audible",
|
||||||
"autoDiscardable",
|
|
||||||
"discarded",
|
"discarded",
|
||||||
"favIconUrl",
|
"favIconUrl",
|
||||||
"hidden",
|
"hidden",
|
||||||
|
@ -747,11 +741,6 @@
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"description": "Whether the tabs are audible."
|
"description": "Whether the tabs are audible."
|
||||||
},
|
},
|
||||||
"autoDiscardable": {
|
|
||||||
"type": "boolean",
|
|
||||||
"optional": true,
|
|
||||||
"description": "Whether the tab can be discarded automatically by the browser when resources are low."
|
|
||||||
},
|
|
||||||
"muted": {
|
"muted": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -949,11 +938,6 @@
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))."
|
"description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))."
|
||||||
},
|
},
|
||||||
"autoDiscardable": {
|
|
||||||
"type": "boolean",
|
|
||||||
"optional": true,
|
|
||||||
"description": "Whether the tab can be discarded automatically by the browser when resources are low."
|
|
||||||
},
|
|
||||||
"highlighted": {
|
"highlighted": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
@ -1631,11 +1615,6 @@
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"description": "The tab's new audible state."
|
"description": "The tab's new audible state."
|
||||||
},
|
},
|
||||||
"autoDiscardable": {
|
|
||||||
"type": "boolean",
|
|
||||||
"optional": true,
|
|
||||||
"description": "The tab's new autoDiscardable state."
|
|
||||||
},
|
|
||||||
"discarded": {
|
"discarded": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
|
|
@ -296,7 +296,6 @@ skip-if =
|
||||||
[browser_ext_tabs_attention.js]
|
[browser_ext_tabs_attention.js]
|
||||||
https_first_disabled = true
|
https_first_disabled = true
|
||||||
[browser_ext_tabs_audio.js]
|
[browser_ext_tabs_audio.js]
|
||||||
[browser_ext_tabs_autoDiscardable.js]
|
|
||||||
[browser_ext_tabs_containerIsolation.js]
|
[browser_ext_tabs_containerIsolation.js]
|
||||||
https_first_disabled = true
|
https_first_disabled = true
|
||||||
[browser_ext_tabs_cookieStoreId.js]
|
[browser_ext_tabs_cookieStoreId.js]
|
||||||
|
|
|
@ -1,177 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
add_task(async function test_autoDiscardable() {
|
|
||||||
let files = {
|
|
||||||
"schema.json": JSON.stringify([
|
|
||||||
{
|
|
||||||
namespace: "experiments",
|
|
||||||
functions: [
|
|
||||||
{
|
|
||||||
name: "unload",
|
|
||||||
type: "function",
|
|
||||||
async: "callback",
|
|
||||||
description:
|
|
||||||
"Unload the least recently used tab using Firefox's built-in tab unloader mechanism",
|
|
||||||
parameters: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
"parent.js": () => {
|
|
||||||
const { TabUnloader } = ChromeUtils.importESModule(
|
|
||||||
"resource:///modules/TabUnloader.sys.mjs"
|
|
||||||
);
|
|
||||||
const { ExtensionError } = ExtensionUtils;
|
|
||||||
this.experiments = class extends ExtensionAPI {
|
|
||||||
getAPI(context) {
|
|
||||||
return {
|
|
||||||
experiments: {
|
|
||||||
async unload() {
|
|
||||||
try {
|
|
||||||
await TabUnloader.unloadLeastRecentlyUsedTab(null);
|
|
||||||
} catch (error) {
|
|
||||||
// We need to do this, otherwise failures won't bubble up to the test properly.
|
|
||||||
throw ExtensionError(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
async function background() {
|
|
||||||
let firstTab = await browser.tabs.create({
|
|
||||||
active: false,
|
|
||||||
url: "https://example.org/",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure setting and getting works properly
|
|
||||||
browser.test.assertTrue(
|
|
||||||
firstTab.autoDiscardable,
|
|
||||||
"autoDiscardable should always be true by default"
|
|
||||||
);
|
|
||||||
let result = await browser.tabs.update(firstTab.id, {
|
|
||||||
autoDiscardable: false,
|
|
||||||
});
|
|
||||||
browser.test.assertFalse(
|
|
||||||
result.autoDiscardable,
|
|
||||||
"autoDiscardable should be false after setting it as such"
|
|
||||||
);
|
|
||||||
result = await browser.tabs.update(firstTab.id, {
|
|
||||||
autoDiscardable: true,
|
|
||||||
});
|
|
||||||
browser.test.assertTrue(
|
|
||||||
result.autoDiscardable,
|
|
||||||
"autoDiscardable should be true after setting it as such"
|
|
||||||
);
|
|
||||||
result = await browser.tabs.update(firstTab.id, {
|
|
||||||
autoDiscardable: false,
|
|
||||||
});
|
|
||||||
browser.test.assertFalse(
|
|
||||||
result.autoDiscardable,
|
|
||||||
"autoDiscardable should be false after setting it as such"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure the tab can't be unloaded when autoDiscardable is false
|
|
||||||
await browser.experiments.unload();
|
|
||||||
result = await browser.tabs.get(firstTab.id);
|
|
||||||
browser.test.assertFalse(
|
|
||||||
result.discarded,
|
|
||||||
"Tab should not unload when autoDiscardable is false"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure the tab CAN be unloaded when autoDiscardable is true
|
|
||||||
await browser.tabs.update(firstTab.id, {
|
|
||||||
autoDiscardable: true,
|
|
||||||
});
|
|
||||||
await browser.experiments.unload();
|
|
||||||
result = await browser.tabs.get(firstTab.id);
|
|
||||||
browser.test.assertTrue(
|
|
||||||
result.discarded,
|
|
||||||
"Tab should unload when autoDiscardable is true"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure filtering for discardable tabs works properly
|
|
||||||
result = await browser.tabs.query({ autoDiscardable: true });
|
|
||||||
browser.test.assertEq(
|
|
||||||
2,
|
|
||||||
result.length,
|
|
||||||
"tabs.query should return 2 when autoDiscardable is true "
|
|
||||||
);
|
|
||||||
await browser.tabs.update(firstTab.id, {
|
|
||||||
autoDiscardable: false,
|
|
||||||
});
|
|
||||||
result = await browser.tabs.query({ autoDiscardable: true });
|
|
||||||
browser.test.assertEq(
|
|
||||||
1,
|
|
||||||
result.length,
|
|
||||||
"tabs.query should return 1 when autoDiscardable is false"
|
|
||||||
);
|
|
||||||
|
|
||||||
let onUpdatedPromise = {};
|
|
||||||
onUpdatedPromise.promise = new Promise(
|
|
||||||
resolve => (onUpdatedPromise.resolve = resolve)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure onUpdated works
|
|
||||||
async function testOnUpdatedEvent(autoDiscardable) {
|
|
||||||
browser.test.log(`Testing autoDiscardable = ${autoDiscardable}`);
|
|
||||||
let onUpdated;
|
|
||||||
let promise = new Promise(resolve => {
|
|
||||||
onUpdated = (tabId, changeInfo, tabInfo) => {
|
|
||||||
browser.test.assertEq(
|
|
||||||
firstTab.id,
|
|
||||||
tabId,
|
|
||||||
"The updated tab's ID should match the correct tab"
|
|
||||||
);
|
|
||||||
browser.test.assertDeepEq(
|
|
||||||
{ autoDiscardable },
|
|
||||||
changeInfo,
|
|
||||||
"The updated tab's changeInfo should be correct"
|
|
||||||
);
|
|
||||||
browser.test.assertEq(
|
|
||||||
tabInfo.autoDiscardable,
|
|
||||||
autoDiscardable,
|
|
||||||
"The updated tab's tabInfo should be correct"
|
|
||||||
);
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
browser.tabs.onUpdated.addListener(onUpdated, {
|
|
||||||
properties: ["autoDiscardable"],
|
|
||||||
});
|
|
||||||
await browser.tabs.update(firstTab.id, { autoDiscardable });
|
|
||||||
await promise;
|
|
||||||
browser.tabs.onUpdated.removeListener(onUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
await testOnUpdatedEvent(true);
|
|
||||||
await testOnUpdatedEvent(false);
|
|
||||||
|
|
||||||
await browser.tabs.remove(firstTab.id); // Cleanup
|
|
||||||
browser.test.notifyPass("autoDiscardable");
|
|
||||||
}
|
|
||||||
let extension = ExtensionTestUtils.loadExtension({
|
|
||||||
isPrivileged: true,
|
|
||||||
manifest: {
|
|
||||||
permissions: ["tabs"],
|
|
||||||
experiment_apis: {
|
|
||||||
experiments: {
|
|
||||||
schema: "schema.json",
|
|
||||||
parent: {
|
|
||||||
scopes: ["addon_parent"],
|
|
||||||
script: "parent.js",
|
|
||||||
paths: [["experiments"]],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
background,
|
|
||||||
files,
|
|
||||||
});
|
|
||||||
await extension.startup();
|
|
||||||
await extension.awaitFinish("autoDiscardable");
|
|
||||||
await extension.unload();
|
|
||||||
});
|
|
|
@ -52,7 +52,7 @@ let CRITERIA_WEIGHT = 1;
|
||||||
*/
|
*/
|
||||||
let DefaultTabUnloaderMethods = {
|
let DefaultTabUnloaderMethods = {
|
||||||
isNonDiscardable(tab, weight) {
|
isNonDiscardable(tab, weight) {
|
||||||
if (tab.undiscardable || tab.selected) {
|
if (tab.selected) {
|
||||||
return weight;
|
return weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,12 +391,6 @@ class Tab extends TabBase {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get autoDiscardable() {
|
|
||||||
// This property reflects whether the browser is allowed to auto-discard.
|
|
||||||
// Since extensions cannot do so on Android, we return true here.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get sharingState() {
|
get sharingState() {
|
||||||
return {
|
return {
|
||||||
screen: undefined,
|
screen: undefined,
|
||||||
|
|
|
@ -140,11 +140,6 @@
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."
|
"description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."
|
||||||
},
|
},
|
||||||
"autoDiscardable": {
|
|
||||||
"type": "boolean",
|
|
||||||
"optional": true,
|
|
||||||
"description": "Whether the tab can be discarded automatically by the browser when resources are low."
|
|
||||||
},
|
|
||||||
"mutedInfo": {
|
"mutedInfo": {
|
||||||
"$ref": "MutedInfo",
|
"$ref": "MutedInfo",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
|
|
@ -18,7 +18,6 @@ prefs =
|
||||||
[test_ext_all_apis.html]
|
[test_ext_all_apis.html]
|
||||||
[test_ext_downloads_event_page.html]
|
[test_ext_downloads_event_page.html]
|
||||||
[test_ext_tab_runtimeConnect.html]
|
[test_ext_tab_runtimeConnect.html]
|
||||||
[test_ext_tabs_autoDiscardable.html]
|
|
||||||
[test_ext_tabs_create.html]
|
[test_ext_tabs_create.html]
|
||||||
[test_ext_tabs_events.html]
|
[test_ext_tabs_events.html]
|
||||||
skip-if =
|
skip-if =
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>autoDiscardable test</title>
|
|
||||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
|
|
||||||
<script type="text/javascript" src="head.js"></script>
|
|
||||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<script type="text/javascript">
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
add_task(async function () {
|
|
||||||
async function background() {
|
|
||||||
const tab = await browser.tabs.create({ title: "Test Tab" });
|
|
||||||
browser.test.assertTrue(
|
|
||||||
tab.autoDiscardable,
|
|
||||||
"autoDiscardable should be true on Android"
|
|
||||||
);
|
|
||||||
browser.test.assertThrows(
|
|
||||||
() => browser.tabs.query({ autoDiscardable: true }),
|
|
||||||
"tabs.query with autoDiscardable should error out on Android"
|
|
||||||
);
|
|
||||||
browser.test.assertThrows(
|
|
||||||
() => browser.tabs.update(tab.id, { autoDiscardable: true }),
|
|
||||||
"tabs.update with autoDiscardable should error out on Android"
|
|
||||||
);
|
|
||||||
browser.test.notifyPass("tab.autoDiscardable");
|
|
||||||
}
|
|
||||||
const extension = ExtensionTestUtils.loadExtension({
|
|
||||||
manifest: {
|
|
||||||
permissions: ["tabs"],
|
|
||||||
background,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await extension.startup();
|
|
||||||
await extension.awaitFinish("tab.autoDiscardable");
|
|
||||||
await extension.unload();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -285,16 +285,6 @@ class TabBase {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @property {boolean} autoDiscardable
|
|
||||||
* Returns true if the tab can be discarded on memory pressure, false otherwise.
|
|
||||||
* @readonly
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
get autoDiscardable() {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {XULElement} browser
|
* @property {XULElement} browser
|
||||||
* Returns the XUL browser for the given tab.
|
* Returns the XUL browser for the given tab.
|
||||||
|
@ -525,8 +515,6 @@ class TabBase {
|
||||||
* Matches against the exact value of the tab's `active` attribute.
|
* Matches against the exact value of the tab's `active` attribute.
|
||||||
* @param {boolean} [queryInfo.audible]
|
* @param {boolean} [queryInfo.audible]
|
||||||
* Matches against the exact value of the tab's `audible` attribute.
|
* Matches against the exact value of the tab's `audible` attribute.
|
||||||
* @param {boolean} [queryInfo.autoDiscardable]
|
|
||||||
* Matches against the exact value of the tab's `autoDiscardable` attribute.
|
|
||||||
* @param {string} [queryInfo.cookieStoreId]
|
* @param {string} [queryInfo.cookieStoreId]
|
||||||
* Matches against the exact value of the tab's `cookieStoreId` attribute.
|
* Matches against the exact value of the tab's `cookieStoreId` attribute.
|
||||||
* @param {boolean} [queryInfo.discarded]
|
* @param {boolean} [queryInfo.discarded]
|
||||||
|
@ -564,7 +552,6 @@ class TabBase {
|
||||||
const PROPS = [
|
const PROPS = [
|
||||||
"active",
|
"active",
|
||||||
"audible",
|
"audible",
|
||||||
"autoDiscardable",
|
|
||||||
"discarded",
|
"discarded",
|
||||||
"hidden",
|
"hidden",
|
||||||
"highlighted",
|
"highlighted",
|
||||||
|
@ -650,7 +637,6 @@ class TabBase {
|
||||||
height: this.height,
|
height: this.height,
|
||||||
lastAccessed: this.lastAccessed,
|
lastAccessed: this.lastAccessed,
|
||||||
audible: this.audible,
|
audible: this.audible,
|
||||||
autoDiscardable: this.autoDiscardable,
|
|
||||||
mutedInfo: this.mutedInfo,
|
mutedInfo: this.mutedInfo,
|
||||||
isArticle: this.isArticle,
|
isArticle: this.isArticle,
|
||||||
isInReaderMode: this.isInReaderMode,
|
isInReaderMode: this.isInReaderMode,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче