зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to central, a=merge
MozReview-Commit-ID: AWf410QGui6
This commit is contained in:
Коммит
252080fa1f
|
@ -8,7 +8,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
|
|||
|
||||
registerCleanupFunction(async function() {
|
||||
await task_resetState();
|
||||
await task_clearHistory();
|
||||
await PlacesUtils.history.clear();
|
||||
});
|
||||
|
||||
add_task(async function test_indicatorDrop() {
|
||||
|
|
|
@ -8,7 +8,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
|
|||
|
||||
registerCleanupFunction(async function() {
|
||||
await task_resetState();
|
||||
await task_clearHistory();
|
||||
await PlacesUtils.history.clear();
|
||||
});
|
||||
|
||||
add_task(async function test_indicatorDrop() {
|
||||
|
|
|
@ -163,16 +163,6 @@ function httpUrl(aFileName) {
|
|||
aFileName;
|
||||
}
|
||||
|
||||
function task_clearHistory() {
|
||||
return new Promise(function(resolve) {
|
||||
Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
|
||||
resolve();
|
||||
}, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
|
||||
PlacesUtils.history.clear();
|
||||
});
|
||||
}
|
||||
|
||||
function openLibrary(aLeftPaneRoot) {
|
||||
let library = window.openDialog("chrome://browser/content/places/places.xul",
|
||||
"", "chrome,toolbar=yes,dialog=no,resizable",
|
||||
|
|
|
@ -19,7 +19,6 @@ const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
|
|||
|
||||
var EXPECTED_NOTIFICATIONS = [
|
||||
"places-shutdown",
|
||||
"places-will-close-connection",
|
||||
"places-expiration-finished",
|
||||
"places-connection-closed"
|
||||
];
|
||||
|
|
|
@ -6,67 +6,40 @@
|
|||
// private browsing mode.
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
add_task(async function test() {
|
||||
const TEST_URL = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html"
|
||||
const TEST_URI = Services.io.newURI(TEST_URL);
|
||||
const TITLE_1 = "Title 1";
|
||||
const TITLE_2 = "Title 2";
|
||||
|
||||
function waitForTitleChanged() {
|
||||
return new Promise(resolve => {
|
||||
let historyObserver = {
|
||||
onTitleChanged(uri, pageTitle) {
|
||||
PlacesUtils.history.removeObserver(historyObserver, false);
|
||||
resolve({uri, pageTitle});
|
||||
},
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onVisit() {},
|
||||
onDeleteURI() {},
|
||||
onClearHistory() {},
|
||||
onPageChanged() {},
|
||||
onDeleteVisits() {},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
|
||||
};
|
||||
await PlacesUtils.history.clear();
|
||||
|
||||
PlacesUtils.history.addObserver(historyObserver);
|
||||
let promiseTitleChanged = PlacesTestUtils.waitForNotification(
|
||||
"onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
registerCleanupFunction(async () => {
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
}
|
||||
info("Wait for a title change notification.");
|
||||
await promiseTitleChanged;
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, TITLE_1,
|
||||
"The title matches the orignal title after first visit");
|
||||
|
||||
await PlacesTestUtils.clearHistory();
|
||||
|
||||
let tabToClose = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
|
||||
await waitForTitleChanged();
|
||||
is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_1, "The title matches the orignal title after first visit");
|
||||
|
||||
let place = {
|
||||
uri: TEST_URI,
|
||||
title: TITLE_2,
|
||||
visits: [{
|
||||
visitDate: Date.now() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
}]
|
||||
};
|
||||
PlacesUtils.asyncHistory.updatePlaces(place, {
|
||||
handleError: () => ok(false, "Unexpected error in adding visit."),
|
||||
handleResult() { },
|
||||
handleCompletion() {}
|
||||
});
|
||||
|
||||
await waitForTitleChanged();
|
||||
is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_2, "The title matches the updated title after updating visit");
|
||||
promiseTitleChanged = PlacesTestUtils.waitForNotification(
|
||||
"onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
|
||||
await PlacesTestUtils.addVisits({ uri: TEST_URL, title: TITLE_2 });
|
||||
info("Wait for a title change notification.");
|
||||
await promiseTitleChanged;
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, TITLE_2,
|
||||
"The title matches the orignal title after updating visit");
|
||||
|
||||
let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
|
||||
await BrowserTestUtils.browserLoaded(privateWin.gBrowser.addTab(TEST_URL).linkedBrowser);
|
||||
|
||||
is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_2, "The title remains the same after visiting in private window");
|
||||
await PlacesTestUtils.clearHistory();
|
||||
|
||||
// Cleanup
|
||||
BrowserTestUtils.closeWindow(privateWin);
|
||||
gBrowser.removeTab(tabToClose);
|
||||
registerCleanupFunction(async () => {
|
||||
await BrowserTestUtils.closeWindow(privateWin);
|
||||
});
|
||||
await BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser, TEST_URL);
|
||||
// Wait long enough to be sure history didn't set a title.
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, TITLE_2,
|
||||
"The title remains the same after visiting in private window");
|
||||
});
|
||||
|
||||
|
|
|
@ -18,78 +18,48 @@ add_task(async function test() {
|
|||
}
|
||||
|
||||
await cleanup();
|
||||
|
||||
let deferredFirst = PromiseUtils.defer();
|
||||
let deferredSecond = PromiseUtils.defer();
|
||||
let deferredThird = PromiseUtils.defer();
|
||||
|
||||
let testNumber = 0;
|
||||
let historyObserver = {
|
||||
onTitleChanged(aURI, aPageTitle) {
|
||||
if (aURI.spec != TEST_URL)
|
||||
return;
|
||||
switch (++testNumber) {
|
||||
case 1:
|
||||
// The first time that the page is loaded
|
||||
deferredFirst.resolve(aPageTitle);
|
||||
break;
|
||||
case 2:
|
||||
// The second time that the page is loaded
|
||||
deferredSecond.resolve(aPageTitle);
|
||||
break;
|
||||
case 3:
|
||||
// After clean up
|
||||
deferredThird.resolve(aPageTitle);
|
||||
break;
|
||||
default:
|
||||
// Checks that opening the page in a private window should not fire a
|
||||
// title change.
|
||||
ok(false, "Title changed. Unexpected pass: " + testNumber);
|
||||
}
|
||||
},
|
||||
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onVisit() {},
|
||||
onDeleteURI() {},
|
||||
onClearHistory() {},
|
||||
onPageChanged() {},
|
||||
onDeleteVisits() {},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
|
||||
};
|
||||
PlacesUtils.history.addObserver(historyObserver);
|
||||
|
||||
registerCleanupFunction(cleanup);
|
||||
|
||||
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
|
||||
let aPageTitle = await deferredFirst.promise;
|
||||
// The first time that the page is loaded
|
||||
is(aPageTitle, "No Cookie",
|
||||
registerCleanupFunction(async () => {
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
let promiseTitleChanged = PlacesTestUtils.waitForNotification(
|
||||
"onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
|
||||
await promiseTitleChanged;
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, "No Cookie",
|
||||
"The page should be loaded without any cookie for the first time");
|
||||
|
||||
win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
|
||||
aPageTitle = await deferredSecond.promise;
|
||||
// The second time that the page is loaded
|
||||
is(aPageTitle, "Cookie",
|
||||
promiseTitleChanged = PlacesTestUtils.waitForNotification(
|
||||
"onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
|
||||
await promiseTitleChanged;
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, "Cookie",
|
||||
"The page should be loaded with a cookie for the second time");
|
||||
|
||||
await cleanup();
|
||||
|
||||
win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
|
||||
aPageTitle = await deferredThird.promise;
|
||||
// After clean up
|
||||
is(aPageTitle, "No Cookie",
|
||||
promiseTitleChanged = PlacesTestUtils.waitForNotification(
|
||||
"onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
|
||||
await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
|
||||
await promiseTitleChanged;
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, "No Cookie",
|
||||
"The page should be loaded without any cookie again");
|
||||
|
||||
// Reopen the page in a private browser window, it should not notify a title
|
||||
// change.
|
||||
let win2 = await BrowserTestUtils.openNewBrowserWindow({private: true});
|
||||
|
||||
let private_tab = win2.gBrowser.addTab(TEST_URL);
|
||||
win2.gBrowser.selectedTab = private_tab;
|
||||
await BrowserTestUtils.browserLoaded(private_tab.linkedBrowser);
|
||||
|
||||
// Cleanup
|
||||
await cleanup();
|
||||
PlacesUtils.history.removeObserver(historyObserver);
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
registerCleanupFunction(async () => {
|
||||
let promisePBExit = TestUtils.topicObserved("last-pb-context-exited");
|
||||
await BrowserTestUtils.closeWindow(win2);
|
||||
await promisePBExit;
|
||||
});
|
||||
|
||||
await BrowserTestUtils.openNewForegroundTab(win2.gBrowser, TEST_URL);
|
||||
// Wait long enough to be sure history didn't set a title.
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
is((await PlacesUtils.history.fetch(TEST_URL)).title, "No Cookie",
|
||||
"The title remains the same after visiting in private window");
|
||||
});
|
||||
|
|
|
@ -2,8 +2,13 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var {PromiseUtils} = Cu.import("resource://gre/modules/PromiseUtils.jsm", {});
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TestUtils",
|
||||
"resource://testing-common/TestUtils.jsm");
|
||||
|
||||
function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
let win = OpenBrowserWindow(aOptions);
|
||||
|
|
|
@ -69,7 +69,7 @@ FormAutofillHandler.prototype = {
|
|||
creditCardFieldDetails: null,
|
||||
|
||||
get isValidAddressForm() {
|
||||
return this.addressFieldDetails.length > FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD;
|
||||
return this.addressFieldDetails.length >= FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD;
|
||||
},
|
||||
|
||||
get isValidCreditCardForm() {
|
||||
|
|
|
@ -117,14 +117,12 @@ const TESTCASES = [
|
|||
<input id="given-name" autocomplete="shipping given-name">
|
||||
<input id="family-name" autocomplete="shipping family-name">
|
||||
<input id="street-addr" autocomplete="shipping street-address">
|
||||
<input id="city" autocomplete="shipping address-level2">
|
||||
<input id="cc-number" autocomplete="shipping cc-number">
|
||||
</form>`,
|
||||
addressFieldDetails: [
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
|
||||
],
|
||||
creditCardFieldDetails: [
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "cc-number"},
|
||||
|
@ -139,7 +137,6 @@ const TESTCASES = [
|
|||
document: `<form>
|
||||
<input id="given-name" autocomplete="shipping given-name">
|
||||
<input autocomplete="shipping address-level2">
|
||||
<select autocomplete="shipping country"></select>
|
||||
<input id="cc-name" autocomplete="cc-name">
|
||||
<input id="cc-exp-month" autocomplete="cc-exp-month">
|
||||
<input id="cc-exp-year" autocomplete="cc-exp-year">
|
||||
|
@ -147,7 +144,6 @@ const TESTCASES = [
|
|||
addressFieldDetails: [
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
|
||||
],
|
||||
creditCardFieldDetails: [
|
||||
{"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"},
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that the CssDocs tooltip of the ruleview can be closed when pressing the Escape
|
||||
* key.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const {setBaseCssDocsUrl} =
|
||||
require("devtools/client/shared/widgets/MdnDocsWidget");
|
||||
|
||||
const PROPERTYNAME = "color";
|
||||
|
||||
const TEST_URI = `
|
||||
<html>
|
||||
<body>
|
||||
<div style="color: red">
|
||||
Test "Show MDN Docs" closes on escape
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
/**
|
||||
* Test that the tooltip is hidden when we press Escape
|
||||
*/
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("div", inspector);
|
||||
|
||||
setBaseCssDocsUrl(URL_ROOT);
|
||||
|
||||
info("Retrieve a valid anchor for the CssDocs tooltip");
|
||||
let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
|
||||
|
||||
info("Showing the MDN docs tooltip");
|
||||
let cssDocs = view.tooltips.getTooltip("cssDocs");
|
||||
let onShown = cssDocs.tooltip.once("shown");
|
||||
cssDocs.show(nameSpan, PROPERTYNAME);
|
||||
yield onShown;
|
||||
ok(true, "The MDN docs tooltip was shown");
|
||||
|
||||
info("Simulate pressing the 'Escape' key");
|
||||
let onHidden = cssDocs.tooltip.once("hidden");
|
||||
EventUtils.sendKey("escape");
|
||||
yield onHidden;
|
||||
ok(true, "The MDN docs tooltip was hidden on pressing 'escape'");
|
||||
});
|
|
@ -180,6 +180,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
|
|||
[browser_inspector_search-reserved.js]
|
||||
[browser_inspector_search-selection.js]
|
||||
[browser_inspector_search-sidebar.js]
|
||||
[browser_inspector_search-suggests-ids-and-classes.js]
|
||||
[browser_inspector_select-docshell.js]
|
||||
[browser_inspector_select-last-selected.js]
|
||||
[browser_inspector_search-navigation.js]
|
||||
|
|
|
@ -17,12 +17,12 @@ const KEY_STATES = [
|
|||
["s", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["p", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["a", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["n", []],
|
||||
["n", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
[" ", [["span div", 1]]],
|
||||
// mixed tag/class/id suggestions only work for the first word
|
||||
["d", [["span div", 1]]],
|
||||
["VK_BACK_SPACE", [["span div", 1]]],
|
||||
["VK_BACK_SPACE", []],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
|
||||
|
@ -77,8 +77,6 @@ add_task(function* () {
|
|||
for (let i = 0; i < expectedSuggestions.length; i++) {
|
||||
is(expectedSuggestions[i][0], actualSuggestions[i].label,
|
||||
"The suggestion at " + i + "th index is correct.");
|
||||
is(expectedSuggestions[i][1] || 1, actualSuggestions[i].count,
|
||||
"The count for suggestion at " + i + "th index is correct.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1147,8 +1147,8 @@ nsDocShell::ConvertDocShellLoadInfoToLoadType(
|
|||
loadType = LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE;
|
||||
break;
|
||||
case nsIDocShellLoadInfo::loadReloadCharsetChangeBypassProxyAndCache:
|
||||
break;
|
||||
loadType = LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE;
|
||||
break;
|
||||
case nsIDocShellLoadInfo::loadReloadBypassCache:
|
||||
loadType = LOAD_RELOAD_BYPASS_CACHE;
|
||||
break;
|
||||
|
|
|
@ -1176,8 +1176,7 @@ KeyframeEffectReadOnly::GetProperties(
|
|||
// a) this is the last segment, or
|
||||
// b) the next segment's from-value differs.
|
||||
if (segmentIdx == segmentLen - 1 ||
|
||||
property.mSegments[segmentIdx + 1].mFromValue.mGecko !=
|
||||
segment.mToValue.mGecko) {
|
||||
property.mSegments[segmentIdx + 1].mFromValue != segment.mToValue) {
|
||||
binding_detail::FastAnimationPropertyValueDetails toValue;
|
||||
CreatePropertyValue(property.mProperty, segment.mToKey,
|
||||
Nothing(), segment.mToValue,
|
||||
|
|
|
@ -139,6 +139,10 @@ ChromiumCDMChild::Allocate(uint32_t aCapacity)
|
|||
ToString(mBuffers).get());
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
|
||||
if (mBuffers.IsEmpty()) {
|
||||
Unused << SendIncreaseShmemPoolSize();
|
||||
}
|
||||
|
||||
// Find the shmem with the least amount of wasted space if we were to
|
||||
// select it for this sized allocation. We need to do this because shmems
|
||||
// for decrypted audio as well as video frames are both stored in this
|
||||
|
|
|
@ -208,6 +208,7 @@ ChromiumCDMParent::InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer,
|
|||
bool
|
||||
ChromiumCDMParent::SendBufferToCDM(uint32_t aSizeInBytes)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::SendBufferToCDM() size=%" PRIu32, aSizeInBytes);
|
||||
Shmem shmem;
|
||||
if (!AllocShmem(aSizeInBytes, Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
|
||||
return false;
|
||||
|
@ -617,6 +618,36 @@ ChromiumCDMParent::RecvDecrypted(const uint32_t& aId,
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvIncreaseShmemPoolSize()
|
||||
{
|
||||
GMP_LOG("%s(this=%p) limit=%" PRIu32 " active=%" PRIu32,
|
||||
__func__,
|
||||
this,
|
||||
mVideoShmemLimit,
|
||||
mVideoShmemsActive);
|
||||
|
||||
// Put an upper limit on the number of shmems we tolerate the CDM asking
|
||||
// for, to prevent a memory blow-out. In practice, we expect the CDM to
|
||||
// need less than 5, but some encodings require more.
|
||||
// We'd expect CDMs to not have video frames larger than 720p-1080p
|
||||
// (due to DRM robustness requirements), which is about 1.5MB-3MB per
|
||||
// frame.
|
||||
if (mVideoShmemLimit > 50) {
|
||||
mDecodePromise.RejectIfExists(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("Failled to ensure CDM has enough shmems.")),
|
||||
__func__);
|
||||
Shutdown();
|
||||
return IPC_OK();
|
||||
}
|
||||
mVideoShmemLimit++;
|
||||
|
||||
EnsureSufficientShmems(mVideoFrameBufferSize);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
bool
|
||||
ChromiumCDMParent::PurgeShmems()
|
||||
{
|
||||
|
@ -675,10 +706,10 @@ ChromiumCDMParent::EnsureSufficientShmems(size_t aVideoFrameSize)
|
|||
// We also have a failure recovery mechanism; if the CDM asks for more
|
||||
// buffers than we have shmem's available, ChromiumCDMChild gives the
|
||||
// CDM a non-shared memory buffer, and returns the frame to the parent
|
||||
// in an nsTArray<uint8_t> instead of a shmem. Every time this happens,
|
||||
// the parent sends an extra shmem to the CDM process for it to add to the
|
||||
// set of shmems with which to return output. Via this mechanism we should
|
||||
// recover from incorrectly predicting how many shmems to pre-allocate.
|
||||
// in an nsTArray<uint8_t> instead of a shmem. The child then sends a
|
||||
// message to the parent asking it to increase the number of shmems in
|
||||
// the pool. Via this mechanism we should recover from incorrectly
|
||||
// predicting how many shmems to pre-allocate.
|
||||
//
|
||||
// At decoder start up, we guess how big the shmems need to be based on
|
||||
// the video frame dimensions. If we guess wrong, the CDM will follow
|
||||
|
@ -692,17 +723,6 @@ ChromiumCDMParent::EnsureSufficientShmems(size_t aVideoFrameSize)
|
|||
return false;
|
||||
}
|
||||
mVideoFrameBufferSize = aVideoFrameSize;
|
||||
} else {
|
||||
// Put an upper limit on the number of shmems we tolerate the CDM asking
|
||||
// for, to prevent a memory blow-out. In practice, we expect the CDM to
|
||||
// need less than 5, but some encodings require more.
|
||||
// We'd expect CDMs to not have video frames larger than 720p-1080p
|
||||
// (due to DRM robustness requirements), which is about 1.5MB-3MB per
|
||||
// frame.
|
||||
if (mVideoShmemLimit > 50) {
|
||||
return false;
|
||||
}
|
||||
mVideoShmemLimit++;
|
||||
}
|
||||
|
||||
while (mVideoShmemsActive < mVideoShmemLimit) {
|
||||
|
@ -722,7 +742,9 @@ ipc::IPCResult
|
|||
ChromiumCDMParent::RecvDecodedData(const CDMVideoFrame& aFrame,
|
||||
nsTArray<uint8_t>&& aData)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p)", this);
|
||||
GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p) time=%" PRId64,
|
||||
this,
|
||||
aFrame.mTimestamp());
|
||||
|
||||
if (mIsShutdown || mDecodePromise.IsEmpty()) {
|
||||
return IPC_OK();
|
||||
|
@ -925,8 +947,15 @@ ChromiumCDMParent::InitializeVideoDecoder(
|
|||
__func__);
|
||||
}
|
||||
|
||||
// The Widevine CDM version 1.4.8.970 and above contain a video decoder that
|
||||
// does not optimally allocate video frames; it requests buffers much larger
|
||||
// than required. The exact formula the CDM uses to calculate their frame
|
||||
// sizes isn't obvious, but they normally request around or slightly more
|
||||
// than 1.5X the optimal amount. So pad the size of buffers we allocate so
|
||||
// that we're likely to have buffers big enough to accomodate the CDM's weird
|
||||
// frame size calculation.
|
||||
const size_t bufferSize =
|
||||
I420FrameBufferSizePadded(aInfo.mImage.width, aInfo.mImage.height);
|
||||
1.7 * I420FrameBufferSizePadded(aInfo.mImage.width, aInfo.mImage.height);
|
||||
if (bufferSize <= 0) {
|
||||
return MediaDataDecoder::InitPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
|
|
|
@ -126,6 +126,7 @@ protected:
|
|||
ipc::IPCResult RecvShutdown() override;
|
||||
ipc::IPCResult RecvResetVideoDecoderComplete() override;
|
||||
ipc::IPCResult RecvDrainComplete() override;
|
||||
ipc::IPCResult RecvIncreaseShmemPoolSize() override;
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
bool SendBufferToCDM(uint32_t aSizeInBytes);
|
||||
|
||||
|
|
|
@ -106,6 +106,8 @@ parent:
|
|||
async DrainComplete();
|
||||
|
||||
async Shutdown();
|
||||
|
||||
async IncreaseShmemPoolSize();
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -504,7 +504,7 @@ class SrcNoteLineScanner
|
|||
ptrdiff_t nextOffset;
|
||||
while ((nextOffset = offset + SN_DELTA(sn)) <= relpc && !SN_IS_TERMINATOR(sn)) {
|
||||
offset = nextOffset;
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE || type == SRC_NEWLINE) {
|
||||
if (type == SRC_SETLINE)
|
||||
lineno = GetSrcNoteOffset(sn, 0);
|
||||
|
|
|
@ -3286,7 +3286,7 @@ js::PCToLineNumber(unsigned startLine, jssrcnote* notes, jsbytecode* code, jsbyt
|
|||
if (offset > target)
|
||||
break;
|
||||
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE) {
|
||||
lineno = unsigned(GetSrcNoteOffset(sn, 0));
|
||||
column = 0;
|
||||
|
@ -3338,7 +3338,7 @@ js::LineNumberToPC(JSScript* script, unsigned target)
|
|||
}
|
||||
}
|
||||
offset += SN_DELTA(sn);
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE) {
|
||||
lineno = unsigned(GetSrcNoteOffset(sn, 0));
|
||||
} else if (type == SRC_NEWLINE) {
|
||||
|
@ -3357,7 +3357,7 @@ js::GetScriptLineExtent(JSScript* script)
|
|||
unsigned lineno = script->lineno();
|
||||
unsigned maxLineNo = lineno;
|
||||
for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE)
|
||||
lineno = unsigned(GetSrcNoteOffset(sn, 0));
|
||||
else if (type == SRC_NEWLINE)
|
||||
|
|
|
@ -2379,7 +2379,7 @@ SrcNotes(JSContext* cx, HandleScript script, Sprinter* sp)
|
|||
for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
||||
unsigned delta = SN_DELTA(sn);
|
||||
offset += delta;
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
const char* name = js_SrcNoteSpec[type].name;
|
||||
if (!sp->jsprintf("%3u: %4u %5u [%4u] %-8s",
|
||||
unsigned(sn - notes), lineno, offset, delta, name))
|
||||
|
|
|
@ -189,7 +189,7 @@ LCovSource::writeScript(JSScript* script)
|
|||
if (snpc <= pc) {
|
||||
size_t oldLine = lineno;
|
||||
while (!SN_IS_TERMINATOR(sn) && snpc <= pc) {
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE)
|
||||
lineno = size_t(GetSrcNoteOffset(sn, 0));
|
||||
else if (type == SRC_NEWLINE)
|
||||
|
|
|
@ -5935,7 +5935,7 @@ class BytecodeRangeWithPosition : private BytecodeRange
|
|||
*/
|
||||
jsbytecode *lastLinePC = nullptr;
|
||||
while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
SrcNoteType type = SN_TYPE(sn);
|
||||
if (type == SRC_COLSPAN) {
|
||||
ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
|
||||
MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
|
||||
|
|
|
@ -3910,8 +3910,8 @@ nsLayoutUtils::BinarySearchForPosition(DrawTarget* aDrawTarget,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
AddBoxesForFrame(nsIFrame* aFrame,
|
||||
void
|
||||
nsLayoutUtils::AddBoxesForFrame(nsIFrame* aFrame,
|
||||
nsLayoutUtils::BoxCallback* aCallback)
|
||||
{
|
||||
nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
|
||||
|
|
|
@ -1154,6 +1154,11 @@ public:
|
|||
*/
|
||||
static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback);
|
||||
|
||||
/**
|
||||
* Like GetAllInFlowBoxes, but doesn't include continuations.
|
||||
*/
|
||||
static void AddBoxesForFrame(nsIFrame* aFrame, BoxCallback* aCallback);
|
||||
|
||||
/**
|
||||
* Find the first frame descendant of aFrame (including aFrame) which is
|
||||
* not an anonymous frame that getBoxQuads/getClientRects should ignore.
|
||||
|
|
|
@ -8581,6 +8581,12 @@ bool nsDisplayMask::TryMerge(nsDisplayItem* aItem)
|
|||
if (aItem->GetType() != TYPE_MASK)
|
||||
return false;
|
||||
|
||||
// Do not merge items for box-decoration-break:clone elements,
|
||||
// since each box should have its own mask in that case.
|
||||
if (mFrame->StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// items for the same content element should be merged into a single
|
||||
// compositing group
|
||||
// aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
span {
|
||||
padding: 0em 1em;
|
||||
margin-left: 10px;
|
||||
font: 24px sans-serif;
|
||||
line-height: 2;
|
||||
clip-path: url(#path);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span>The</span><br>
|
||||
<span>quick</span><br>
|
||||
<span>orange fox</span>
|
||||
|
||||
<svg height="0">
|
||||
<defs>
|
||||
<clipPath id="path" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0" y="0" width="1" height="0.5"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test of box-decoration-break:clone with clip-path</title>
|
||||
<style>
|
||||
.clone {
|
||||
box-decoration-break: clone;
|
||||
}
|
||||
span {
|
||||
padding: 0em 1em;
|
||||
margin-left: 10px;
|
||||
font: 24px sans-serif;
|
||||
line-height: 2;
|
||||
clip-path: url(#path);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span class="clone">The<br>quick<br>orange fox</span>
|
||||
|
||||
<svg height="0">
|
||||
<defs>
|
||||
<clipPath id="path" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0" y="0" width="1" height="0.5"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
span {
|
||||
padding: 0em 1em;
|
||||
margin-left: 10px;
|
||||
font: 24px sans-serif;
|
||||
line-height: 2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span>The</span>
|
||||
</body>
|
|
@ -0,0 +1,31 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test of box-decoration-break:slice with clip-path</title>
|
||||
<style>
|
||||
span {
|
||||
padding: 0em 1em;
|
||||
margin-left: 10px;
|
||||
font: 24px sans-serif;
|
||||
line-height: 2;
|
||||
clip-path: url(#path);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span>The<br>quick<br>orange fox</span>
|
||||
|
||||
<svg height="0">
|
||||
<defs>
|
||||
<clipPath id="path" clipPathUnits="objectBoundingBox">
|
||||
<rect x="0" y="0" width="1" height="0.3"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
|
@ -56,6 +56,9 @@ skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-differenc
|
|||
|
||||
== border-radius-01.html pass.svg
|
||||
|
||||
== box-decoration-break-clone.html box-decoration-break-clone-ref.html
|
||||
== box-decoration-break-slice.html box-decoration-break-slice-ref.html
|
||||
|
||||
== clip-01.svg pass.svg
|
||||
== clip-02a.svg clip-02-ref.svg
|
||||
== clip-02b.svg clip-02-ref.svg
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
window.onload = () => {
|
||||
a = document.createElement("p")
|
||||
document.documentElement.appendChild(a)
|
||||
a.animate([])
|
||||
document.designMode = 'on'
|
||||
document.documentElement.appendChild(document.createElement("div"))
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<div id="test" contenteditable>
|
||||
<div id="first"></div>
|
||||
</div>
|
||||
<script>
|
||||
document.body.offsetTop;
|
||||
let anim = document.createElement('div');
|
||||
anim.animate({ color: ['red', 'green' ] }, 1000);
|
||||
first.appendChild(anim);
|
||||
|
||||
let child = document.createElement('span');
|
||||
child.innerText = 'text';
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
anim.appendChild(child);
|
||||
test.appendChild(anim);
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<style>
|
||||
@keyframes anim {
|
||||
to { background-color: green; }
|
||||
}
|
||||
.foo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: red;
|
||||
animation: anim 10s ease;
|
||||
}
|
||||
</style>
|
||||
<div id="test" contenteditable>
|
||||
</div>
|
||||
<script>
|
||||
document.documentElement.offsetTop;
|
||||
test.innerHTML = '<div class="foo">Some random element</div>';
|
||||
</script>
|
|
@ -185,3 +185,6 @@ load link-transition-before.html
|
|||
load 1381682.html
|
||||
load 1382672.html
|
||||
load 1382710.html
|
||||
load 1383001.html
|
||||
load 1383001-2.html
|
||||
load 1383319.html
|
||||
|
|
|
@ -137,6 +137,21 @@ GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation,
|
|||
return collector.GetResult() + aFirstContinuationToUserSpace;
|
||||
}
|
||||
|
||||
static nsRect
|
||||
GetPreEffectsVisualOverflow(nsIFrame* aFirstContinuation,
|
||||
nsIFrame* aCurrentFrame,
|
||||
const nsPoint& aFirstContinuationToUserSpace)
|
||||
{
|
||||
PreEffectsVisualOverflowCollector collector(aFirstContinuation,
|
||||
nullptr,
|
||||
nsRect(),
|
||||
false);
|
||||
// Compute overflow areas of current frame relative to aFirstContinuation:
|
||||
nsLayoutUtils::AddBoxesForFrame(aCurrentFrame, &collector);
|
||||
// Return the result in user space:
|
||||
return collector.GetResult() + aFirstContinuationToUserSpace;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
|
||||
|
@ -211,9 +226,16 @@ nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
|
|||
nsIFrame* firstFrame =
|
||||
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
|
||||
// 'r' is in "user space":
|
||||
nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
|
||||
nsRect r;
|
||||
if (aNonSVGFrame->StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone) {
|
||||
r = GetPreEffectsVisualOverflow(firstFrame, aNonSVGFrame,
|
||||
GetOffsetToBoundingBox(firstFrame));
|
||||
} else {
|
||||
r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
|
||||
GetOffsetToBoundingBox(firstFrame),
|
||||
false);
|
||||
}
|
||||
|
||||
return nsLayoutUtils::RectToGfxRect(r,
|
||||
aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
|
|
@ -675,12 +675,20 @@ function OnDocumentLoad(event)
|
|||
// Ignore load events for subframes.
|
||||
return;
|
||||
|
||||
if (gClearingForAssertionCheck &&
|
||||
currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
|
||||
if (gClearingForAssertionCheck) {
|
||||
if (currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
|
||||
DoAssertionCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
// It's likely the previous test document reloads itself and causes the
|
||||
// attempt of loading blank page fails. In this case we should retry
|
||||
// loading the blank page.
|
||||
LogInfo("Retry loading a blank page");
|
||||
LoadURI(BLANK_URL_FOR_CLEARING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentDoc.location.href != gCurrentURL) {
|
||||
LogInfo("OnDocumentLoad fired for previous document");
|
||||
// Ignore load events for previous documents.
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
app:navigationIcon="@drawable/abc_ic_clear_mtrl_alpha"
|
||||
app:subtitleTextColor="@android:color/white"
|
||||
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
app:title="@string/bookmark_edit_title"
|
||||
app:titleTextColor="@android:color/white" />
|
||||
|
||||
<LinearLayout
|
||||
|
|
|
@ -232,8 +232,10 @@ public class BookmarkEditFragment extends DialogFragment implements SelectFolder
|
|||
nameText.setText(bookmark.title);
|
||||
|
||||
if (bookmark.type == Bookmarks.TYPE_FOLDER) {
|
||||
toolbar.setTitle(R.string.bookmark_edit_folder_title);
|
||||
locationLayout.setVisibility(View.GONE);
|
||||
} else {
|
||||
toolbar.setTitle(R.string.bookmark_edit_title);
|
||||
locationLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
locationText.setText(bookmark.url);
|
||||
|
|
|
@ -548,6 +548,7 @@
|
|||
<!ENTITY page_removed "Page removed">
|
||||
|
||||
<!ENTITY bookmark_edit_title "Edit Bookmark">
|
||||
<!ENTITY bookmark_edit_folder_title "Edit Folder">
|
||||
<!ENTITY bookmark_edit_name "Name">
|
||||
<!ENTITY bookmark_edit_location "Location">
|
||||
<!ENTITY bookmark_edit_keyword "Keyword">
|
||||
|
|
|
@ -436,6 +436,7 @@
|
|||
<string name="page_removed">&page_removed;</string>
|
||||
|
||||
<string name="bookmark_edit_title">&bookmark_edit_title;</string>
|
||||
<string name="bookmark_edit_folder_title">&bookmark_edit_folder_title;</string>
|
||||
<string name="bookmark_edit_name">&bookmark_edit_name;</string>
|
||||
<string name="bookmark_edit_location">&bookmark_edit_location;</string>
|
||||
<string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
|
||||
|
|
|
@ -54,7 +54,9 @@ function init() {
|
|||
links.forEach(function(link) {
|
||||
let url = formatter.formatURLPref(link.pref);
|
||||
let element = document.getElementById(link.id);
|
||||
if (element) {
|
||||
element.setAttribute("href", url);
|
||||
}
|
||||
});
|
||||
} catch (ex) {}
|
||||
|
||||
|
|
|
@ -5348,9 +5348,9 @@ pref("urlclassifier.gethashnoise", 4);
|
|||
// Gethash timeout for Safebrowsing.
|
||||
pref("urlclassifier.gethash.timeout_ms", 5000);
|
||||
// Update server response timeout for Safebrowsing.
|
||||
pref("urlclassifier.update.response_timeout_ms", 15000);
|
||||
pref("urlclassifier.update.response_timeout_ms", 30000);
|
||||
// Download update timeout for Safebrowsing.
|
||||
pref("urlclassifier.update.timeout_ms", 60000);
|
||||
pref("urlclassifier.update.timeout_ms", 90000);
|
||||
|
||||
// Name of the about: page contributed by safebrowsing to handle display of error
|
||||
// pages on phishing/malware hits. (bug 399233)
|
||||
|
|
|
@ -228,10 +228,8 @@ impl FetchTaskTarget for IpcSender<FetchResponseMsg> {
|
|||
}
|
||||
|
||||
fn process_response_eof(&mut self, response: &Response) {
|
||||
if response.is_network_error() {
|
||||
// todo: finer grained errors
|
||||
let _ =
|
||||
self.send(FetchResponseMsg::ProcessResponseEOF(Err(NetworkError::Internal("Network error".into()))));
|
||||
if let Some(e) = response.get_network_error() {
|
||||
let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(e.clone())));
|
||||
} else {
|
||||
let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(())));
|
||||
}
|
||||
|
|
|
@ -298,22 +298,16 @@ impl ElementData {
|
|||
self.styles.primary.is_some()
|
||||
}
|
||||
|
||||
/// Returns whether we have any outstanding style invalidation.
|
||||
pub fn has_invalidations(&self) -> bool {
|
||||
self.restyle.hint.has_self_invalidations()
|
||||
}
|
||||
|
||||
/// Returns the kind of restyling that we're going to need to do on this
|
||||
/// element, based of the stored restyle hint.
|
||||
pub fn restyle_kind(&self,
|
||||
shared_context: &SharedStyleContext)
|
||||
-> RestyleKind {
|
||||
pub fn restyle_kind(
|
||||
&self,
|
||||
shared_context: &SharedStyleContext
|
||||
) -> RestyleKind {
|
||||
if shared_context.traversal_flags.for_animation_only() {
|
||||
return self.restyle_kind_for_animation(shared_context);
|
||||
}
|
||||
|
||||
debug_assert!(!self.has_styles() || self.has_invalidations(),
|
||||
"Should've stopped earlier");
|
||||
if !self.has_styles() {
|
||||
return RestyleKind::MatchAndCascade;
|
||||
}
|
||||
|
@ -330,14 +324,15 @@ impl ElementData {
|
|||
}
|
||||
|
||||
debug_assert!(hint.has_recascade_self(),
|
||||
"We definitely need to do something!");
|
||||
"We definitely need to do something: {:?}!", hint);
|
||||
return RestyleKind::CascadeOnly;
|
||||
}
|
||||
|
||||
/// Returns the kind of restyling for animation-only restyle.
|
||||
pub fn restyle_kind_for_animation(&self,
|
||||
shared_context: &SharedStyleContext)
|
||||
-> RestyleKind {
|
||||
fn restyle_kind_for_animation(
|
||||
&self,
|
||||
shared_context: &SharedStyleContext,
|
||||
) -> RestyleKind {
|
||||
debug_assert!(shared_context.traversal_flags.for_animation_only());
|
||||
debug_assert!(self.has_styles(),
|
||||
"Unstyled element shouldn't be traversed during \
|
||||
|
@ -350,8 +345,8 @@ impl ElementData {
|
|||
if hint.has_animation_hint() {
|
||||
return RestyleKind::CascadeWithReplacements(hint & RestyleHint::for_animations());
|
||||
}
|
||||
return RestyleKind::CascadeOnly;
|
||||
|
||||
return RestyleKind::CascadeOnly;
|
||||
}
|
||||
|
||||
/// Return true if important rules are different.
|
||||
|
@ -362,9 +357,11 @@ impl ElementData {
|
|||
/// the check which properties do they want.
|
||||
/// If it costs too much, get_properties_overriding_animations() should return a set
|
||||
/// containing only opacity and transform properties.
|
||||
pub fn important_rules_are_different(&self,
|
||||
pub fn important_rules_are_different(
|
||||
&self,
|
||||
rules: &StrongRuleNode,
|
||||
guards: &StylesheetGuards) -> bool {
|
||||
guards: &StylesheetGuards
|
||||
) -> bool {
|
||||
debug_assert!(self.has_styles());
|
||||
let (important_rules, _custom) =
|
||||
self.styles.primary().rules().get_properties_overriding_animations(&guards);
|
||||
|
|
|
@ -470,19 +470,12 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
|||
/// Flags this element as having handled already its snapshot.
|
||||
unsafe fn set_handled_snapshot(&self);
|
||||
|
||||
/// Returns whether the element's styles are up-to-date.
|
||||
fn has_current_styles(&self, data: &ElementData) -> bool {
|
||||
if self.has_snapshot() && !self.handled_snapshot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.has_styles() && !data.has_invalidations()
|
||||
}
|
||||
|
||||
/// Returns whether the element's styles are up-to-date for |traversal_flags|.
|
||||
fn has_current_styles_for_traversal(&self,
|
||||
fn has_current_styles_for_traversal(
|
||||
&self,
|
||||
data: &ElementData,
|
||||
traversal_flags: TraversalFlags) -> bool {
|
||||
traversal_flags: TraversalFlags,
|
||||
) -> bool {
|
||||
if traversal_flags.for_animation_only() {
|
||||
// In animation-only restyle we never touch snapshots and don't
|
||||
// care about them. But we can't assert '!self.handled_snapshot()'
|
||||
|
@ -499,7 +492,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
|
|||
return false;
|
||||
}
|
||||
|
||||
data.has_styles() && !data.has_invalidations()
|
||||
data.has_styles() && !data.restyle.hint.has_non_animation_invalidations()
|
||||
}
|
||||
|
||||
/// Flags an element and its ancestors with a given `DescendantsBit`.
|
||||
|
|
|
@ -65,8 +65,12 @@ impl RestyleHint {
|
|||
}
|
||||
|
||||
/// Returns whether we need to restyle this element.
|
||||
pub fn has_self_invalidations(&self) -> bool {
|
||||
self.intersects(RESTYLE_SELF | RECASCADE_SELF | Self::replacements())
|
||||
pub fn has_non_animation_invalidations(&self) -> bool {
|
||||
self.intersects(
|
||||
RESTYLE_SELF |
|
||||
RECASCADE_SELF |
|
||||
(Self::replacements() & !Self::for_animations())
|
||||
)
|
||||
}
|
||||
|
||||
/// Propagates this restyle hint to a child element.
|
||||
|
|
|
@ -46,6 +46,11 @@ def to_camel_case_lower(ident):
|
|||
return camel[0].lower() + camel[1:]
|
||||
|
||||
|
||||
# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
|
||||
def to_idl_name(ident):
|
||||
return re.sub("-([a-z])", lambda m: m.group(1).upper(), ident)
|
||||
|
||||
|
||||
def parse_aliases(value):
|
||||
aliases = {}
|
||||
for pair in value.split():
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||
|
||||
<% from data import SYSTEM_FONT_LONGHANDS %>
|
||||
<% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
|
||||
|
||||
use app_units::Au;
|
||||
use cssparser::{Parser, RGBA};
|
||||
|
@ -23,7 +23,8 @@ use properties::longhands::transform::computed_value::ComputedOperation as Trans
|
|||
use properties::longhands::transform::computed_value::T as TransformList;
|
||||
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
|
||||
use properties::longhands::visibility::computed_value::T as Visibility;
|
||||
#[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
|
||||
#[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
|
||||
#[cfg(feature = "gecko")] use properties::{ShorthandId};
|
||||
use selectors::parser::SelectorParseError;
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp;
|
||||
|
@ -3204,3 +3205,56 @@ impl Animatable for AnimatedFilterList {
|
|||
Ok(square_distance.sqrt())
|
||||
}
|
||||
}
|
||||
|
||||
/// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
|
||||
/// shorthands with fewer components are sorted before shorthands with more components,
|
||||
/// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order].
|
||||
///
|
||||
/// Using this allows us to prioritize values specified by longhands (or smaller
|
||||
/// shorthand subsets) when longhands and shorthands are both specified on the one keyframe.
|
||||
///
|
||||
/// Example orderings that result from this:
|
||||
///
|
||||
/// margin-left, margin
|
||||
///
|
||||
/// and:
|
||||
///
|
||||
/// border-top-color, border-color, border-top, border
|
||||
///
|
||||
/// [property-order] https://w3c.github.io/web-animations/#calculating-computed-keyframes
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
|
||||
match (a.as_shorthand(), b.as_shorthand()) {
|
||||
// Within shorthands, sort by the number of subproperties, then by IDL name.
|
||||
(Ok(a), Ok(b)) => {
|
||||
let subprop_count_a = a.longhands().len();
|
||||
let subprop_count_b = b.longhands().len();
|
||||
subprop_count_a.cmp(&subprop_count_b).then_with(
|
||||
|| get_idl_name_sort_order(&a).cmp(&get_idl_name_sort_order(&b)))
|
||||
},
|
||||
|
||||
// Longhands go before shorthands.
|
||||
(Ok(_), Err(_)) => cmp::Ordering::Greater,
|
||||
(Err(_), Ok(_)) => cmp::Ordering::Less,
|
||||
|
||||
// Both are longhands or custom properties in which case they don't overlap and should
|
||||
// sort equally.
|
||||
_ => cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn get_idl_name_sort_order(shorthand: &ShorthandId) -> u32 {
|
||||
<%
|
||||
# Sort by IDL name.
|
||||
sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
|
||||
|
||||
# Annotate with sorted position
|
||||
sorted_shorthands = [(p, position) for position, p in enumerate(sorted_shorthands)]
|
||||
%>
|
||||
match *shorthand {
|
||||
% for property, position in sorted_shorthands:
|
||||
ShorthandId::${property.camel_case} => ${position},
|
||||
% endfor
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,17 +52,7 @@ where
|
|||
{
|
||||
let parent_el = element.inheritance_parent();
|
||||
let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
|
||||
let parent_style = parent_data.as_ref().map(|d| {
|
||||
// Sometimes Gecko eagerly styles things without processing
|
||||
// pending restyles first. In general we'd like to avoid this,
|
||||
// but there can be good reasons (for example, needing to
|
||||
// construct a frame for some small piece of newly-added
|
||||
// content in order to do something specific with that frame,
|
||||
// but not wanting to flush all of layout).
|
||||
debug_assert!(cfg!(feature = "gecko") ||
|
||||
parent_el.unwrap().has_current_styles(d));
|
||||
d.styles.primary()
|
||||
});
|
||||
let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
|
||||
|
||||
let mut layout_parent_el = parent_el.clone();
|
||||
let layout_parent_data;
|
||||
|
|
|
@ -374,9 +374,8 @@ pub trait DomTraversal<E: TElement> : Sync {
|
|||
parent: E,
|
||||
parent_data: &ElementData,
|
||||
) -> bool {
|
||||
// See the comment on `cascade_node` for why we allow this on Gecko.
|
||||
debug_assert!(cfg!(feature = "gecko") ||
|
||||
parent.has_current_styles(parent_data));
|
||||
parent.has_current_styles_for_traversal(parent_data, context.shared.traversal_flags));
|
||||
|
||||
// If the parent computed display:none, we don't style the subtree.
|
||||
if parent_data.styles.is_display_none() {
|
||||
|
|
|
@ -83,6 +83,7 @@ use style::gecko_bindings::structs::nsCSSValueSharedList;
|
|||
use style::gecko_bindings::structs::nsCompatibility;
|
||||
use style::gecko_bindings::structs::nsIDocument;
|
||||
use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
|
||||
use style::gecko_bindings::structs::nsTArray;
|
||||
use style::gecko_bindings::structs::nsresult;
|
||||
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
|
||||
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
|
||||
|
@ -94,9 +95,10 @@ use style::parallel;
|
|||
use style::parser::ParserContext;
|
||||
use style::properties::{ComputedValues, Importance};
|
||||
use style::properties::{IS_FIELDSET_CONTENT, LonghandIdSet};
|
||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
|
||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId};
|
||||
use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
|
||||
use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
|
||||
use style::properties::animated_properties::compare_property_priority;
|
||||
use style::properties::parse_one_declaration_into;
|
||||
use style::rule_tree::StyleSource;
|
||||
use style::selector_parser::PseudoElementCascadeType;
|
||||
|
@ -273,22 +275,27 @@ pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed,
|
|||
(Root::Normal, Restyle::ForThrottledAnimationFlush)
|
||||
=> TraversalFlags::empty(),
|
||||
(Root::UnstyledChildrenOnly, Restyle::Normal) |
|
||||
(Root::UnstyledChildrenOnly, Restyle::ForNewlyBoundElement) |
|
||||
(Root::UnstyledChildrenOnly, Restyle::ForThrottledAnimationFlush)
|
||||
(Root::UnstyledChildrenOnly, Restyle::ForNewlyBoundElement)
|
||||
=> UNSTYLED_CHILDREN_ONLY,
|
||||
(Root::Normal, Restyle::ForCSSRuleChanges) => FOR_CSS_RULE_CHANGES,
|
||||
(Root::Normal, Restyle::ForReconstruct) => FOR_RECONSTRUCT,
|
||||
_ => panic!("invalid combination of TraversalRootBehavior and TraversalRestyleBehavior"),
|
||||
};
|
||||
|
||||
let needs_animation_only_restyle = element.has_animation_only_dirty_descendants() ||
|
||||
// It makes no sense to do an animation restyle when we're restyling
|
||||
// newly-inserted content.
|
||||
if !traversal_flags.contains(UNSTYLED_CHILDREN_ONLY) {
|
||||
let needs_animation_only_restyle =
|
||||
element.has_animation_only_dirty_descendants() ||
|
||||
element.has_animation_restyle_hints();
|
||||
|
||||
if needs_animation_only_restyle {
|
||||
traverse_subtree(element,
|
||||
raw_data,
|
||||
traversal_flags | ANIMATION_ONLY,
|
||||
unsafe { &*snapshots });
|
||||
}
|
||||
}
|
||||
|
||||
if restyle_behavior == Restyle::ForThrottledAnimationFlush {
|
||||
return element.has_animation_only_dirty_descendants() ||
|
||||
|
@ -2812,7 +2819,7 @@ pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed,
|
|||
TraversalFlags::empty()
|
||||
};
|
||||
debug_assert!(element.has_current_styles_for_traversal(&*data, flags),
|
||||
"Resolving style on element without current styles");
|
||||
"Resolving style on {:?} without current styles: {:?}", element, data);
|
||||
data.styles.primary().clone().into()
|
||||
}
|
||||
|
||||
|
@ -2916,6 +2923,53 @@ fn create_context<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
struct PropertyAndIndex {
|
||||
property: PropertyId,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
struct PrioritizedPropertyIter<'a> {
|
||||
properties: &'a nsTArray<PropertyValuePair>,
|
||||
sorted_property_indices: Vec<PropertyAndIndex>,
|
||||
curr: usize,
|
||||
}
|
||||
|
||||
impl<'a> PrioritizedPropertyIter<'a> {
|
||||
pub fn new(properties: &'a nsTArray<PropertyValuePair>) -> PrioritizedPropertyIter {
|
||||
// If we fail to convert a nsCSSPropertyID into a PropertyId we shouldn't fail outright
|
||||
// but instead by treating that property as the 'all' property we make it sort last.
|
||||
let all = PropertyId::Shorthand(ShorthandId::All);
|
||||
|
||||
let mut sorted_property_indices: Vec<PropertyAndIndex> =
|
||||
properties.iter().enumerate().map(|(index, pair)| {
|
||||
PropertyAndIndex {
|
||||
property: PropertyId::from_nscsspropertyid(pair.mProperty)
|
||||
.unwrap_or(all.clone()),
|
||||
index,
|
||||
}
|
||||
}).collect();
|
||||
sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));
|
||||
|
||||
PrioritizedPropertyIter {
|
||||
properties,
|
||||
sorted_property_indices,
|
||||
curr: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PrioritizedPropertyIter<'a> {
|
||||
type Item = &'a PropertyValuePair;
|
||||
|
||||
fn next(&mut self) -> Option<&'a PropertyValuePair> {
|
||||
if self.curr >= self.sorted_property_indices.len() {
|
||||
return None
|
||||
}
|
||||
self.curr += 1;
|
||||
Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeListBorrowed,
|
||||
element: RawGeckoElementBorrowed,
|
||||
|
@ -2946,9 +3000,8 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
|
|||
|
||||
let mut seen = LonghandIdSet::new();
|
||||
|
||||
let iter = keyframe.mPropertyValues.iter();
|
||||
let mut property_index = 0;
|
||||
for property in iter {
|
||||
for property in PrioritizedPropertyIter::new(&keyframe.mPropertyValues) {
|
||||
if simulate_compute_values_failure(property) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ class TestSafeBrowsingInitialDownload(PuppeteerMixin, MarionetteTestCase):
|
|||
self.prefs_provider_update_time.keys(), True)
|
||||
|
||||
try:
|
||||
Wait(self.marionette, timeout=90).until(
|
||||
Wait(self.marionette, timeout=170).until(
|
||||
check_downloaded, message='Not all safebrowsing files have been downloaded')
|
||||
finally:
|
||||
files_on_disk_toplevel = os.listdir(self.safebrowsing_path)
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
expected: FAIL
|
||||
|
||||
[Fieldsets]
|
||||
disabled:
|
||||
if (os == "win") and (version == "10.0.15063"): https://bugzilla.mozilla.org/show_bug.cgi?id=1383056
|
||||
expected:
|
||||
if not debug and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
|
||||
if not debug and not e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
|
||||
if debug and not stylo and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
|
||||
|
||||
if (os == "win") and (version == "10.0.15063"): FAIL
|
||||
|
|
|
@ -1003,7 +1003,6 @@ Barrier.prototype = Object.freeze({
|
|||
if (!isContent) {
|
||||
this.AsyncShutdown.profileChangeTeardown = getPhase("profile-change-teardown");
|
||||
this.AsyncShutdown.profileBeforeChange = getPhase("profile-before-change");
|
||||
this.AsyncShutdown.placesClosingInternalConnection = getPhase("places-will-close-connection");
|
||||
this.AsyncShutdown.sendTelemetry = getPhase("profile-before-change-telemetry");
|
||||
}
|
||||
|
||||
|
|
|
@ -456,8 +456,17 @@ Database::GetStatement(const nsACString& aQuery)
|
|||
already_AddRefed<nsIAsyncShutdownClient>
|
||||
Database::GetClientsShutdown()
|
||||
{
|
||||
MOZ_ASSERT(mClientsShutdown);
|
||||
if (mClientsShutdown)
|
||||
return mClientsShutdown->GetClient();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIAsyncShutdownClient>
|
||||
Database::GetConnectionShutdown()
|
||||
{
|
||||
if (mConnectionShutdown)
|
||||
return mConnectionShutdown->GetClient();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -32,10 +32,6 @@
|
|||
// you should only use this notification, next ones are intended only for
|
||||
// internal Places use.
|
||||
#define TOPIC_PLACES_SHUTDOWN "places-shutdown"
|
||||
// For Internal use only. Fired when connection is about to be closed, only
|
||||
// cleanup tasks should run at this stage, nothing should be added to the
|
||||
// database, nor APIs should be called.
|
||||
#define TOPIC_PLACES_WILL_CLOSE_CONNECTION "places-will-close-connection"
|
||||
// Fired when the connection has gone, nothing will work from now on.
|
||||
#define TOPIC_PLACES_CONNECTION_CLOSED "places-connection-closed"
|
||||
|
||||
|
@ -86,6 +82,11 @@ public:
|
|||
*/
|
||||
already_AddRefed<nsIAsyncShutdownClient> GetClientsShutdown();
|
||||
|
||||
/**
|
||||
* The AsyncShutdown client used by clients of this API to be informed of connection shutdown.
|
||||
*/
|
||||
already_AddRefed<nsIAsyncShutdownClient> GetConnectionShutdown();
|
||||
|
||||
/**
|
||||
* Getter to use when instantiating the class.
|
||||
*
|
||||
|
|
|
@ -750,20 +750,42 @@ var invalidateFrecencies = async function(db, idList) {
|
|||
|
||||
// Inner implementation of History.clear().
|
||||
var clear = async function(db) {
|
||||
await db.executeTransaction(async function() {
|
||||
// Remove all non-bookmarked places entries first, this will speed up the
|
||||
// triggers work.
|
||||
await db.execute(`DELETE FROM moz_places WHERE foreign_count = 0`);
|
||||
await db.execute(`DELETE FROM moz_updatehosts_temp`);
|
||||
|
||||
// Expire orphan icons.
|
||||
await db.executeCached(`DELETE FROM moz_pages_w_icons
|
||||
WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places)`);
|
||||
await db.executeCached(`DELETE FROM moz_icons
|
||||
WHERE root = 0 AND id NOT IN (SELECT icon_id FROM moz_icons_to_pages)`);
|
||||
|
||||
// Expire annotations.
|
||||
await db.execute(`DELETE FROM moz_items_annos WHERE expiration = :expire_session`,
|
||||
{ expire_session: Ci.nsIAnnotationService.EXPIRE_SESSION });
|
||||
await db.execute(`DELETE FROM moz_annos WHERE id in (
|
||||
SELECT a.id FROM moz_annos a
|
||||
LEFT JOIN moz_places h ON a.place_id = h.id
|
||||
WHERE h.id IS NULL
|
||||
OR expiration = :expire_session
|
||||
OR (expiration = :expire_with_history
|
||||
AND h.last_visit_date ISNULL)
|
||||
)`, { expire_session: Ci.nsIAnnotationService.EXPIRE_SESSION,
|
||||
expire_with_history: Ci.nsIAnnotationService.EXPIRE_WITH_HISTORY });
|
||||
|
||||
// Expire inputhistory.
|
||||
await db.execute(`DELETE FROM moz_inputhistory WHERE place_id IN (
|
||||
SELECT i.place_id FROM moz_inputhistory i
|
||||
LEFT JOIN moz_places h ON h.id = i.place_id
|
||||
WHERE h.id IS NULL)`);
|
||||
|
||||
// Remove all history.
|
||||
await db.execute("DELETE FROM moz_historyvisits");
|
||||
|
||||
// Clear the registered embed visits.
|
||||
PlacesUtils.history.clearEmbedVisits();
|
||||
|
||||
// Expiration will take care of orphans.
|
||||
let observers = PlacesUtils.history.getObservers();
|
||||
notify(observers, "onClearHistory");
|
||||
|
||||
// Invalidate frecencies for the remaining places. This must happen
|
||||
// after the notification to ensure it runs enqueued to expiration.
|
||||
await db.execute(
|
||||
`UPDATE moz_places SET frecency =
|
||||
// Invalidate frecencies for the remaining places.
|
||||
await db.execute(`UPDATE moz_places SET frecency =
|
||||
(CASE
|
||||
WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
|
||||
hash("place", "prefix_hi")
|
||||
|
@ -771,7 +793,13 @@ var clear = async function(db) {
|
|||
ELSE -1
|
||||
END)
|
||||
WHERE frecency > 0`);
|
||||
});
|
||||
|
||||
// Clear the registered embed visits.
|
||||
PlacesUtils.history.clearEmbedVisits();
|
||||
|
||||
let observers = PlacesUtils.history.getObservers();
|
||||
notify(observers, "onClearHistory");
|
||||
// Notify frecency change observers.
|
||||
notify(observers, "onManyFrecenciesChanged");
|
||||
};
|
||||
|
|
|
@ -2142,7 +2142,8 @@ function setupDbForShutdown(conn, name) {
|
|||
// Before it can safely close its connection, we need to make sure
|
||||
// that we have closed the high-level connection.
|
||||
try {
|
||||
AsyncShutdown.placesClosingInternalConnection.addBlocker(`${name} closing as part of Places shutdown`,
|
||||
PlacesUtils.history.connectionShutdownClient.jsclient.addBlocker(
|
||||
`${name} closing as part of Places shutdown`,
|
||||
async function() {
|
||||
state = "1. Service has initiated shutdown";
|
||||
|
||||
|
|
|
@ -22,6 +22,19 @@ PlacesShutdownBlocker::PlacesShutdownBlocker(const nsString& aName)
|
|||
if (mCounter > 1) {
|
||||
mName.AppendInt(mCounter);
|
||||
}
|
||||
// Create a barrier that will be exposed to clients through GetClient(), so
|
||||
// they can block Places shutdown.
|
||||
nsCOMPtr<nsIAsyncShutdownService> asyncShutdown = services::GetAsyncShutdown();
|
||||
MOZ_ASSERT(asyncShutdown);
|
||||
if (asyncShutdown) {
|
||||
nsCOMPtr<nsIAsyncShutdownBarrier> barrier;
|
||||
nsresult rv = asyncShutdown->MakeBarrier(mName, getter_AddRefs(barrier));
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
if (NS_SUCCEEDED(rv) && barrier) {
|
||||
mBarrier = new nsMainThreadPtrHolder<nsIAsyncShutdownBarrier>(
|
||||
"PlacesShutdownBlocker::mBarrier", barrier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nsIAsyncShutdownBlocker
|
||||
|
@ -71,39 +84,8 @@ PlacesShutdownBlocker::GetState(nsIPropertyBag** _state)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncShutdownBlocker
|
||||
NS_IMETHODIMP
|
||||
PlacesShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
|
||||
{
|
||||
MOZ_ASSERT(false, "should always be overridden");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
PlacesShutdownBlocker,
|
||||
nsIAsyncShutdownBlocker
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ClientsShutdownBlocker::ClientsShutdownBlocker()
|
||||
: PlacesShutdownBlocker(NS_LITERAL_STRING("Places Clients shutdown"))
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Create a barrier that will be exposed to clients through GetClient(), so
|
||||
// they can block Places shutdown.
|
||||
nsCOMPtr<nsIAsyncShutdownService> asyncShutdown = services::GetAsyncShutdown();
|
||||
MOZ_ASSERT(asyncShutdown);
|
||||
if (asyncShutdown) {
|
||||
nsCOMPtr<nsIAsyncShutdownBarrier> barrier;
|
||||
MOZ_ALWAYS_SUCCEEDS(asyncShutdown->MakeBarrier(mName, getter_AddRefs(barrier)));
|
||||
mBarrier = new nsMainThreadPtrHolder<nsIAsyncShutdownBarrier>(
|
||||
"ClientsShutdownBlocker::mBarrier", barrier);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsIAsyncShutdownClient>
|
||||
ClientsShutdownBlocker::GetClient()
|
||||
PlacesShutdownBlocker::GetClient()
|
||||
{
|
||||
nsCOMPtr<nsIAsyncShutdownClient> client;
|
||||
if (mBarrier) {
|
||||
|
@ -114,7 +96,7 @@ ClientsShutdownBlocker::GetClient()
|
|||
|
||||
// nsIAsyncShutdownBlocker
|
||||
NS_IMETHODIMP
|
||||
ClientsShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
|
||||
PlacesShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
|
||||
|
@ -132,6 +114,28 @@ ClientsShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncShutdownCompletionCallback
|
||||
NS_IMETHODIMP
|
||||
PlacesShutdownBlocker::Done()
|
||||
{
|
||||
MOZ_ASSERT(false, "Should always be overridden");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
PlacesShutdownBlocker,
|
||||
nsIAsyncShutdownBlocker,
|
||||
nsIAsyncShutdownCompletionCallback
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ClientsShutdownBlocker::ClientsShutdownBlocker()
|
||||
: PlacesShutdownBlocker(NS_LITERAL_STRING("Places Clients shutdown"))
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// nsIAsyncShutdownCompletionCallback
|
||||
NS_IMETHODIMP
|
||||
ClientsShutdownBlocker::Done()
|
||||
|
@ -150,10 +154,9 @@ ClientsShutdownBlocker::Done()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(
|
||||
ClientsShutdownBlocker,
|
||||
PlacesShutdownBlocker,
|
||||
nsIAsyncShutdownCompletionCallback
|
||||
PlacesShutdownBlocker
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -165,25 +168,17 @@ ConnectionShutdownBlocker::ConnectionShutdownBlocker(Database* aDatabase)
|
|||
// Do nothing.
|
||||
}
|
||||
|
||||
// nsIAsyncShutdownBlocker
|
||||
// nsIAsyncShutdownCompletionCallback
|
||||
NS_IMETHODIMP
|
||||
ConnectionShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
|
||||
ConnectionShutdownBlocker::Done()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
|
||||
"ConnectionShutdownBlocker::mParentClient", aParentClient);
|
||||
mState = RECEIVED_BLOCK_SHUTDOWN;
|
||||
// At this point all the clients are done, we can stop blocking the shutdown
|
||||
// phase.
|
||||
mState = RECEIVED_DONE;
|
||||
|
||||
// Annotate that Database shutdown started.
|
||||
sIsStarted = true;
|
||||
|
||||
// Fire internal database closing notification.
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
MOZ_ASSERT(os);
|
||||
if (os) {
|
||||
Unused << os->NotifyObservers(nullptr, TOPIC_PLACES_WILL_CLOSE_CONNECTION, nullptr);
|
||||
}
|
||||
mState = NOTIFIED_OBSERVERS_PLACES_WILL_CLOSE_CONNECTION;
|
||||
|
||||
// At this stage, any use of this database is forbidden. Get rid of
|
||||
// `gDatabase`. Note, however, that the database could be
|
||||
// resurrected. This can happen in particular during tests.
|
||||
|
@ -193,6 +188,7 @@ ConnectionShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
|
|||
// Database::Shutdown will invoke Complete once the connection is closed.
|
||||
mDatabase->Shutdown();
|
||||
mState = CALLED_STORAGESHUTDOWN;
|
||||
mBarrier = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,6 @@ class Database;
|
|||
* Database::Init (see Database::mConnectionShutdown).
|
||||
* When profile-before-change is observer by async shutdown, it calls
|
||||
* ConnectionShutdownBlocker::BlockShutdown.
|
||||
* This is the last chance for any Places internal work, like privacy cleanups,
|
||||
* before the connection is closed. This a places-will-close-connection
|
||||
* notification is sent to legacy clients that must complete any operation in
|
||||
* the same tick, since we won't wait for them.
|
||||
* Then the control is passed to Database::Shutdown, that executes some sanity
|
||||
* checks, clears cached statements and proceeds with asyncClose.
|
||||
* Once the connection is definitely closed, Database will call back
|
||||
|
@ -58,13 +54,17 @@ class Database;
|
|||
* A base AsyncShutdown blocker in charge of shutting down Places.
|
||||
*/
|
||||
class PlacesShutdownBlocker : public nsIAsyncShutdownBlocker
|
||||
, public nsIAsyncShutdownCompletionCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIASYNCSHUTDOWNBLOCKER
|
||||
NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
|
||||
|
||||
explicit PlacesShutdownBlocker(const nsString& aName);
|
||||
|
||||
already_AddRefed<nsIAsyncShutdownClient> GetClient();
|
||||
|
||||
/**
|
||||
* `true` if we have not started shutdown, i.e. if
|
||||
* `BlockShutdown()` hasn't been called yet, false otherwise.
|
||||
|
@ -126,18 +126,13 @@ protected:
|
|||
* Blocker also used to wait for clients, through an owned barrier.
|
||||
*/
|
||||
class ClientsShutdownBlocker final : public PlacesShutdownBlocker
|
||||
, public nsIAsyncShutdownCompletionCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
|
||||
|
||||
explicit ClientsShutdownBlocker();
|
||||
|
||||
NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aParentClient) override;
|
||||
|
||||
already_AddRefed<nsIAsyncShutdownClient> GetClient();
|
||||
|
||||
NS_IMETHOD Done() override;
|
||||
private:
|
||||
~ClientsShutdownBlocker() {}
|
||||
};
|
||||
|
@ -152,10 +147,10 @@ public:
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
|
||||
|
||||
NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aParentClient) override;
|
||||
|
||||
explicit ConnectionShutdownBlocker(mozilla::places::Database* aDatabase);
|
||||
|
||||
NS_IMETHOD Done() override;
|
||||
|
||||
private:
|
||||
~ConnectionShutdownBlocker() {}
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ const PREF_PRELOADED_SITES_ENABLED = [ "usepreloadedtopurls.enabled", true ];
|
|||
const PREF_PRELOADED_SITES_EXPIRE_DAYS = [ "usepreloadedtopurls.expire_days", 14 ];
|
||||
|
||||
const PREF_MATCH_BUCKETS = [ "matchBuckets", "general:5,suggestion:Infinity" ];
|
||||
// Will default to matchBuckets if not defined.
|
||||
const PREF_MATCH_BUCKETS_SEARCH = [ "matchBucketsSearch", "" ];
|
||||
|
||||
// AutoComplete query type constants.
|
||||
// Describes the various types of queries that we can process rows for.
|
||||
|
@ -124,6 +126,8 @@ const MATCHTYPE = {
|
|||
// Each bucket is an array containing the following indices:
|
||||
// 0: The match type of the acceptable entries.
|
||||
// 1: available number of slots in this bucket.
|
||||
// There are different matchBuckets definition for different contexts, currently
|
||||
// a general one (_matchBuckets) and a search one (_matchBucketsSearch).
|
||||
//
|
||||
// First buckets. Anything with an Infinity frecency ends up here.
|
||||
const DEFAULT_BUCKETS_BEFORE = [
|
||||
|
@ -508,16 +512,30 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
|
|||
store.preloadedSitesEnabled = prefs.get(...PREF_PRELOADED_SITES_ENABLED);
|
||||
store.preloadedSitesExpireDays = prefs.get(...PREF_PRELOADED_SITES_EXPIRE_DAYS);
|
||||
store.matchBuckets = prefs.get(...PREF_MATCH_BUCKETS);
|
||||
// Convert from the string format to an array.
|
||||
// Convert from pref char format to an array and add the default buckets.
|
||||
try {
|
||||
store.matchBuckets = convertBucketsCharPrefToArray(store.matchBuckets)
|
||||
store.matchBuckets = convertBucketsCharPrefToArray(store.matchBuckets);
|
||||
} catch (ex) {
|
||||
store.matchBuckets = convertBucketsCharPrefToArray(PREF_MATCH_BUCKETS[1]);
|
||||
}
|
||||
// Add the default buckets.
|
||||
store.matchBuckets = [ ...DEFAULT_BUCKETS_BEFORE,
|
||||
...store.matchBuckets,
|
||||
...DEFAULT_BUCKETS_AFTER ];
|
||||
store.matchBucketsSearch = prefs.get(...PREF_MATCH_BUCKETS_SEARCH);
|
||||
// Default to matchBuckets if not defined.
|
||||
if (!store.matchBucketsSearch) {
|
||||
store.matchBucketsSearch = store.matchBuckets;
|
||||
} else {
|
||||
// Convert from pref char format to an array and add the default buckets.
|
||||
try {
|
||||
store.matchBucketsSearch = convertBucketsCharPrefToArray(store.matchBucketsSearch);
|
||||
store.matchBucketsSearch = [ ...DEFAULT_BUCKETS_BEFORE,
|
||||
...store.matchBucketsSearch,
|
||||
...DEFAULT_BUCKETS_AFTER ];
|
||||
} catch (ex) {
|
||||
store.matchBucketsSearch = store.matchBuckets;
|
||||
}
|
||||
}
|
||||
store.keywordEnabled = Services.prefs.getBoolPref("keyword.enabled", true);
|
||||
|
||||
// If history is not set, onlyTyped value should be ignored.
|
||||
|
@ -815,14 +833,6 @@ function Search(searchString, searchParam, autocompleteListener,
|
|||
// Resolved when all the matches providers have been fetched.
|
||||
this._allMatchesPromises = [];
|
||||
|
||||
// Convert the buckets to readable objects with a count property.
|
||||
this._buckets = Prefs.matchBuckets
|
||||
.map(([type, available]) => ({
|
||||
type,
|
||||
available,
|
||||
count: 0
|
||||
}));
|
||||
|
||||
// Counters for the number of matches per MATCHTYPE.
|
||||
this._counts = Object.values(MATCHTYPE)
|
||||
.reduce((o, p) => { o[p] = 0; return o; }, {});
|
||||
|
@ -1824,6 +1834,17 @@ Search.prototype = {
|
|||
|
||||
_getInsertIndexForMatch(match) {
|
||||
let index = 0;
|
||||
// The buckets change depending on the context, that is currently decided by
|
||||
// the first added match (the heuristic one).
|
||||
if (!this._buckets) {
|
||||
// Convert the buckets to readable objects with a count property.
|
||||
let buckets = match.style.includes("searchengine") ? Prefs.matchBucketsSearch
|
||||
: Prefs.matchBuckets;
|
||||
this._buckets = buckets.map(([type, available]) => ({ type,
|
||||
available,
|
||||
count: 0,
|
||||
}));
|
||||
}
|
||||
for (let bucket of this._buckets) {
|
||||
// Move to the next bucket if the match type is incompatible, or if there
|
||||
// is no available space or if the frecency is below the threshold.
|
||||
|
|
|
@ -2905,10 +2905,23 @@ NS_IMETHODIMP
|
|||
nsNavHistory::GetShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_shutdownClient);
|
||||
RefPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
|
||||
MOZ_ASSERT(client);
|
||||
nsCOMPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
|
||||
if (!client) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
client.forget(_shutdownClient);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistory::GetConnectionShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_shutdownClient);
|
||||
nsCOMPtr<nsIAsyncShutdownClient> client = mDB->GetConnectionShutdown();
|
||||
if (!client) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
client.forget(_shutdownClient);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,14 @@ interface nsPIPlacesDatabase : nsISupports
|
|||
/**
|
||||
* Hook for clients who need to perform actions during/by the end of
|
||||
* the shutdown of the database.
|
||||
* May be null if it's too late to get one.
|
||||
*/
|
||||
readonly attribute nsIAsyncShutdownClient shutdownClient;
|
||||
|
||||
/**
|
||||
* Hook for internal clients who need to perform actions just before the
|
||||
* connection gets closed.
|
||||
* May be null if it's too late to get one.
|
||||
*/
|
||||
readonly attribute nsIAsyncShutdownClient connectionShutdownClient;
|
||||
};
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
* to preserve batteries in portable devices.
|
||||
* - At shutdown, only if the database is dirty, we should still avoid to
|
||||
* expire too heavily on shutdown.
|
||||
* - On ClearHistory we run a full expiration for privacy reasons.
|
||||
* - On a repeating timer we expire in small chunks.
|
||||
*
|
||||
* Expiration algorithm will adapt itself based on:
|
||||
|
@ -33,7 +32,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
|||
// Constants
|
||||
|
||||
// Last expiration step should run before the final sync.
|
||||
const TOPIC_SHUTDOWN = "places-will-close-connection";
|
||||
const TOPIC_PREF_CHANGED = "nsPref:changed";
|
||||
const TOPIC_DEBUG_START_EXPIRATION = "places-debug-start-expiration";
|
||||
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
|
||||
|
@ -109,11 +107,6 @@ const URIENTRY_AVG_SIZE = 600;
|
|||
// stand-by or mobile devices batteries.
|
||||
const IDLE_TIMEOUT_SECONDS = 5 * 60;
|
||||
|
||||
// If a clear history ran just before we shutdown, we will skip most of the
|
||||
// expiration at shutdown. This is maximum number of seconds from last
|
||||
// clearHistory to decide to skip expiration at shutdown.
|
||||
const SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS = 10;
|
||||
|
||||
// If the number of pages over history limit is greater than this threshold,
|
||||
// expiration will be more aggressive, to bring back history to a saner size.
|
||||
const OVERLIMIT_PAGES_THRESHOLD = 1000;
|
||||
|
@ -134,7 +127,7 @@ const ANNOS_EXPIRE_POLICIES = [
|
|||
// When we expire we can use these limits:
|
||||
// - SMALL for usual partial expirations, will expire a small chunk.
|
||||
// - LARGE for idle or shutdown expirations, will expire a large chunk.
|
||||
// - UNLIMITED for clearHistory, will expire everything.
|
||||
// - UNLIMITED will expire all the orphans.
|
||||
// - DEBUG will use a known limit, passed along with the debug notification.
|
||||
const LIMIT = {
|
||||
SMALL: 0,
|
||||
|
@ -154,11 +147,10 @@ const STATUS = {
|
|||
const ACTION = {
|
||||
TIMED: 1 << 0, // happens every this._interval
|
||||
TIMED_OVERLIMIT: 1 << 1, // like TIMED but only when history is over limits
|
||||
CLEAR_HISTORY: 1 << 2, // happens when history is cleared
|
||||
SHUTDOWN_DIRTY: 1 << 3, // happens at shutdown for DIRTY state
|
||||
IDLE_DIRTY: 1 << 4, // happens on idle for DIRTY state
|
||||
IDLE_DAILY: 1 << 5, // happens once a day on idle
|
||||
DEBUG: 1 << 6, // happens on TOPIC_DEBUG_START_EXPIRATION
|
||||
SHUTDOWN_DIRTY: 1 << 2, // happens at shutdown for DIRTY state
|
||||
IDLE_DIRTY: 1 << 3, // happens on idle for DIRTY state
|
||||
IDLE_DAILY: 1 << 4, // happens once a day on idle
|
||||
DEBUG: 1 << 5, // happens on TOPIC_DEBUG_START_EXPIRATION
|
||||
};
|
||||
|
||||
// The queries we use to expire.
|
||||
|
@ -240,27 +232,12 @@ const EXPIRATION_QUERIES = {
|
|||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire orphan URIs from the database.
|
||||
QUERY_SILENT_EXPIRE_ORPHAN_URIS: {
|
||||
sql: `DELETE FROM moz_places WHERE id IN (
|
||||
SELECT h.id
|
||||
FROM moz_places h
|
||||
LEFT JOIN moz_historyvisits v ON h.id = v.place_id
|
||||
WHERE h.last_visit_date IS NULL
|
||||
AND h.foreign_count = 0
|
||||
AND v.id IS NULL
|
||||
LIMIT :limit_uris
|
||||
)`,
|
||||
actions: ACTION.CLEAR_HISTORY
|
||||
},
|
||||
|
||||
// Hosts accumulated during the places delete are updated through a trigger
|
||||
// (see nsPlacesTriggers.h).
|
||||
QUERY_UPDATE_HOSTS: {
|
||||
sql: `DELETE FROM moz_updatehosts_temp`,
|
||||
actions: ACTION.CLEAR_HISTORY | ACTION.TIMED | ACTION.TIMED_OVERLIMIT |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire orphan pages from the icons database.
|
||||
|
@ -269,9 +246,8 @@ const EXPIRATION_QUERIES = {
|
|||
WHERE page_url_hash NOT IN (
|
||||
SELECT url_hash FROM moz_places
|
||||
)`,
|
||||
actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire orphan icons from the database.
|
||||
|
@ -280,9 +256,8 @@ const EXPIRATION_QUERIES = {
|
|||
WHERE root = 0 AND id NOT IN (
|
||||
SELECT icon_id FROM moz_icons_to_pages
|
||||
)`,
|
||||
actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire orphan page annotations from the database.
|
||||
|
@ -293,9 +268,8 @@ const EXPIRATION_QUERIES = {
|
|||
WHERE h.id IS NULL
|
||||
LIMIT :limit_annos
|
||||
)`,
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire page annotations based on expiration policy.
|
||||
|
@ -307,9 +281,8 @@ const EXPIRATION_QUERIES = {
|
|||
AND :expire_weeks_time > MAX(lastModified, dateAdded))
|
||||
OR (expiration = :expire_months
|
||||
AND :expire_months_time > MAX(lastModified, dateAdded))`,
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire items annotations based on expiration policy.
|
||||
|
@ -321,9 +294,8 @@ const EXPIRATION_QUERIES = {
|
|||
AND :expire_weeks_time > MAX(lastModified, dateAdded))
|
||||
OR (expiration = :expire_months
|
||||
AND :expire_months_time > MAX(lastModified, dateAdded))`,
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire page annotations based on expiration policy.
|
||||
|
@ -332,9 +304,8 @@ const EXPIRATION_QUERIES = {
|
|||
WHERE expiration = :expire_with_history
|
||||
AND NOT EXISTS (SELECT id FROM moz_historyvisits
|
||||
WHERE place_id = moz_annos.place_id LIMIT 1)`,
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire item annos without a corresponding item id.
|
||||
|
@ -345,7 +316,7 @@ const EXPIRATION_QUERIES = {
|
|||
WHERE b.id IS NULL
|
||||
LIMIT :limit_annos
|
||||
)`,
|
||||
actions: ACTION.CLEAR_HISTORY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
actions: ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire all annotation names without a corresponding annotation.
|
||||
|
@ -358,7 +329,7 @@ const EXPIRATION_QUERIES = {
|
|||
AND t.anno_attribute_id IS NULL
|
||||
LIMIT :limit_annos
|
||||
)`,
|
||||
actions: ACTION.CLEAR_HISTORY | ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY |
|
||||
actions: ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY |
|
||||
ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
|
@ -370,21 +341,8 @@ const EXPIRATION_QUERIES = {
|
|||
WHERE h.id IS NULL
|
||||
LIMIT :limit_inputhistory
|
||||
)`,
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
|
||||
ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
|
||||
ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire all session annotations. Should only be called at shutdown.
|
||||
QUERY_EXPIRE_ANNOS_SESSION: {
|
||||
sql: "DELETE FROM moz_annos WHERE expiration = :expire_session",
|
||||
actions: ACTION.CLEAR_HISTORY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Expire all session item annotations. Should only be called at shutdown.
|
||||
QUERY_EXPIRE_ITEMS_ANNOS_SESSION: {
|
||||
sql: "DELETE FROM moz_items_annos WHERE expiration = :expire_session",
|
||||
actions: ACTION.CLEAR_HISTORY | ACTION.DEBUG
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
|
||||
},
|
||||
|
||||
// Select entries for notifications.
|
||||
|
@ -474,40 +432,37 @@ function nsPlacesExpiration() {
|
|||
}, Cu.reportError);
|
||||
|
||||
// Register topic observers.
|
||||
Services.obs.addObserver(this, TOPIC_SHUTDOWN, true);
|
||||
Services.obs.addObserver(this, TOPIC_DEBUG_START_EXPIRATION, true);
|
||||
Services.obs.addObserver(this, TOPIC_IDLE_DAILY, true);
|
||||
|
||||
// Block shutdown.
|
||||
let shutdownClient = PlacesUtils.history.connectionShutdownClient.jsclient;
|
||||
shutdownClient.addBlocker("Places Expiration: shutdown", () => {
|
||||
if (this._shuttingDown) {
|
||||
return;
|
||||
}
|
||||
this._shuttingDown = true;
|
||||
this.expireOnIdle = false;
|
||||
if (this._timer) {
|
||||
this._timer.cancel();
|
||||
this._timer = null;
|
||||
}
|
||||
// If the database is dirty, we want to expire some entries, to speed up
|
||||
// the expiration process.
|
||||
if (this.status == STATUS.DIRTY) {
|
||||
this._expireWithActionAndLimit(ACTION.SHUTDOWN_DIRTY, LIMIT.LARGE);
|
||||
}
|
||||
this._finalizeInternalStatements();
|
||||
});
|
||||
}
|
||||
|
||||
nsPlacesExpiration.prototype = {
|
||||
|
||||
// nsIObserver
|
||||
|
||||
observe: function PEX_observe(aSubject, aTopic, aData) {
|
||||
if (this._shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTopic == TOPIC_SHUTDOWN) {
|
||||
this._shuttingDown = true;
|
||||
this.expireOnIdle = false;
|
||||
|
||||
if (this._timer) {
|
||||
this._timer.cancel();
|
||||
this._timer = null;
|
||||
}
|
||||
|
||||
// If we didn't ran a clearHistory recently and database is dirty, we
|
||||
// want to expire some entries, to speed up the expiration process.
|
||||
let hasRecentClearHistory =
|
||||
Date.now() - this._lastClearHistoryTime <
|
||||
SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS * 1000;
|
||||
if (!hasRecentClearHistory && this.status == STATUS.DIRTY) {
|
||||
this._expireWithActionAndLimit(ACTION.SHUTDOWN_DIRTY, LIMIT.LARGE);
|
||||
}
|
||||
|
||||
this._finalizeInternalStatements();
|
||||
} else if (aTopic == TOPIC_PREF_CHANGED) {
|
||||
if (aTopic == TOPIC_PREF_CHANGED) {
|
||||
this._loadPrefsPromise = this._loadPrefs().then(() => {
|
||||
if (aData == PREF_INTERVAL_SECONDS) {
|
||||
// Renew the timer with the new interval value.
|
||||
|
@ -577,12 +532,9 @@ nsPlacesExpiration.prototype = {
|
|||
this._newTimer();
|
||||
},
|
||||
|
||||
_lastClearHistoryTime: 0,
|
||||
onClearHistory: function PEX_onClearHistory() {
|
||||
this._lastClearHistoryTime = Date.now();
|
||||
// Expire orphans. History status is clean after a clear history.
|
||||
// History status is clean after a clear history.
|
||||
this.status = STATUS.CLEAN;
|
||||
this._expireWithActionAndLimit(ACTION.CLEAR_HISTORY, LIMIT.UNLIMITED);
|
||||
},
|
||||
|
||||
onVisit() {},
|
||||
|
@ -982,9 +934,6 @@ nsPlacesExpiration.prototype = {
|
|||
case "QUERY_FIND_URIS_TO_EXPIRE":
|
||||
params.limit_uris = baseLimit;
|
||||
break;
|
||||
case "QUERY_SILENT_EXPIRE_ORPHAN_URIS":
|
||||
params.limit_uris = baseLimit;
|
||||
break;
|
||||
case "QUERY_EXPIRE_ANNOS":
|
||||
// Each page may have multiple annos.
|
||||
params.limit_annos = baseLimit * EXPIRE_AGGRESSIVITY_MULTIPLIER;
|
||||
|
@ -1009,10 +958,6 @@ nsPlacesExpiration.prototype = {
|
|||
case "QUERY_EXPIRE_INPUTHISTORY":
|
||||
params.limit_inputhistory = baseLimit;
|
||||
break;
|
||||
case "QUERY_EXPIRE_ANNOS_SESSION":
|
||||
case "QUERY_EXPIRE_ITEMS_ANNOS_SESSION":
|
||||
params.expire_session = Ci.nsIAnnotationService.EXPIRE_SESSION;
|
||||
break;
|
||||
}
|
||||
|
||||
return stmt;
|
||||
|
|
|
@ -128,16 +128,11 @@ this.PlacesTestUtils = Object.freeze({
|
|||
* @return {Promise}
|
||||
* @resolves When history was cleared successfully.
|
||||
* @rejects JavaScript exception.
|
||||
*
|
||||
* @deprecated New consumers should directly use PlacesUtils.history.clear().
|
||||
*/
|
||||
clearHistory() {
|
||||
let expirationFinished = new Promise(resolve => {
|
||||
Services.obs.addObserver(function observe(subj, topic, data) {
|
||||
Services.obs.removeObserver(observe, topic);
|
||||
resolve();
|
||||
}, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
|
||||
});
|
||||
|
||||
return Promise.all([expirationFinished, PlacesUtils.history.clear()]);
|
||||
return PlacesUtils.history.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,28 +91,20 @@ add_task(async function shutdown() {
|
|||
// cannot test for them.
|
||||
// Put an history notification that triggers AsyncGetBookmarksForURI between
|
||||
// asyncClose() and the actual connection closing. Enqueuing a main-thread
|
||||
// event just after places-will-close-connection should ensure it runs before
|
||||
// event as a shutdown blocker should ensure it runs before
|
||||
// places-connection-closed.
|
||||
// Notice this code is not using helpers cause it depends on a very specific
|
||||
// order, a change in the helpers code could make this test useless.
|
||||
await new Promise(resolve => {
|
||||
Services.obs.addObserver(function onNotification() {
|
||||
Services.obs.removeObserver(onNotification, "places-will-close-connection");
|
||||
do_check_true(true, "Observed fake places shutdown");
|
||||
|
||||
Services.tm.dispatchToMainThread(() => {
|
||||
let shutdownClient = PlacesUtils.history.shutdownClient.jsclient;
|
||||
shutdownClient.addBlocker("Places Expiration: shutdown",
|
||||
function() {
|
||||
Services.tm.mainThread.dispatch(() => {
|
||||
// WARNING: this is very bad, never use out of testing code.
|
||||
PlacesUtils.bookmarks.QueryInterface(Ci.nsINavHistoryObserver)
|
||||
.onPageChanged(NetUtil.newURI("http://book.ma.rk/"),
|
||||
Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON,
|
||||
"test", "test");
|
||||
resolve(promiseTopicObserved("places-connection-closed"));
|
||||
});
|
||||
}, "places-will-close-connection");
|
||||
shutdownPlaces();
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -21,14 +21,6 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
|
||||
// Put any other stuff relative to this test folder below.
|
||||
|
||||
|
||||
// Simulates an expiration at shutdown.
|
||||
function shutdownExpiration() {
|
||||
let expire = Cc["@mozilla.org/places/expiration;1"].getService(Ci.nsIObserver);
|
||||
expire.observe(null, "places-will-close-connection", null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Causes expiration component to start, otherwise it would wait for the first
|
||||
* history notification.
|
||||
|
|
|
@ -11,69 +11,8 @@
|
|||
* annos.
|
||||
*/
|
||||
|
||||
var as = PlacesUtils.annotations;
|
||||
|
||||
/**
|
||||
* Creates an aged annotation.
|
||||
*
|
||||
* @param aIdentifier Either a page url or an item id.
|
||||
* @param aIdentifier Name of the annotation.
|
||||
* @param aValue Value for the annotation.
|
||||
* @param aExpirePolicy Expiration policy of the annotation.
|
||||
* @param aAgeInDays Age in days of the annotation.
|
||||
* @param [optional] aLastModifiedAgeInDays Age in days of the annotation, for lastModified.
|
||||
*/
|
||||
var now = Date.now();
|
||||
function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy,
|
||||
aAgeInDays, aLastModifiedAgeInDays) {
|
||||
let expireDate = (now - (aAgeInDays * 86400 * 1000)) * 1000;
|
||||
let lastModifiedDate = 0;
|
||||
if (aLastModifiedAgeInDays)
|
||||
lastModifiedDate = (now - (aLastModifiedAgeInDays * 86400 * 1000)) * 1000;
|
||||
|
||||
let sql;
|
||||
if (typeof(aIdentifier) == "number") {
|
||||
// Item annotation.
|
||||
as.setItemAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
|
||||
// Update dateAdded for the last added annotation.
|
||||
sql = "UPDATE moz_items_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
|
||||
"WHERE id = ( " +
|
||||
"SELECT a.id FROM moz_items_annos a " +
|
||||
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
|
||||
"WHERE a.item_id = :id " +
|
||||
"AND n.name = :anno_name " +
|
||||
"ORDER BY a.dateAdded DESC LIMIT 1 " +
|
||||
")";
|
||||
} else if (aIdentifier instanceof Ci.nsIURI) {
|
||||
// Page annotation.
|
||||
as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
|
||||
// Update dateAdded for the last added annotation.
|
||||
sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
|
||||
"WHERE id = ( " +
|
||||
"SELECT a.id FROM moz_annos a " +
|
||||
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
|
||||
"JOIN moz_places h on h.id = a.place_id " +
|
||||
"WHERE h.url_hash = hash(:id) AND h.url = :id " +
|
||||
"AND n.name = :anno_name " +
|
||||
"ORDER BY a.dateAdded DESC LIMIT 1 " +
|
||||
")";
|
||||
} else
|
||||
do_throw("Wrong identifier type");
|
||||
|
||||
let stmt = DBConn().createStatement(sql);
|
||||
stmt.params.id = (typeof(aIdentifier) == "number") ? aIdentifier
|
||||
: aIdentifier.spec;
|
||||
stmt.params.expire_date = expireDate;
|
||||
stmt.params.last_modified = lastModifiedDate;
|
||||
stmt.params.anno_name = aName;
|
||||
try {
|
||||
stmt.executeStep();
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function test_historyClear() {
|
||||
let as = PlacesUtils.annotations;
|
||||
// Set interval to a large value so we don't expire on it.
|
||||
setInterval(3600); // 1h
|
||||
|
||||
|
@ -99,12 +38,6 @@ add_task(async function test_historyClear() {
|
|||
as.setItemAnnotation(id, "expire_session", "test", 0, as.EXPIRE_SESSION);
|
||||
as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION);
|
||||
// Annotations with timed policy will expire regardless bookmarked status.
|
||||
add_old_anno(id, "expire_days", "test", as.EXPIRE_DAYS, 8);
|
||||
add_old_anno(id, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
|
||||
add_old_anno(id, "expire_months", "test", as.EXPIRE_MONTHS, 181);
|
||||
add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8);
|
||||
add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
|
||||
add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181);
|
||||
}
|
||||
|
||||
// Add some visited page and annotations for each.
|
||||
|
@ -115,22 +48,18 @@ add_task(async function test_historyClear() {
|
|||
await PlacesTestUtils.addVisits({ uri: pageURI });
|
||||
as.setPageAnnotation(pageURI, "expire", "test", 0, as.EXPIRE_NEVER);
|
||||
as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION);
|
||||
add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8);
|
||||
add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
|
||||
add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181);
|
||||
}
|
||||
|
||||
// Expire all visits for the bookmarks
|
||||
await PlacesTestUtils.clearHistory();
|
||||
|
||||
["expire_days", "expire_weeks", "expire_months", "expire_session",
|
||||
["expire_session",
|
||||
"expire"].forEach(function(aAnno) {
|
||||
let pages = as.getPagesWithAnnotation(aAnno);
|
||||
do_check_eq(pages.length, 0);
|
||||
});
|
||||
|
||||
["expire_days", "expire_weeks", "expire_months", "expire_session",
|
||||
"expire"].forEach(function(aAnno) {
|
||||
["expire_session", "expire"].forEach(function(aAnno) {
|
||||
let items = as.getItemsWithAnnotation(aAnno);
|
||||
do_check_eq(items.length, 0);
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ function run_test() {
|
|||
// Set interval to a large value so we don't expire on it.
|
||||
setInterval(3600); // 1h
|
||||
|
||||
PlacesTestUtils.clearHistory();
|
||||
promiseForceExpirationStep(1);
|
||||
|
||||
do_timeout(2000, check_result);
|
||||
do_test_pending();
|
||||
|
|
|
@ -17,26 +17,24 @@ const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3;
|
|||
|
||||
var tests = [
|
||||
|
||||
// This test should be the first, so the interval won't be influenced by
|
||||
// status of history.
|
||||
{ desc: "Set interval to 1s.",
|
||||
interval: 1,
|
||||
expectedTimerDelay: 1
|
||||
expectedTimerDelay: 1 * EXPIRE_AGGRESSIVITY_MULTIPLIER
|
||||
},
|
||||
|
||||
{ desc: "Set interval to a negative value.",
|
||||
interval: -1,
|
||||
expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS
|
||||
expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS * EXPIRE_AGGRESSIVITY_MULTIPLIER
|
||||
},
|
||||
|
||||
{ desc: "Set interval to 0.",
|
||||
interval: 0,
|
||||
expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS
|
||||
expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS * EXPIRE_AGGRESSIVITY_MULTIPLIER
|
||||
},
|
||||
|
||||
{ desc: "Set interval to a large value.",
|
||||
interval: 100,
|
||||
expectedTimerDelay: 100
|
||||
expectedTimerDelay: 100 * EXPIRE_AGGRESSIVITY_MULTIPLIER
|
||||
},
|
||||
|
||||
];
|
||||
|
@ -49,13 +47,12 @@ add_task(async function test() {
|
|||
force_expiration_start();
|
||||
|
||||
for (let currentTest of tests) {
|
||||
currentTest = tests.shift();
|
||||
print(currentTest.desc);
|
||||
let promise = promiseTopicObserved("test-interval-changed");
|
||||
setInterval(currentTest.interval);
|
||||
let [, data] = await promise;
|
||||
Assert.equal(data, currentTest.expectedTimerDelay * EXPIRE_AGGRESSIVITY_MULTIPLIER);
|
||||
Assert.equal(data, currentTest.expectedTimerDelay);
|
||||
}
|
||||
|
||||
clearInterval();
|
||||
});
|
||||
|
||||
|
|
|
@ -443,7 +443,7 @@ add_task(async function mixup_frecency() {
|
|||
],
|
||||
});
|
||||
|
||||
// Change the results mixup.
|
||||
// Change the "general" context mixup.
|
||||
Services.prefs.setCharPref("browser.urlbar.matchBuckets",
|
||||
"suggestion:1,general:5,suggestion:1");
|
||||
|
||||
|
@ -502,7 +502,65 @@ add_task(async function mixup_frecency() {
|
|||
],
|
||||
});
|
||||
|
||||
// Change the "search" context mixup.
|
||||
Services.prefs.setCharPref("browser.urlbar.matchBucketsSearch",
|
||||
"suggestion:2,general:4");
|
||||
|
||||
await check_autocomplete({
|
||||
checkSorting: true,
|
||||
search: "frecency",
|
||||
searchParam: "enable-actions",
|
||||
matches: [
|
||||
makeSearchMatch("frecency", { engineName: ENGINE_NAME, heuristic: true }),
|
||||
{
|
||||
uri: makeActionURI(("searchengine"), {
|
||||
engineName: ENGINE_NAME,
|
||||
input: "frecency foo",
|
||||
searchQuery: "frecency",
|
||||
searchSuggestion: "frecency foo",
|
||||
}),
|
||||
title: ENGINE_NAME,
|
||||
style: ["action", "searchengine"],
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
uri: makeActionURI(("searchengine"), {
|
||||
engineName: ENGINE_NAME,
|
||||
input: "frecency bar",
|
||||
searchQuery: "frecency",
|
||||
searchSuggestion: "frecency bar",
|
||||
}),
|
||||
title: ENGINE_NAME,
|
||||
style: ["action", "searchengine"],
|
||||
icon: "",
|
||||
},
|
||||
{ uri: NetUtil.newURI("http://example.com/hi3"),
|
||||
title: "high frecency 3",
|
||||
style: [ "bookmark" ] },
|
||||
{ uri: NetUtil.newURI("http://example.com/hi2"),
|
||||
title: "high frecency 2",
|
||||
style: [ "bookmark" ] },
|
||||
{ uri: NetUtil.newURI("http://example.com/hi1"),
|
||||
title: "high frecency 1",
|
||||
style: [ "bookmark" ] },
|
||||
{ uri: NetUtil.newURI("http://example.com/hi0"),
|
||||
title: "high frecency 0",
|
||||
style: [ "bookmark" ] },
|
||||
{ uri: NetUtil.newURI("http://example.com/lo4"),
|
||||
title: "low frecency 4" },
|
||||
{ uri: NetUtil.newURI("http://example.com/lo3"),
|
||||
title: "low frecency 3" },
|
||||
{ uri: NetUtil.newURI("http://example.com/lo2"),
|
||||
title: "low frecency 2" },
|
||||
{ uri: NetUtil.newURI("http://example.com/lo1"),
|
||||
title: "low frecency 1" },
|
||||
{ uri: NetUtil.newURI("http://example.com/lo0"),
|
||||
title: "low frecency 0" },
|
||||
],
|
||||
});
|
||||
|
||||
Services.prefs.clearUserPref("browser.urlbar.matchBuckets");
|
||||
Services.prefs.clearUserPref("browser.urlbar.matchBucketsSearch");
|
||||
await cleanUpSuggestions();
|
||||
});
|
||||
|
||||
|
|
|
@ -6,31 +6,6 @@
|
|||
|
||||
var mDBConn = DBConn();
|
||||
|
||||
function promiseOnClearHistoryObserved() {
|
||||
return new Promise(resolve => {
|
||||
|
||||
let historyObserver = {
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onVisit() {},
|
||||
onTitleChanged() {},
|
||||
onDeleteURI(aURI) {},
|
||||
onPageChanged() {},
|
||||
onDeleteVisits() {},
|
||||
|
||||
onClearHistory() {
|
||||
PlacesUtils.history.removeObserver(this, false);
|
||||
resolve();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsINavHistoryObserver,
|
||||
])
|
||||
}
|
||||
PlacesUtils.history.addObserver(historyObserver);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_history_clear() {
|
||||
await PlacesTestUtils.addVisits([
|
||||
{ uri: uri("http://typed.mozilla.org/"),
|
||||
|
@ -78,14 +53,14 @@ add_task(async function test_history_clear() {
|
|||
await PlacesTestUtils.promiseAsyncUpdates();
|
||||
|
||||
// Clear history and wait for the onClearHistory notification.
|
||||
let promiseWaitClearHistory = promiseOnClearHistoryObserved();
|
||||
let promiseClearHistory =
|
||||
PlacesTestUtils.waitForNotification("onClearHistory", () => true, "history");
|
||||
PlacesUtils.history.clear();
|
||||
await promiseWaitClearHistory;
|
||||
await promiseClearHistory;
|
||||
|
||||
// check browserHistory returns no entries
|
||||
do_check_eq(0, PlacesUtils.history.hasHistoryEntries);
|
||||
|
||||
await promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
|
||||
await PlacesTestUtils.promiseAsyncUpdates();
|
||||
|
||||
// Check that frecency for not cleared items (bookmarks) has been converted
|
||||
|
|
|
@ -148,7 +148,19 @@ nsRFPService::UpdatePref()
|
|||
// the time of initialization.
|
||||
if (!mInitialTZValue.IsEmpty()) {
|
||||
nsAutoCString tzValue = NS_LITERAL_CSTRING("TZ=") + mInitialTZValue;
|
||||
PR_SetEnv(tzValue.get());
|
||||
static char* tz = nullptr;
|
||||
|
||||
// If the tz has been set before, we free it first since it will be allocated
|
||||
// a new value later.
|
||||
if (tz) {
|
||||
free(tz);
|
||||
}
|
||||
// PR_SetEnv() needs the input string been leaked intentionally, so
|
||||
// we copy it here.
|
||||
tz = ToNewCString(tzValue);
|
||||
if (tz) {
|
||||
PR_SetEnv(tz);
|
||||
}
|
||||
} else {
|
||||
#if defined(XP_LINUX) || defined (XP_MACOSX)
|
||||
// For POSIX like system, we reset the TZ to the /etc/localtime, which is the
|
||||
|
|
Загрузка…
Ссылка в новой задаче