Merge autoland to central, a=merge

MozReview-Commit-ID: AWf410QGui6
This commit is contained in:
Wes Kocher 2017-07-24 17:57:54 -07:00
Родитель 1b05f8ad37 1ac9609130
Коммит 252080fa1f
74 изменённых файлов: 829 добавлений и 642 удалений

Просмотреть файл

@ -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