Merge autoland to m-c, a=merge

MozReview-Commit-ID: 3KyMJNBoJpp
This commit is contained in:
Phil Ringnalda 2017-07-04 20:30:34 -07:00
commit edd2fcf74b
93 changed files with 1211 additions and 729 deletions

View File

@ -37,7 +37,8 @@ function tag (message) {
event: message.event
};
resData.data = taggingService.tagURI(newURI(data.url), data.tags);
if (data.tags && data.tags.length > 0)
resData.data = taggingService.tagURI(newURI(data.url), data.tags);
respond(resData);
}
@ -48,7 +49,8 @@ function untag (message) {
event: message.event
};
resData.data = taggingService.untagURI(newURI(data.url), data.tags);
if (!data.tags || data.tags.length > 0)
resData.data = taggingService.untagURI(newURI(data.url), data.tags);
respond(resData);
}

View File

@ -76,7 +76,32 @@ const EXPECTED_APPMENU_OPEN_REFLOWS = [
const EXPECTED_APPMENU_SUBVIEW_REFLOWS = [
/**
* Nothing here! Please don't add anything new!
* The synced tabs view has labels that are multiline. Because of bugs in
* XUL layout relating to multiline text in scrollable containers, we need
* to manually read their height in order to ensure container heights are
* correct. Unfortunately this requires 2 sync reflows.
*
* If we add more views where this is necessary, we may need to duplicate
* these expected reflows further.
*
* Because the test dirties the frame tree by manipulating margins,
* getBoundingClientRect() in the descriptionHeightWorkaround code
* seems to sometimes fire multiple times. Bug 1363361 will change how the
* test dirties the frametree, after which this (2 hits in that method)
* should become deterministic and we can re-enable the subview testing
* for the remotetabs subview (this is bug 1376822). In the meantime,
* that subview only is excluded from this test.
[
"descriptionHeightWorkaround@resource:///modules/PanelMultiView.jsm",
"onTransitionEnd@resource:///modules/PanelMultiView.jsm",
],
[
"descriptionHeightWorkaround@resource:///modules/PanelMultiView.jsm",
"onTransitionEnd@resource:///modules/PanelMultiView.jsm",
],
*/
/**
* Please don't add anything new!
*/
];
@ -110,8 +135,17 @@ add_task(async function() {
}
for (let button of navButtons) {
// We skip the remote tabs subview, see the comments above
// in EXPECTED_APPMENU_SUBVIEW_REFLOWS. bug 1376822 tracks
// re-enabling this.
if (button.id == "appMenu-library-remotetabs-button") {
info("Skipping " + button.id);
continue;
}
info("Click " + button.id);
button.click();
await BrowserTestUtils.waitForEvent(PanelUI.panel, "ViewShown");
info("Shown " + PanelUI.multiView.instance._currentSubView.id);
// Unfortunately, I can't find a better accessor to the current
// subview, so I have to reach the PanelMultiView instance
// here.

View File

@ -16,10 +16,9 @@
</broadcasterset>
<panelmultiview id="identity-popup-multiView"
mainViewId="identity-popup-mainView"
descriptionheightworkaround="true">
<panelview id="identity-popup-mainView" flex="1">
mainViewId="identity-popup-mainView">
<panelview id="identity-popup-mainView" flex="1"
descriptionheightworkaround="true">
<!-- Security Section -->
<hbox id="identity-popup-security" class="identity-popup-section">
<vbox id="identity-popup-security-content" flex="1">
@ -97,7 +96,8 @@
</panelview>
<!-- Security SubView -->
<panelview id="identity-popup-securityView">
<panelview id="identity-popup-securityView"
descriptionheightworkaround="true">
<vbox id="identity-popup-securityView-header">
<label class="plain">
<label class="identity-popup-headline identity-popup-host"></label>

View File

@ -443,6 +443,8 @@ const CustomizableWidgets = [
}
}
this._tabsList.appendChild(fragment);
let panelView = this._tabsList.closest("panelview");
panelView.panelMultiView.descriptionHeightWorkaround(panelView);
}).catch(err => {
Cu.reportError(err);
}).then(() => {

View File

@ -1035,10 +1035,10 @@ this.PanelMultiView = class {
/**
* If the main view or a subview contains wrapping elements, the attribute
* "descriptionheightworkaround" should be set on the view to force all the
* "description" or wrapping toolbarbutton elements to a fixed height.
* If the attribute is set and the visibility, contents, or width of any of
* these elements changes, this function should be called to refresh the
* calculated heights.
* wrapping "description", "label" or "toolbarbutton" elements to a fixed
* height. If the attribute is set and the visibility, contents, or width
* of any of these elements changes, this function should be called to
* refresh the calculated heights.
*
* This may trigger a synchronous layout.
*
@ -1047,7 +1047,7 @@ this.PanelMultiView = class {
* view if omitted.
*/
descriptionHeightWorkaround(viewNode = this._mainView) {
if (!this.node.hasAttribute("descriptionheightworkaround")) {
if (!viewNode.hasAttribute("descriptionheightworkaround")) {
// This view does not require the workaround.
return;
}
@ -1056,8 +1056,20 @@ this.PanelMultiView = class {
// First we reset any change we may have made previously. The first time
// this is called, and in the best case scenario, this has no effect.
let items = [];
for (let element of viewNode.querySelectorAll(
"description:not([hidden]):not([value]),toolbarbutton[wrap]:not([hidden])")) {
// Non-hidden <label> or <description> elements that also aren't empty
// and also don't have a value attribute can be multiline (if their
// text content is long enough).
let isMultiline = ":not(:-moz-any([hidden],[value],:empty))";
let selector = [
"description" + isMultiline,
"label" + isMultiline,
"toolbarbutton[wrap]:not([hidden])",
].join(",");
for (let element of viewNode.querySelectorAll(selector)) {
// Ignore items in hidden containers.
if (element.closest("[hidden]")) {
continue;
}
// Take the label for toolbarbuttons; it only exists on those elements.
element = element.labelElement || element;

View File

@ -11,7 +11,8 @@
noautofocus="true">
<panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView"
viewCacheId="appMenu-viewCache">
<panelview id="PanelUI-mainView" context="customizationPanelContextMenu">
<panelview id="PanelUI-mainView" context="customizationPanelContextMenu"
descriptionheightworkaround="true">
<vbox id="PanelUI-contents-scroller">
<vbox id="PanelUI-contents" class="panelUI-grid"/>
</vbox>
@ -109,7 +110,8 @@
oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
</panelview>
<panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView">
<panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView"
descriptionheightworkaround="true">
<label value="&appMenuRemoteTabs.label;" class="panel-subview-header"/>
<vbox class="panel-subview-body">
<!-- this widget has 3 boxes in the body, but only 1 is ever visible -->
@ -321,7 +323,8 @@
</vbox>
</panelview>
<panelview id="PanelUI-panicView" flex="1">
<panelview id="PanelUI-panicView" flex="1"
descriptionheightworkaround="true">
<vbox class="panel-subview-body">
<hbox id="PanelUI-panic-timeframe">
<image id="PanelUI-panic-timeframe-icon" alt=""/>
@ -536,9 +539,9 @@
position="bottomcenter topright"
noautofocus="true">
<photonpanelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView"
descriptionheightworkaround="true"
viewCacheId="appMenu-viewCache">
<panelview id="appMenu-mainView" class="PanelUI-subView">
<panelview id="appMenu-mainView" class="PanelUI-subView"
descriptionheightworkaround="true">
<vbox class="panel-subview-body">
<vbox id="appMenu-addon-banners"/>
<toolbarbutton class="panel-banner-item"

View File

@ -107,8 +107,7 @@
</menupopup>
<panelmultiview id="downloadsPanel-multiView"
mainViewId="downloadsPanel-mainView"
descriptionheightworkaround="true">
mainViewId="downloadsPanel-mainView">
<panelview id="downloadsPanel-mainView">
<vbox class="panel-view-body-unscrollable">
@ -157,7 +156,8 @@
</vbox>
</panelview>
<panelview id="downloadsPanel-blockedSubview">
<panelview id="downloadsPanel-blockedSubview"
descriptionheightworkaround="true">
<vbox class="panel-view-body-unscrollable">
<description id="downloadsPanel-blockedSubview-title"/>
<description id="downloadsPanel-blockedSubview-details1"/>

View File

@ -511,7 +511,10 @@ var gEditItemOverlay = {
if (aCurrentTags.length == 0)
return { newTags: inputTags, removedTags: [] };
let removedTags = aCurrentTags.filter(t => !inputTags.includes(t));
// Do not remove tags that may be reinserted with a different
// case, since the tagging service may handle those more efficiently.
let lcInputTags = inputTags.map(t => t.toLowerCase());
let removedTags = aCurrentTags.filter(t => !lcInputTags.includes(t.toLowerCase()));
let newTags = inputTags.filter(t => !aCurrentTags.includes(t));
return { removedTags, newTags };
},
@ -537,14 +540,14 @@ var gEditItemOverlay = {
}
let setTags = async function() {
if (removedTags.length > 0) {
await PlacesTransactions.Untag({ urls: aURIs, tags: removedTags })
.transact();
}
if (newTags.length > 0) {
await PlacesTransactions.Tag({ urls: aURIs, tags: newTags })
.transact();
}
if (removedTags.length > 0) {
await PlacesTransactions.Untag({ urls: aURIs, tags: removedTags })
.transact();
}
};
// Only in the library info-pane it's safe (and necessary) to batch these.

View File

@ -31,7 +31,7 @@ add_task(async function() {
Assert.ok(tree.controller.isCommandEnabled("placesCmd_show:info"),
"'placesCmd_show:info' on current selected node is enabled");
let promiseTitleResetNotification = promiseBookmarksNotification(
let promiseTitleResetNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (itemId, prop, isAnno, val) => prop == "title" && val == "tag1");
await withBookmarksDialog(
@ -48,7 +48,7 @@ add_task(async function() {
Assert.ok(!namepicker.readOnly, "Name field should not be read-only");
Assert.equal(namepicker.value, "tag1", "Node title is correct");
let promiseTitleChangeNotification = promiseBookmarksNotification(
let promiseTitleChangeNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (itemId, prop, isAnno, val) => prop == "title" && val == "tag2");
fillBookmarkTextField("editBMPanel_namePicker", "tag2", dialogWin);

View File

@ -398,18 +398,16 @@ function open_properties_dialog() {
return;
ww.unregisterNotification(windowObserver);
let observerWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
waitForFocus(() => {
// Windows has been loaded, execute our test now.
executeSoon(function() {
// Ensure overlay is loaded
ok(observerWindow.gEditItemOverlay.initialized, "EditItemOverlay is initialized");
gCurrentTest.window = observerWindow;
try {
gCurrentTest.run();
} catch (ex) {
ok(false, "An error occured during test run: " + ex.message);
}
});
waitForFocus(async () => {
// Ensure overlay is loaded
await BrowserTestUtils.waitForCondition(
() => observerWindow.gEditItemOverlay.initialized, "EditItemOverlay is initialized");
gCurrentTest.window = observerWindow;
try {
gCurrentTest.run();
} catch (ex) {
ok(false, "An error occured during test run: " + ex.message);
}
}, observerWindow);
}
ww.registerNotification(windowObserver);

View File

@ -28,6 +28,7 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://browser/content/places/editBookmarkOverlay.js"/>
<script type="application/javascript" src="head.js" />
<body xmlns="http://www.w3.org/1999/xhtml" />
@ -35,10 +36,8 @@
<script type="application/javascript">
<![CDATA[
function runTest() {
SimpleTest.waitForExplicitFinish();
(async function() {
let testTag = "foo";
let testTagUpper = "Foo";
@ -60,14 +59,23 @@
// add a tag
document.getElementById("editBMPanel_tagsField").value = testTag;
let promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
gEditItemOverlay.onTagsFieldChange();
await promiseNotification;
// test that the tag has been added in the backend
is(PlacesUtils.tagging.getTagsForURI(testURI)[0], testTag, "tags match");
// change the tag
document.getElementById("editBMPanel_tagsField").value = testTagUpper;
// The old sync API doesn't notify a tags change, and fixing it would be
// quite complex, so we just wait for a title change until tags are
// refactored.
promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "title");
gEditItemOverlay.onTagsFieldChange();
await promiseNotification;
// test that the tag has been added in the backend
is(PlacesUtils.tagging.getTagsForURI(testURI)[0], testTagUpper, "tags match");

View File

@ -27,6 +27,7 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://browser/content/places/editBookmarkOverlay.js"/>
<script type="application/javascript" src="head.js" />
<body xmlns="http://www.w3.org/1999/xhtml" />
@ -34,7 +35,6 @@
<script type="application/javascript">
<![CDATA[
/**
* This test checks that editing tags doesn't scroll the tags selector
* listbox to wrong positions.
@ -42,19 +42,15 @@
function runTest() {
SimpleTest.waitForExplicitFinish();
(async function() {
let bs = PlacesUtils.bookmarks;
await PlacesUtils.bookmarks.eraseEverything();
let tags = ["a", "b", "c", "d", "e", "f", "g",
"h", "i", "l", "m", "n", "o", "p"];
// Add a bookmark and tag it.
let uri1 = Services.io.newURI("http://www1.mozilla.org/");
let bm1 = await bs.insert({
parentGuid: bs.toolbarGuid,
index: bs.DEFAULT_INDEX,
type: bs.TYPE_BOOKMARK,
let bm1 = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
title: "mozilla",
url: uri1.spec
});
@ -62,10 +58,8 @@
// Add a second bookmark so that tags won't disappear when unchecked.
let uri2 = Services.io.newURI("http://www2.mozilla.org/");
let bm2 = await bs.insert({
parentGuid: bs.toolbarGuid,
index: bs.DEFAULT_INDEX,
type: bs.TYPE_BOOKMARK,
let bm2 = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
title: "mozilla",
url: uri2.spec
});
@ -93,7 +87,10 @@
let selectedTag = listItem.label;
// Uncheck the tag.
let promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
listItem.checked = false;
await promiseNotification;
is(visibleIndex, tagsSelector.getIndexOfFirstVisibleRow(),
"Scroll position did not change");
@ -104,13 +101,16 @@
is(newItem.label, selectedTag, "Correct tag is still selected");
// Check the tag.
promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
newItem.checked = true;
await promiseNotification;
is(visibleIndex, tagsSelector.getIndexOfFirstVisibleRow(),
"Scroll position did not change");
}
// Remove the second bookmark, then nuke some of the tags.
await bs.remove(bm2.guid);
await PlacesUtils.bookmarks.remove(bm2);
// Doing this backwords tests more interesting paths.
for (let i = tags.length - 1; i >= 0 ; i -= 2) {
@ -125,7 +125,10 @@
let selectedTag = listItem.label;
// Uncheck the tag.
let promiseNotification = PlacesTestUtils.waitForNotification(
"onItemChanged", (id, property) => property == "tags");
listItem.checked = false;
await promiseNotification;
// Ensure the first visible tag is still visible in the list.
let firstVisibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
@ -145,8 +148,8 @@
}
// Cleanup.
await bs.remove(bm1.guid);
})().then(SimpleTest.finish).catch(alert);
await PlacesUtils.bookmarks.remove(bm1);
})().catch(ex => ok(false, "test failed: " + ex)).then(SimpleTest.finish);
}
function openTagSelector() {
@ -158,10 +161,8 @@
resolve();
});
});
// Open the tags selector.
document.getElementById("editBMPanel_tagsSelectorExpander").doCommand();
return promise;
}
]]>

View File

@ -413,7 +413,8 @@ var gSearchResultsPane = {
searchTooltip.setAttribute("class", "search-tooltip");
searchTooltip.textContent = query;
anchorNode.setAttribute("data-has-tooltip", "true");
// Set tooltipNode property to track corresponded tooltip node.
anchorNode.tooltipNode = searchTooltip;
anchorNode.parentElement.classList.add("search-tooltip-parent");
anchorNode.parentElement.appendChild(searchTooltip);
@ -421,8 +422,7 @@ var gSearchResultsPane = {
},
calculateTooltipPosition(anchorNode) {
let searchTooltip = anchorNode.parentElement.querySelector(":scope > .search-tooltip");
let searchTooltip = anchorNode.tooltipNode;
// In order to get the up-to-date position of each of the nodes that we're
// putting tooltips on, we have to flush layout intentionally, and that
// this is the result of a XUL limitation (bug 1363730).
@ -430,11 +430,14 @@ var gSearchResultsPane = {
let tooltipRect = searchTooltip.getBoundingClientRect();
let parentRect = anchorNode.parentElement.getBoundingClientRect();
let offSet = (anchorRect.width / 2) - (tooltipRect.width / 2);
let offSetLeft = (anchorRect.width / 2) - (tooltipRect.width / 2);
let relativeOffset = anchorRect.left - parentRect.left;
offSet += relativeOffset > 0 ? relativeOffset : 0;
offSetLeft += relativeOffset > 0 ? relativeOffset : 0;
// 20.5 is reserved for tooltip position
let offSetTop = anchorRect.top - parentRect.top - 20.5;
searchTooltip.style.setProperty("left", `${offSet}px`);
searchTooltip.style.setProperty("left", `${offSetLeft}px`);
searchTooltip.style.setProperty("top", `${offSetTop}px`);
},
/**
@ -446,7 +449,7 @@ var gSearchResultsPane = {
searchTooltip.parentElement.classList.remove("search-tooltip-parent");
searchTooltip.remove();
}
this.listSearchTooltips.forEach((anchorNode) => anchorNode.removeAttribute("data-has-tooltip"));
this.listSearchTooltips.forEach((anchorNode) => anchorNode.tooltipNode.remove());
this.listSearchTooltips.clear();
},

View File

@ -338,16 +338,12 @@ SubDialog.prototype = {
gSearchResultsPane.searchWithinNode(this._titleElement, gSearchResultsPane.query);
// Search within sub-dialog document and highlight matched keyword.
let subDialogsChildren = this._frame.contentDocument
.querySelectorAll(":scope > *:not([data-hidden-from-search])");
for (let i = 0; i < subDialogsChildren.length; i++) {
gSearchResultsPane.searchWithinNode(subDialogsChildren[i], gSearchResultsPane.query);
}
gSearchResultsPane.searchWithinNode(this._frame.contentDocument.firstElementChild,
gSearchResultsPane.query);
// Creating tooltips for all the instances found
for (let node of gSearchResultsPane.listSearchTooltips) {
if (!node.getAttribute("data-has-tooltip")) {
if (!node.tooltipNode) {
gSearchResultsPane.createSearchTooltip(node, gSearchResultsPane.query);
}
}

View File

@ -498,14 +498,32 @@ var FormAutofillContent = {
},
_previewProfile(doc) {
let selectedIndex = ProfileAutocomplete._getSelectedIndex(doc.ownerGlobal);
let docWin = doc.ownerGlobal;
let selectedIndex = ProfileAutocomplete._getSelectedIndex(docWin);
let lastAutoCompleteResult = ProfileAutocomplete.getProfileAutoCompleteResult();
let focusedInput = formFillController.focusedInput;
let mm = this._messageManagerFromWindow(docWin);
if (selectedIndex === -1 ||
!focusedInput ||
!lastAutoCompleteResult ||
lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile") {
mm.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {});
ProfileAutocomplete._clearProfilePreview();
} else {
let focusedInputDetails = this.getInputDetails(focusedInput);
let profile = JSON.parse(lastAutoCompleteResult.getCommentAt(selectedIndex));
let allFieldNames = FormAutofillContent.getAllFieldNames(focusedInput);
let profileFields = allFieldNames.filter(fieldName => !!profile[fieldName]);
let focusedCategory = FormAutofillUtils.getCategoryFromFieldName(focusedInputDetails.fieldName);
let categories = FormAutofillUtils.getCategoriesFromFieldNames(profileFields);
mm.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {
focusedCategory,
categories,
});
ProfileAutocomplete._previewSelectedProfile(selectedIndex);
}
},

View File

@ -45,15 +45,19 @@ this.FormAutofillUtils = {
return this._fieldNameInfo[fieldName] == "creditCard";
},
getCategoryFromFieldName(fieldName) {
return this._fieldNameInfo[fieldName];
},
getCategoriesFromFieldNames(fieldNames) {
let categories = new Set();
for (let fieldName of fieldNames) {
let info = this._fieldNameInfo[fieldName];
let info = this.getCategoryFromFieldName(fieldName);
if (info) {
categories.add(info);
}
}
return categories;
return Array.from(categories);
},
getAddressSeparator() {

View File

@ -42,6 +42,7 @@ this.ProfileAutoCompleteResult = function(searchString,
this._popupLabels.push({
primary: "",
secondary: "",
categories: FormAutofillUtils.getCategoriesFromFieldNames(allFieldNames),
});
};

View File

@ -58,7 +58,7 @@
<body></body>
</method>
<method name="_adjustProfileItemLayout">
<method name="_adjustAutofillItemLayout">
<body>
<![CDATA[
let outerBoxRect = this.parentNode.getBoundingClientRect();
@ -124,11 +124,10 @@
]]></setter>
</property>
<method name="_adjustAcItem">
<body>
<![CDATA[
this._adjustProfileItemLayout();
this._adjustAutofillItemLayout();
this.setAttribute("formautofillattached", "true");
let {primary, secondary} = JSON.parse(this.getAttribute("ac-value"));
@ -144,6 +143,10 @@
<binding id="autocomplete-profile-listitem-footer" extends="chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem-base">
<xbl:content xmlns="http://www.w3.org/1999/xhtml">
<div anonid="autofill-footer" class="autofill-item-box autofill-footer">
<div anonid="autofill-warning" class="autofill-footer-row autofill-warning">
</div>
<div anonid="autofill-option-button" class="autofill-footer-row autofill-option-button">
</div>
</div>
</xbl:content>
@ -159,28 +162,100 @@
this._itemBox = document.getAnonymousElementByAttribute(
this, "anonid", "autofill-footer"
);
this._optionButton = document.getAnonymousElementByAttribute(
this, "anonid", "autofill-option-button"
);
this._warningTextBox = document.getAnonymousElementByAttribute(
this, "anonid", "autofill-warning"
);
this.allFieldCategories = JSON.parse(this.getAttribute("ac-value")).categories;
/**
* Update the text on the footer.
*
* @private
* @param {string|string[]} categories
* A list of categories that used to generate the message.
* @param {boolean} hasExtraCategories
* Used to determine if it has the extra categories other than the focued category. If
* the value is true, we show "Also fill ...", otherwise, show "Fill ..." only.
*/
this._updateText = (categories = this.allFieldCategories, hasExtraCategories = true) => {
let warningTextTmplKey = hasExtraCategories ? "phishingWarningMessage" : "phishingWarningMessage2";
let sep = this._stringBundle.GetStringFromName("fieldNameSeparator");
let categoriesText = categories.map(this._stringBundle.GetStringFromName).join(sep);
this._warningTextBox.textContent = this._stringBundle.formatStringFromName(warningTextTmplKey,
[categoriesText], 1);
this.parentNode.parentNode.adjustHeight();
};
/**
* A handler for updating warning message once selectedIndex has been changed.
*
* There're three different states of warning message:
* 1. None of addresses were selected: We show all the categories in the form.
* 2. An address was selested: Show the additional categories that will also be filled.
* 3. An address was selected, but the focused category is the same as the only all categories: Only show
* the exact category that we're going to fill in.
*
* @private
* @param {string} focusedCategory
* The category that the focused input's field belongs to.
* @param {string[]} categories
* The categories of all the fields contained in the selected address.
*/
this._updateWarningMsgHandler = ({data: {focusedCategory, categories}} = {data: {}}) => {
let hasSelectedAddress = focusedCategory && categories;
// If the length of categories is 1, that means all the fillable fields are in the same
// category. We will change the way to inform user according to this flag.
let hasExtraCategories = hasSelectedAddress && categories.length > 1;
if (!hasSelectedAddress) {
this._updateText();
return;
}
let showCategories = hasExtraCategories ?
categories.filter(category => category != focusedCategory) :
[focusedCategory];
this._updateText(showCategories, hasExtraCategories);
};
this._adjustAcItem();
this._updateText();
]]>
</constructor>
<method name="_onCollapse">
<body>
<![CDATA[
/* global messageManager */
messageManager.removeMessageListener("FormAutofill:UpdateWarningMessage", this._updateWarningMsgHandler);
]]>
</body>
</method>
<method name="_adjustAcItem">
<body>
<![CDATA[
/* global Cu */
this._adjustProfileItemLayout();
this._adjustAutofillItemLayout();
this.setAttribute("formautofillattached", "true");
let {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
let footerTextBundleKey = AppConstants.platform == "macosx" ?
let buttonTextBundleKey = AppConstants.platform == "macosx" ?
"autocompleteFooterOptionOSX" : "autocompleteFooterOption";
// If the popup shows up with small layout, we should use short string to
// have a better fit in the box.
if (this._itemBox.getAttribute("size") == "small") {
footerTextBundleKey += "Short";
buttonTextBundleKey += "Short";
}
let footerText = this._stringBundle.GetStringFromName(footerTextBundleKey);
this._itemBox.textContent = footerText;
let buttonText = this._stringBundle.GetStringFromName(buttonTextBundleKey);
this._optionButton.textContent = buttonText;
messageManager.addMessageListener("FormAutofill:UpdateWarningMessage", this._updateWarningMsgHandler);
]]>
</body>
</method>

View File

@ -18,3 +18,16 @@ autocompleteFooterOption = Form Autofill Options
autocompleteFooterOptionShort = Options
autocompleteFooterOptionOSX = Form Autofill Preferences
autocompleteFooterOptionOSXShort = Preferences
address = address
name = name
organization = company
tel = phone
email = email
# LOCALIZATION NOTE (fieldNameSeparator): This is used as a separator between categories.
fieldNameSeparator = ,\u0020
# LOCALIZATION NOTE (phishingWarningMessage, phishingWarningMessage2): The warning
# text that is displayed for informing users what categories are about to be filled.
# "%S" will be replaced with a list generated from the pre-defined categories.
# The text would be e.g. Also fill company, phone, email
phishingWarningMessage = Also fill %S
phishingWarningMessage2 = Fill %S

View File

@ -13,6 +13,10 @@
font-size: .7em;
}
.autofill-footer {
.autofill-footer > .autofill-warning {
font-size: .7em;
}
.autofill-footer > .autofill-option-button {
font-size: .77em;
}

View File

@ -12,3 +12,7 @@
.autofill-item-box > .profile-item-col > .profile-comment {
font-size: .9em;
}
.autofill-footer > .autofill-warning {
font-size: .9em;
}

View File

@ -10,7 +10,7 @@ xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .autofill-i
background-color: #F2F2F2;
}
xul|richlistitem[originaltype="autofill-footer"][selected="true"] > .autofill-footer {
xul|richlistitem[originaltype="autofill-footer"][selected="true"] > .autofill-item-box > .autofill-option-button {
background-color: #DCDCDE;
}
@ -29,7 +29,9 @@ xul|richlistitem[originaltype="autofill-footer"][selected="true"] > .autofill-fo
--item-width: 100%;
}
.autofill-footer {
.autofill-footer,
.autofill-footer[size="small"] {
--item-width: 100%;
--item-padding-vertical: 0;
--item-padding-horizontal: 0;
}
@ -81,7 +83,25 @@ xul|richlistitem[originaltype="autofill-footer"][selected="true"] > .autofill-fo
}
.autofill-footer {
flex-direction: column;
}
.autofill-footer > .autofill-footer-row {
display: flex;
justify-content: center;
align-items: center;
width: var(--item-width);
}
.autofill-footer > .autofill-warning {
padding: 2.5px 0;
color: #989898;
text-align: center;
background-color: rgba(248,232,28,.2);
border-bottom: 1px solid rgba(38,38,38,.15);
}
.autofill-footer > .autofill-option-button {
height: 41px;
background-color: #EDEDED;
justify-content: center;
}

View File

@ -10,7 +10,11 @@
font-size: .83em;
}
.autofill-footer {
.autofill-footer > .autofill-warning {
font-size: .83em;
}
.autofill-footer > .autofill-option-button {
font-size: .91em;
}

View File

@ -3,6 +3,16 @@
const URL = BASE_URL + "autocomplete_basic.html";
const PRIVACY_PREF_URL = "about:preferences#privacy";
async function expectWarningText(browser, expectedText) {
const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
const warningBox = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._warningTextBox;
await BrowserTestUtils.waitForCondition(() => {
return warningBox.textContent == expectedText;
}, `Waiting for expected warning text: ${expectedText}, Got ${warningBox.textContent}`);
ok(true, `Got expected warning text: ${expectedText}`);
}
add_task(async function setup_storage() {
await saveAddress(TEST_ADDRESS_1);
await saveAddress(TEST_ADDRESS_2);
@ -13,17 +23,11 @@ add_task(async function test_click_on_footer() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
await ContentTask.spawn(browser, {}, async function() {
content.document.getElementById("organization").focus();
});
await sleep(2000);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectPopupOpen(browser);
await openPopupOn(browser, "#organization");
// Click on the footer
const listItemElems = itemsBox.querySelectorAll(".autocomplete-richlistitem");
const optionButton = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._optionButton;
const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
await EventUtils.synthesizeMouseAtCenter(listItemElems[listItemElems.length - 1], {});
await EventUtils.synthesizeMouseAtCenter(optionButton, {});
await BrowserTestUtils.removeTab(await prefTabPromise);
ok(true, "Tab: preferences#privacy was successfully opened by clicking on the footer");
@ -37,16 +41,9 @@ add_task(async function test_click_on_footer() {
add_task(async function test_press_enter_on_footer() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
const {autoCompletePopup: {richlistbox: itemsBox}} = browser;
await ContentTask.spawn(browser, {}, async function() {
const input = content.document.getElementById("organization");
input.focus();
});
await sleep(2000);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectPopupOpen(browser);
const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
await openPopupOn(browser, "#organization");
// Navigate to the footer and press enter.
const listItemElems = itemsBox.querySelectorAll(".autocomplete-richlistitem");
const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
@ -56,5 +53,35 @@ add_task(async function test_press_enter_on_footer() {
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await BrowserTestUtils.removeTab(await prefTabPromise);
ok(true, "Tab: preferences#privacy was successfully opened by pressing enter on the footer");
// Ensure the popup is closed before entering the next test.
await ContentTask.spawn(browser, {}, async function() {
content.document.getElementById("organization").blur();
});
await BrowserTestUtils.waitForCondition(() => !autoCompletePopup.popupOpen);
});
});
add_task(async function test_phishing_warning() {
await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
const {autoCompletePopup, autoCompletePopup: {richlistbox: itemsBox}} = browser;
await openPopupOn(browser, "#street-address");
const warningBox = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._warningTextBox;
ok(warningBox, "Got phishing warning box");
await expectWarningText(browser, "Also fill company, address, phone, email");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectWarningText(browser, "Also fill company, phone, email");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectWarningText(browser, "Fill address");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectWarningText(browser, "Also fill company, address, phone, email");
// Ensure the popup is closed before entering the next test.
await ContentTask.spawn(browser, {}, async function() {
content.document.getElementById("street-address").blur();
});
await BrowserTestUtils.waitForCondition(() => !autoCompletePopup.popupOpen);
});
});

View File

@ -1,6 +1,7 @@
/* exported MANAGE_PROFILES_DIALOG_URL, EDIT_PROFILE_DIALOG_URL, BASE_URL,
TEST_ADDRESS_1, TEST_ADDRESS_2, TEST_ADDRESS_3,
sleep, expectPopupOpen, getAddresses, saveAddress, removeAddresses */
sleep, expectPopupOpen, openPopupOn,
getAddresses, saveAddress, removeAddresses */
"use strict";
@ -50,6 +51,16 @@ async function expectPopupOpen(browser) {
});
}
async function openPopupOn(browser, selector) {
/* eslint no-shadow: ["error", { "allow": ["selector"] }] */
await ContentTask.spawn(browser, {selector}, async function({selector}) {
content.document.querySelector(selector).focus();
});
await sleep(2000);
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await expectPopupOpen(browser);
}
function getAddresses() {
return new Promise(resolve => {
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {

View File

@ -48,7 +48,7 @@
padding-right: 2px;
}
%ifdef MOZ_PHOTON_THEME
#identity-box:not(.chromeUI) {
#identity-box:not(.chromeUI):not(.extensionPage) {
--urlbar-separator-color: transparent;
}
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity {
@ -63,6 +63,7 @@
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity,
#urlbar[pageproxystate=valid] > #identity-box.chromeUI,
#urlbar[pageproxystate=valid] > #identity-box.extensionPage,
#urlbar-display-box {
margin-inline-end: 4px;
border-inline-end: 1px solid var(--urlbar-separator-color);
@ -71,7 +72,8 @@
}
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity,
#urlbar[pageproxystate=valid] > #identity-box.chromeUI {
#urlbar[pageproxystate=valid] > #identity-box.chromeUI,
#urlbar[pageproxystate=valid] > #identity-box.extensionPage {
padding-inline-end: 4px;
}

View File

@ -623,14 +623,14 @@ groupbox {
font-size: 1.25rem;
position: absolute;
padding: 0 10px;
bottom: 100%;
background-color: #ffe900;
border: 1px solid #d7b600;
-moz-user-select: none;
}
.search-tooltip:hover,
.search-tooltip:hover::before {
filter: opacity(10%);
opacity: .1;
}
.search-tooltip::before {

View File

@ -129,20 +129,26 @@
#endif
skin/classic/browser/characterEncoding.svg (../shared/icons/characterEncoding.svg)
skin/classic/browser/chevron.svg (../shared/icons/chevron.svg)
#ifdef MOZ_PHOTON_ANIMATIONS
skin/classic/browser/chevron-animation.svg (../shared/icons/chevron-animation.svg)
#endif
skin/classic/browser/check.svg (../shared/icons/check.svg)
skin/classic/browser/containers.svg (../shared/icons/containers.svg)
skin/classic/browser/customize.svg (../shared/icons/customize.svg)
skin/classic/browser/developer.svg (../shared/icons/developer.svg)
skin/classic/browser/device-mobile.svg (../shared/icons/device-mobile.svg)
#ifdef MOZ_PHOTON_THEME
skin/classic/browser/device-desktop.svg (../shared/icons/device-desktop.svg)
#endif
#ifndef MOZ_PHOTON_THEME
skin/classic/browser/download.svg (../shared/icons/download.svg)
#endif
skin/classic/browser/edit-copy.svg (../shared/icons/edit-copy.svg)
skin/classic/browser/edit-cut.svg (../shared/icons/edit-cut.svg)
skin/classic/browser/edit-paste.svg (../shared/icons/edit-paste.svg)
#ifdef MOZ_PHOTON_THEME
skin/classic/browser/email-link.svg (../shared/icons/email-link.svg)
#endif
skin/classic/browser/feed.svg (../shared/icons/feed.svg)
skin/classic/browser/find.svg (../shared/icons/find.svg)
skin/classic/browser/forget.svg (../shared/icons/forget.svg)
@ -154,13 +160,17 @@
skin/classic/browser/history.svg (../shared/icons/history.svg)
skin/classic/browser/home.svg (../shared/icons/home.svg)
skin/classic/browser/library.svg (../shared/icons/library.svg)
#ifdef MOZ_PHOTON_THEME
skin/classic/browser/link.svg (../shared/icons/link.svg)
#endif
skin/classic/browser/mail.svg (../shared/icons/mail.svg)
skin/classic/browser/menu.svg (../shared/icons/menu.svg)
skin/classic/browser/new-tab.svg (../shared/icons/new-tab.svg)
skin/classic/browser/new-window.svg (../shared/icons/new-window.svg)
skin/classic/browser/open.svg (../shared/icons/open.svg)
#ifdef MOZ_PHOTON_THEME
skin/classic/browser/page-action.svg (../shared/icons/page-action.svg)
#endif
skin/classic/browser/print.svg (../shared/icons/print.svg)
skin/classic/browser/privateBrowsing.svg (../shared/icons/privateBrowsing.svg)
skin/classic/browser/quit.svg (../shared/icons/quit.svg)
@ -194,7 +204,9 @@
skin/classic/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/connecting@2x.png (../shared/tabbrowser/connecting@2x.png)
skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
#ifdef MOZ_PHOTON_THEME
skin/classic/browser/tabbrowser/indicator-tab-attention.svg (../shared/tabbrowser/indicator-tab-attention.svg)
#endif
skin/classic/browser/tabbrowser/pendingpaint.png (../shared/tabbrowser/pendingpaint.png)
skin/classic/browser/tabbrowser/tab-audio-playing.svg (../shared/tabbrowser/tab-audio-playing.svg)
skin/classic/browser/tabbrowser/tab-audio-muted.svg (../shared/tabbrowser/tab-audio-muted.svg)

View File

@ -741,10 +741,6 @@ body,
/* Network details panel toggle */
.network-details-panel-toggle[disabled] {
display: none;
}
.network-details-panel-toggle:dir(ltr)::before,
.network-details-panel-toggle.pane-collapsed:dir(rtl)::before {
background-image: var(--theme-pane-collapse-image);

View File

@ -96,7 +96,8 @@ ImageFactory::CreateImage(nsIRequest* aRequest,
// Record the image load for startup performance testing.
if (NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(obs)) {
NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
if (obs) {
nsAutoCString spec;
aURI->GetSpec(spec);
obs->NotifyObservers(nullptr, "image-loading", NS_ConvertUTF8toUTF16(spec).get());

View File

@ -1399,7 +1399,8 @@ RasterImage::DrawInternal(DrawableSurface&& aSurface,
// Record the image drawing for startup performance testing.
if (NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(obs)) {
NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
if (obs) {
nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
nsAutoCString spec;
imageURI->GetSpec(spec);

View File

@ -1029,7 +1029,8 @@ VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
// Record the image drawing for startup performance testing.
if (NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(obs)) {
NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
if (obs) {
nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
nsAutoCString spec;
imageURI->GetSpec(spec);

View File

@ -213,11 +213,11 @@ const JSClass JSXrayTraits::HolderClass = {
};
bool
OpaqueXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, HandleObject wrapper,
OpaqueXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target,
HandleObject holder, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc);
bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, id, desc);
if (!ok || desc.object())
return ok;
@ -518,13 +518,13 @@ ShouldResolveStaticProperties(JSProtoKey key)
}
bool
JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
HandleObject wrapper, HandleObject holder,
JSXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper,
HandleObject target, HandleObject holder,
HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
// Call the common code.
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder,
bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder,
id, desc);
if (!ok || desc.object())
return ok;
@ -541,7 +541,6 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
return true;
}
RootedObject target(cx, getTargetObject(wrapper));
JSProtoKey key = getProtoKey(holder);
if (!isPrototype(holder)) {
// For Object and Array instances, we expose some properties from the
@ -1505,12 +1504,11 @@ wrappedJSObject_getter(JSContext* cx, unsigned argc, Value* vp)
}
bool
XrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
HandleObject wrapper, HandleObject holder, HandleId id,
XrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target,
HandleObject holder, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
desc.object().set(nullptr);
RootedObject target(cx, getTargetObject(wrapper));
RootedObject expando(cx);
if (!getExpandoObject(cx, target, wrapper, &expando))
return false;
@ -1577,13 +1575,13 @@ XrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
}
bool
XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
HandleObject wrapper, HandleObject holder,
XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper,
HandleObject target, HandleObject holder,
HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
// Call the common code.
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder,
bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder,
id, desc);
if (!ok || desc.object())
return ok;
@ -1683,12 +1681,12 @@ XPCWrappedNativeXrayTraits::construct(JSContext* cx, HandleObject wrapper,
}
bool
DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, HandleObject wrapper,
DOMXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target,
HandleObject holder, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
// Call the common code.
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc);
bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, id, desc);
if (!ok || desc.object())
return ok;
@ -1722,9 +1720,8 @@ DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, Handl
return true;
}
RootedObject obj(cx, getTargetObject(wrapper));
bool cacheOnHolder;
if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc, cacheOnHolder))
if (!XrayResolveOwnProperty(cx, wrapper, target, id, desc, cacheOnHolder))
return false;
MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
@ -1895,14 +1892,14 @@ HasNativeProperty(JSContext* cx, HandleObject wrapper, HandleId id, bool* hasPro
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
XrayTraits* traits = GetXrayTraits(wrapper);
MOZ_ASSERT(traits);
RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
RootedObject holder(cx, traits->ensureHolder(cx, wrapper));
NS_ENSURE_TRUE(holder, false);
*hasProp = false;
Rooted<PropertyDescriptor> desc(cx);
const Wrapper* handler = Wrapper::wrapperHandler(wrapper);
// Try resolveOwnProperty.
if (!traits->resolveOwnProperty(cx, *handler, wrapper, holder, id, &desc))
if (!traits->resolveOwnProperty(cx, wrapper, target, holder, id, &desc))
return false;
if (desc.object()) {
*hasProp = true;
@ -2011,6 +2008,7 @@ XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext* cx, HandleObject wra
{
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
if (!holder)
@ -2036,7 +2034,7 @@ XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext* cx, HandleObject wra
// and unconditionally caches what it finds on the holder.
// Check resolveOwnProperty.
if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc))
return false;
// Check the holder.
@ -2099,9 +2097,10 @@ XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext* cx, HandleObject
{
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc))
return false;
if (desc.object())
desc.object().set(wrapper);

View File

@ -77,8 +77,8 @@ public:
// on the holder. If the result is not cached, the lookup will happen afresh
// for each access, which is the right thing for things like dynamic NodeList
// properties.
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper,
JS::HandleObject wrapper, JS::HandleObject holder,
virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper,
JS::HandleObject target, JS::HandleObject holder,
JS::HandleId id, JS::MutableHandle<JS::PropertyDescriptor> desc);
bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
@ -150,7 +150,7 @@ public:
virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
JS::HandleObject holder, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
JS::HandleObject holder, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
@ -204,7 +204,7 @@ public:
// but we can't do that yet because XrayUtils::HasNativeProperty calls this.
return true;
}
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
JS::HandleObject holder, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
@ -251,7 +251,7 @@ public:
MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
}
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
JS::HandleObject holder, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
@ -368,7 +368,7 @@ public:
MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
}
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
JS::HandleObject holder, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc) override;

View File

@ -961,18 +961,66 @@ ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
if (Element* parent = aElement->GetFlattenedTreeParentElementForStyle()) {
parent->NoteDirtyDescendantsForServo();
}
PostRestyleEvent(aElement, restyleHint, changeHint);
if (restyleHint || changeHint) {
Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
}
}
static inline bool
AttributeInfluencesOtherPseudoClassState(Element* aElement, nsIAtom* aAttribute)
AttributeInfluencesOtherPseudoClassState(const Element& aElement,
const nsIAtom* aAttribute)
{
// We must record some state for :-moz-browser-frame and
// :-moz-table-border-nonzero.
return (aAttribute == nsGkAtoms::mozbrowser &&
aElement->IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame)) ||
(aAttribute == nsGkAtoms::border &&
aElement->IsHTMLElement(nsGkAtoms::table));
if (aAttribute == nsGkAtoms::mozbrowser) {
return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
}
if (aAttribute == nsGkAtoms::border) {
return aElement.IsHTMLElement(nsGkAtoms::table);
}
return false;
}
static inline bool
NeedToRecordAttrChange(const ServoStyleSet& aStyleSet,
const Element& aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
bool* aInfluencesOtherPseudoClassState)
{
*aInfluencesOtherPseudoClassState =
AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
// If the attribute influences one of the pseudo-classes that are backed by
// attributes, we just record it.
if (*aInfluencesOtherPseudoClassState) {
return true;
}
// We assume that id and class attributes are used in class/id selectors, and
// thus record them.
//
// TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
// presumably we could try to filter the old and new id, but it's not clear
// it's worth it.
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
return true;
}
// We always record lang="", even though we force a subtree restyle when it
// changes, since it can change how its siblings match :lang(..) due to
// selectors like :lang(..) + div.
if (aAttribute == nsGkAtoms::lang) {
return true;
}
// Otherwise, just record the attribute change if a selector in the page may
// reference it from an attribute selector.
return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
}
void
@ -1002,15 +1050,12 @@ ServoRestyleManager::TakeSnapshotForAttributeChange(Element* aElement,
return;
}
bool influencesOtherPseudoClassState =
AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
if (!influencesOtherPseudoClassState &&
!((aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::id ||
aAttribute == nsGkAtoms::_class)) ||
aAttribute == nsGkAtoms::lang ||
StyleSet()->MightHaveAttributeDependency(*aElement, aAttribute))) {
bool influencesOtherPseudoClassState;
if (!NeedToRecordAttrChange(*StyleSet(),
*aElement,
aNameSpaceID,
aAttribute,
&influencesOtherPseudoClassState)) {
return;
}
@ -1026,6 +1071,21 @@ ServoRestyleManager::TakeSnapshotForAttributeChange(Element* aElement,
}
}
// For some attribute changes we must restyle the whole subtree:
//
// * <td> is affected by the cellpadding on its ancestor table
// * lang="" and xml:lang="" can affect all descendants due to :lang()
//
static inline bool
AttributeChangeRequiresSubtreeRestyle(const Element& aElement, nsIAtom* aAttr)
{
if (aAttr == nsGkAtoms::cellpadding) {
return aElement.IsHTMLElement(nsGkAtoms::table);
}
return aAttr == nsGkAtoms::lang;
}
void
ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
nsIAtom* aAttribute, int32_t aModType,
@ -1033,30 +1093,25 @@ ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
{
MOZ_ASSERT(!mInStyleRefresh);
nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
if (primaryFrame) {
if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
if (hint) {
PostRestyleEvent(aElement, nsRestyleHint(0), hint);
}
auto changeHint = nsChangeHint(0);
auto restyleHint = nsRestyleHint(0);
changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
if (aAttribute == nsGkAtoms::style) {
PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0));
restyleHint |= eRestyle_StyleAttribute;
} else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
restyleHint |= eRestyle_Subtree;
} else if (aElement->IsAttributeMapped(aAttribute)) {
restyleHint |= eRestyle_Self;
}
// For some attribute changes we must restyle the whole subtree:
//
// * <td> is affected by the cellpadding on its ancestor table
// * lang="" and xml:lang="" can affect all descendants due to :lang()
if ((aAttribute == nsGkAtoms::cellpadding &&
aElement->IsHTMLElement(nsGkAtoms::table)) ||
aAttribute == nsGkAtoms::lang) {
PostRestyleEvent(aElement, eRestyle_Subtree, nsChangeHint(0));
} else if (aElement->IsAttributeMapped(aAttribute)) {
Servo_NoteExplicitHints(aElement, eRestyle_Self, nsChangeHint(0));
if (restyleHint || changeHint) {
Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
}
}

View File

@ -842,6 +842,19 @@ nsPresContext::Init(nsDeviceContext* aDeviceContext)
mDeviceContext = aDeviceContext;
// In certain rare cases (such as changing page mode), we tear down layout
// state and re-initialize a new prescontext for a document. Given that we
// hang style state off the DOM, we detect that re-initialization case and
// lazily drop the servo data. We don't do this eagerly during layout teardown
// because that would incur an extra whole-tree traversal that's unnecessary
// most of the time.
if (mDocument->IsStyledByServo()) {
Element* root = mDocument->GetRootElement();
if (root && root->HasServoData()) {
ServoRestyleManager::ClearServoDataFromSubtree(root);
}
}
if (mDeviceContext->SetFullZoom(mFullZoom))
mDeviceContext->FlushFontCache();
mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();

View File

@ -157,6 +157,9 @@ SERVO_BINDING_FUNC(Servo_StyleRule_GetSpecificityAtIndex, void,
uint64_t* specificity)
SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorCount, void,
RawServoStyleRuleBorrowed rule, uint32_t* count)
SERVO_BINDING_FUNC(Servo_StyleRule_SelectorMatchesElement, bool,
RawServoStyleRuleBorrowed, RawGeckoElementBorrowed,
uint32_t index, mozilla::CSSPseudoElementType pseudo_type)
SERVO_BINDING_FUNC(Servo_ImportRule_GetHref, void,
RawServoImportRuleBorrowed rule, nsAString* result)
SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet,

View File

@ -279,7 +279,14 @@ ServoStyleRule::SelectorMatchesElement(Element* aElement,
const nsAString& aPseudo,
bool* aMatches)
{
// TODO Bug 1370502
nsCOMPtr<nsIAtom> pseudoElt = NS_Atomize(aPseudo);
const CSSPseudoElementType pseudoType =
nsCSSPseudoElements::GetPseudoType(pseudoElt,
CSSEnabledState::eIgnoreEnabledState);
*aMatches = Servo_StyleRule_SelectorMatchesElement(mRawRule,
aElement,
aSelectorIndex,
pseudoType);
return NS_OK;
}

View File

@ -82,30 +82,6 @@ ServoStyleSet::Init(nsPresContext* aPresContext, nsBindingManager* aBindingManag
// mRawSet, so there was nothing to flush.
}
// Traverses the given frame tree, calling ClearServoDataFromSubtree on
// any NAC that is found.
static void
ClearServoDataFromNAC(nsIFrame* aFrame)
{
nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame);
if (ac) {
nsTArray<nsIContent*> nodes;
ac->AppendAnonymousContentTo(nodes, 0);
for (nsIContent* node : nodes) {
if (node->IsElement()) {
ServoRestyleManager::ClearServoDataFromSubtree(node->AsElement());
}
}
}
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
for (nsIFrame* child : lists.CurrentList()) {
ClearServoDataFromNAC(child);
}
}
}
void
ServoStyleSet::BeginShutdown()
{
@ -117,55 +93,6 @@ ServoStyleSet::BeginShutdown()
doc->CSSLoader()->RemoveObserver(mStyleRuleMap);
mStyleRuleMap = nullptr;
}
// It's important to do this before mRawSet is released, since that will cause
// a RuleTree GC, which needs to happen after we have dropped all of the
// document's strong references to RuleNodes. We also need to do it here,
// in BeginShutdown, and not in Shutdown, since Shutdown happens after the
// frame tree has been destroyed, but before the script runners that delete
// native anonymous content (which also could be holding on the RuleNodes)
// have run. By clearing style here, before the frame tree is destroyed,
// the AllChildrenIterator will find the anonymous content.
//
// Note that this is pretty bad for performance; we should find a way to
// get by with the ServoNodeDatas being dropped as part of the document
// going away.
DocumentStyleRootIterator iter(doc);
while (Element* root = iter.GetNextStyleRoot()) {
ServoRestyleManager::ClearServoDataFromSubtree(root);
}
// We can also have some cloned canvas custom content stored in the document
// (as done in nsCanvasFrame::DestroyFrom), due to bug 1348480, when we create
// the clone (wastefully) during PresShell destruction. Clear data from that
// clone.
for (RefPtr<AnonymousContent>& ac : doc->GetAnonymousContents()) {
ServoRestyleManager::ClearServoDataFromSubtree(ac->GetContentNode());
}
// Also look for any NAC created by position:fixed replicated frames in a
// print or print preview presentation.
if (nsIPresShell* shell = doc->GetShell()) {
if (nsIFrame* pageSeq = shell->FrameConstructor()->GetPageSequenceFrame()) {
auto iter = pageSeq->PrincipalChildList().begin();
if (*iter) {
++iter; // skip past first page
while (nsIFrame* page = *iter) {
MOZ_ASSERT(page->IsPageFrame());
// The position:fixed replicated frames live on the PageContent frame.
nsIFrame* pageContent = page->PrincipalChildList().FirstChild();
MOZ_ASSERT(pageContent && pageContent->IsPageContentFrame());
for (nsIFrame* f : pageContent->GetChildList(nsIFrame::kFixedList)) {
ClearServoDataFromNAC(f);
}
++iter;
}
}
}
}
}
void
@ -1425,14 +1352,15 @@ ServoStyleSet::StyleRuleMap()
bool
ServoStyleSet::MightHaveAttributeDependency(const Element& aElement,
nsIAtom* aAttribute)
nsIAtom* aAttribute) const
{
return Servo_StyleSet_MightHaveAttributeDependency(
mRawSet.get(), &aElement, aAttribute);
}
bool
ServoStyleSet::HasStateDependency(const Element& aElement, EventStates aState)
ServoStyleSet::HasStateDependency(const Element& aElement,
EventStates aState) const
{
return Servo_StyleSet_HasStateDependency(
mRawSet.get(), &aElement, aState.ServoValue());

View File

@ -459,7 +459,7 @@ public:
* a style sheet.
*/
bool MightHaveAttributeDependency(const dom::Element& aElement,
nsIAtom* aAttribute);
nsIAtom* aAttribute) const;
/**
* Returns true if a change in event state on an element might require
@ -469,7 +469,8 @@ public:
* the changed state isn't depended upon by any pseudo-class selectors
* in a style sheet.
*/
bool HasStateDependency(const dom::Element& aElement, EventStates aState);
bool HasStateDependency(const dom::Element& aElement,
EventStates aState) const;
private:
// On construction, sets sInServoTraversal to the given ServoStyleSet.

View File

@ -829,6 +829,10 @@ MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const
if (rv != mp4parse_status_OK) {
continue;
}
// JPEG 'video' decoder is not supported in media stack yet.
if (track_info.codec == mp4parse_codec::mp4parse_codec_JPEG) {
continue;
}
if (TrackTypeEqual(aType, track_info.track_type)) {
total += 1;
}
@ -901,6 +905,7 @@ MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType,
case mp4parse_codec_VP9: codec_string = "vp9"; break;
case mp4parse_codec_MP3: codec_string = "mp3"; break;
case mp4parse_codec_MP4V: codec_string = "mp4v"; break;
case mp4parse_codec_JPEG: codec_string = "jpeg"; break;
case mp4parse_codec_AC3: codec_string = "ac-3"; break;
case mp4parse_codec_EC3: codec_string = "ec-3"; break;
}

View File

@ -21,7 +21,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing//utility/ooura_fft.h"
#include "webrtc/modules/audio_processing/utility/ooura_fft.h"
#include <math.h>

View File

@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing//utility/ooura_fft.h"
#include "webrtc/modules/audio_processing/utility/ooura_fft.h"
#include <emmintrin.h>

View File

@ -68,7 +68,6 @@ mozmem_malloc_impl(_ZdaPvRKSt9nothrow_t)(void *ptr)
#undef strndup
#undef strdup
#ifndef XP_DARWIN
MOZ_MEMORY_API char *
strndup_impl(const char *src, size_t len)
{
@ -86,7 +85,6 @@ strdup_impl(const char *src)
size_t len = strlen(src);
return strndup_impl(src, len);
}
#endif /* XP_DARWIN */
#ifdef ANDROID
#include <stdarg.h>

View File

@ -53,11 +53,10 @@
*
* - On MacOSX, the system libc has a zone allocator, which allows us to
* hook custom malloc implementation functions without exporting them.
* The malloc implementation functions are all prefixed with "je_" and used
* this way from the custom zone allocator. They are not exported.
* Duplication functions are not included, since they will call the custom
* zone allocator anyways. Jemalloc-specific functions are also left
* unprefixed.
* However, since we want things in Firefox to skip the system zone
* allocator, the malloc implementation functions are all exported
* unprefixed, as well as duplication functions.
* Jemalloc-specific functions are also left unprefixed.
*
* - On Android and Gonk, all functions are left unprefixed. Additionally,
* C++ allocation functions (operator new/delete) are also exported and
@ -134,7 +133,7 @@
# define mozmem_jemalloc_impl(a) je_ ## a
# else
# define MOZ_JEMALLOC_API MOZ_EXTERN_C MFBT_API
# if (defined(XP_WIN) || defined(XP_DARWIN))
# if defined(XP_WIN)
# if defined(MOZ_REPLACE_MALLOC)
# define mozmem_malloc_impl(a) a ## _impl
# else

View File

@ -85,22 +85,20 @@ android {
flavorDimensions "audience", "skin"
productFlavors {
// For API 21+ - with multi dex, this will be faster for local development.
// For API 21+ - with pre-dexing, this will be faster for local development.
local {
dimension "audience"
// For multi dex, setting `minSdkVersion 21` allows the Android gradle plugin to
// For pre-dexing, setting `minSdkVersion 21` allows the Android gradle plugin to
// pre-DEX each module and produce an APK that can be tested on
// Android Lollipop without time consuming DEX merging processes.
minSdkVersion 21
dexOptions {
preDexLibraries true
multiDexEnabled true
}
}
// For API < 21 - does not support multi dex because local development
// is slow in that case. Most builds will not require multi dex so this
// should not be an issue.
// For API < 21 - does not support pre-dexing because local development
// is slow in that case.
localOld {
dimension "audience"
}
@ -268,7 +266,7 @@ dependencies {
}
// Include LeakCanary in most gradle based builds. LeakCanary adds about 5k methods, so we disable
// it for the (non-proguarded, non-multidex) localOld builds to allow space for other libraries.
// it for the (non-proguarded, non-predex) localOld builds to allow space for other libraries.
// Gradle based tests include the no-op version. Mach based builds only include the no-op version
// of this library.
// It doesn't seem like there is a non-trivial way to be conditional on 'localOld', so instead we explicitly

View File

@ -169,6 +169,10 @@ public class BookmarkEditFragment extends DialogFragment implements SelectFolder
return;
}
// When coming back from SelectFolderFragment, we update view with data stored in `bookmark`,
// so before navigating, we have to save current title from nameText into `bookmark`.
bookmark.title = nameText.getText().toString();
final SelectFolderFragment dialog = SelectFolderFragment.newInstance(bookmark.parentId, bookmark.id);
dialog.setTargetFragment(BookmarkEditFragment.this, 0);
dialog.show(getActivity().getSupportFragmentManager(), "select-bookmark-folder");

View File

@ -47,7 +47,8 @@ public final class HardwareCodecCapabilityUtils {
"GT-I9505", // S4
"GT-I9515", // S4
"SGH-I337", // S4
"SAMSUNG-SGH-I337" // S4
"SAMSUNG-SGH-I337", // S4
"LG-D605" // LG Optimus L9 II
};
@WrapForJNI

View File

@ -478,7 +478,7 @@ HasStableTSC()
// detect if the Advanced Power Management feature is supported
__cpuid(regs, 0x80000000);
if (regs[0] < 0x80000007) {
if ((unsigned int)regs[0] < 0x80000007) {
// XXX should we return true here? If there is no APM there may be
// no way how TSC can run out of sync among cores.
return false;

View File

@ -2,22 +2,22 @@
* Copyright (c) 2007 Henri Sivonen
* Copyright (c) 2008-2015 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
@ -25,19 +25,19 @@ package nu.validator.htmlparser.impl;
import java.io.IOException;
import org.xml.sax.SAXException;
import nu.validator.htmlparser.annotation.Auto;
import nu.validator.htmlparser.annotation.Inline;
import nu.validator.htmlparser.common.ByteReadable;
import org.xml.sax.SAXException;
public abstract class MetaScanner {
/**
* Constant for "charset".
*/
private static final char[] CHARSET = { 'h', 'a', 'r', 's', 'e', 't' };
/**
* Constant for "content".
*/
@ -58,13 +58,13 @@ public abstract class MetaScanner {
private static final int NO = 0;
private static final int M = 1;
private static final int E = 2;
private static final int T = 3;
private static final int A = 4;
private static final int DATA = 0;
private static final int TAG_OPEN = 1;
@ -90,7 +90,7 @@ public abstract class MetaScanner {
private static final int AFTER_ATTRIBUTE_VALUE_QUOTED = 11;
private static final int MARKUP_DECLARATION_OPEN = 13;
private static final int MARKUP_DECLARATION_HYPHEN = 14;
private static final int COMMENT_START = 15;
@ -102,11 +102,11 @@ public abstract class MetaScanner {
private static final int COMMENT_END_DASH = 18;
private static final int COMMENT_END = 19;
private static final int SELF_CLOSING_START_TAG = 20;
private static final int HTTP_EQUIV_NOT_SEEN = 0;
private static final int HTTP_EQUIV_CONTENT_TYPE = 1;
private static final int HTTP_EQUIV_OTHER = 2;
@ -115,7 +115,7 @@ public abstract class MetaScanner {
* The data source.
*/
protected ByteReadable readable;
/**
* The state of the state machine that recognizes the tag name "meta".
*/
@ -125,7 +125,7 @@ public abstract class MetaScanner {
* The current position in recognizing the attribute name "content".
*/
private int contentIndex = Integer.MAX_VALUE;
/**
* The current position in recognizing the attribute name "charset".
*/
@ -155,13 +155,13 @@ public abstract class MetaScanner {
* Accumulation buffer for attribute values.
*/
private @Auto char[] strBuf;
private String content;
private String charset;
private int httpEquivState;
// CPPONLY: private TreeBuilder treeBuilder;
public MetaScanner(
@ -180,18 +180,19 @@ public abstract class MetaScanner {
this.charset = null;
this.httpEquivState = HTTP_EQUIV_NOT_SEEN;
// CPPONLY: this.treeBuilder = tb;
// CPPONLY: this.mEncoding = null;
}
@SuppressWarnings("unused") private void destructor() {
Portability.releaseString(content);
Portability.releaseString(charset);
}
// [NOCPP[
/**
* Reads a byte from the data source.
*
*
* -1 means end.
* @return
* @throws IOException
@ -243,7 +244,7 @@ public abstract class MetaScanner {
metaState = M;
state = MetaScanner.TAG_NAME;
break tagopenloop;
// continue stateloop;
// continue stateloop;
case '!':
state = MetaScanner.MARKUP_DECLARATION_OPEN;
continue stateloop;
@ -350,7 +351,7 @@ public abstract class MetaScanner {
httpEquivIndex = Integer.MAX_VALUE;
contentTypeIndex = Integer.MAX_VALUE;
state = MetaScanner.ATTRIBUTE_NAME;
break beforeattributenameloop;
break beforeattributenameloop;
case 'h':
case 'H':
contentIndex = Integer.MAX_VALUE;
@ -358,7 +359,7 @@ public abstract class MetaScanner {
httpEquivIndex = 0;
contentTypeIndex = Integer.MAX_VALUE;
state = MetaScanner.ATTRIBUTE_NAME;
break beforeattributenameloop;
break beforeattributenameloop;
default:
contentIndex = Integer.MAX_VALUE;
charsetIndex = Integer.MAX_VALUE;
@ -416,7 +417,7 @@ public abstract class MetaScanner {
++httpEquivIndex;
} else {
httpEquivIndex = Integer.MAX_VALUE;
}
}
}
continue;
}
@ -823,7 +824,7 @@ public abstract class MetaScanner {
httpEquivState = HTTP_EQUIV_NOT_SEEN;
return stop;
}
private boolean handleTagInner() throws SAXException {
if (charset != null && tryCharset(charset)) {
return true;
@ -844,11 +845,11 @@ public abstract class MetaScanner {
/**
* Tries to switch to an encoding.
*
*
* @param encoding
* @return <code>true</code> if successful
* @throws SAXException
*/
protected abstract boolean tryCharset(String encoding) throws SAXException;
}

View File

@ -2,22 +2,22 @@
* Copyright (c) 2007 Henri Sivonen
* Copyright (c) 2008-2015 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

View File

@ -2,22 +2,22 @@
* Copyright (c) 2007 Henri Sivonen
* Copyright (c) 2008-2015 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

View File

@ -404,10 +404,8 @@ class nsHtml5TreeBuilder : public nsAHtml5TreeBuilderState
void addAttributesToHtml(nsHtml5HtmlAttributes* attributes);
void pushHeadPointerOntoStack();
void reconstructTheActiveFormattingElements();
public:
void notifyUnusedStackNode(int32_t idxInStackNodes);
private:
nsHtml5StackNode* getUnusedStackNode();
nsHtml5StackNode* createStackNode(int32_t flags,

View File

@ -144,14 +144,14 @@ class ConfigEnvironment(object):
self.substs['ACDEFINES'] = ' '.join(['-D%s=%s' % (name,
shell_quote(self.defines[name]).replace('$', '$$'))
for name in sorted(global_defines)])
def serialize(obj):
def serialize(name, obj):
if isinstance(obj, StringTypes):
return obj
if isinstance(obj, Iterable):
return ' '.join(obj)
raise Exception('Unhandled type %s', type(obj))
raise Exception('Unhandled type %s for %s', type(obj), str(name))
self.substs['ALLSUBSTS'] = '\n'.join(sorted(['%s = %s' % (name,
serialize(self.substs[name])) for name in self.substs if self.substs[name]]))
serialize(name, self.substs[name])) for name in self.substs if self.substs[name]]))
self.substs['ALLEMPTYSUBSTS'] = '\n'.join(sorted(['%s =' % name
for name in self.substs if not self.substs[name]]))

View File

@ -273,12 +273,17 @@ license file's hash.
('mozjs_sys', 'js/src'),
('geckodriver', 'testing/geckodriver'),
)
lockfiles = []
for (lib, crate_root) in crates_and_roots:
path = mozpath.join(self.topsrcdir, crate_root)
# We use check_call instead of mozprocess to ensure errors are displayed.
# We do an |update -p| here to regenerate the Cargo.lock file with minimal changes. See bug 1324462
subprocess.check_call([cargo, 'update', '--manifest-path', mozpath.join(path, 'Cargo.toml'), '-p', lib], cwd=self.topsrcdir)
subprocess.check_call([cargo, 'vendor', '--quiet', '--no-delete', '--sync', mozpath.join(path, 'Cargo.lock'), vendor_dir], cwd=self.topsrcdir)
lockfiles.append('--sync')
lockfiles.append(mozpath.join(path, 'Cargo.lock'))
subprocess.check_call([cargo, 'vendor', '--quiet', '--no-delete'] + lockfiles + [vendor_dir], cwd=self.topsrcdir)
if not self._check_licenses(vendor_dir):
self.log(logging.ERROR, 'license_check_failed', {},

View File

@ -229,8 +229,9 @@ static const char contentSandboxRules[] = R"(
(if (string? debugWriteDir)
(allow file-write* (subpath debugWriteDir)))
; bug 1324610
(allow network-outbound (literal "/private/var/run/cupsd"))
; bug 1324610
(allow network-outbound file-read*
(literal "/private/var/run/cupsd"))
(allow-shared-list "org.mozilla.plugincontainer")
@ -275,11 +276,8 @@ static const char contentSandboxRules[] = R"(
; we don't have a profile dir
(allow file-read* (require-not (home-subpath "/Library")))))))
; level 3: global read access permitted, no global write access,
; no read access to the home directory,
; no read access to /private/var (but read-metadata allowed above),
; no read access to /{Volumes,Network,Users}
; read access permitted to $PROFILE/{extensions,chrome}
; level 3: no global read/write access,
; read access permitted to $PROFILE/{extensions,chrome}
(if (string=? sandbox-level-3 "TRUE")
(if (string=? hasFilePrivileges "TRUE")
; This process has blanket file read privileges
@ -287,27 +285,9 @@ static const char contentSandboxRules[] = R"(
; This process does not have blanket file read privileges
(if (string=? hasProfileDir "TRUE")
; we have a profile dir
(begin
(allow file-read* (require-all
(require-not (subpath home-path))
(require-not (subpath profileDir))
(require-not (subpath "/Volumes"))
(require-not (subpath "/Network"))
(require-not (subpath "/Users"))
(require-not (subpath "/private/var"))))
(allow file-read* (literal "/private/var/run/cupsd"))
(allow file-read*
(profile-subpath "/extensions")
(profile-subpath "/chrome")))
; we don't have a profile dir
(begin
(allow file-read* (require-all
(require-not (subpath home-path))
(require-not (subpath "/Volumes"))
(require-not (subpath "/Network"))
(require-not (subpath "/Users"))
(require-not (subpath "/private/var"))))
(allow file-read* (literal "/private/var/run/cupsd"))))))
(profile-subpath "/extensions")
(profile-subpath "/chrome")))))
; accelerated graphics
(allow-shared-preferences-read "com.apple.opengl")
@ -330,7 +310,7 @@ static const char contentSandboxRules[] = R"(
(iokit-user-client-class "NVDVDContextTesla")
(iokit-user-client-class "Gen6DVDContext"))
; bug 1237847
; bug 1237847
(allow file-read* file-write*
(subpath appTempDir))
)";

View File

@ -812,20 +812,13 @@ Sync11Service.prototype = {
throw "Application is offline, login should not be called";
}
if (this._checkSetup() == CLIENT_NOT_CONFIGURED) {
throw "Aborting login, client not configured.";
}
// Ask the identity manager to explicitly login now.
this._log.info("Logging in the user.");
let cb = Async.makeSpinningCallback();
this.identity.ensureLoggedIn().then(
() => cb(null),
err => cb(err || "ensureLoggedIn failed")
);
// Just let any errors bubble up - they've more context than we do!
cb.wait();
try {
Async.promiseSpinningly(this.identity.ensureLoggedIn());
} finally {
this._checkSetup(); // _checkSetup has a side effect of setting the right state.
}
this._updateCachedURLs();

View File

@ -85,6 +85,17 @@ add_task(async function test_login_logout() {
do_check_eq(Service.status.login, LOGIN_SUCCEEDED);
do_check_true(Service.isLoggedIn);
_("Profile refresh edge case: FxA configured but prefs reset");
Service.startOver();
let config = makeIdentityConfig({ username: "johndoe" }, server);
config.fxaccount.token.endpoint = server.baseURI + "/1.1/" + config.username + "/";
configureFxAccountIdentity(Service.identity, config);
Service.login();
do_check_eq(Service.status.service, STATUS_OK);
do_check_eq(Service.status.login, LOGIN_SUCCEEDED);
do_check_true(Service.isLoggedIn);
_("Logout.");
Service.logout();
do_check_false(Service.isLoggedIn);

View File

@ -2144,6 +2144,16 @@ extern "C" {
pub fn Servo_StyleRule_GetSelectorCount(rule: RawServoStyleRuleBorrowed,
count: *mut u32);
}
extern "C" {
pub fn Servo_StyleRule_SelectorMatchesElement(arg1:
RawServoStyleRuleBorrowed,
arg2:
RawGeckoElementBorrowed,
index: u32,
pseudo_type:
CSSPseudoElementType)
-> bool;
}
extern "C" {
pub fn Servo_ImportRule_GetHref(rule: RawServoImportRuleBorrowed,
result: *mut nsAString);

View File

@ -694,6 +694,11 @@ impl<'le> GeckoElement<'le> {
let node = self.as_node();
unsafe { Gecko_GetDocumentLWTheme(node.owner_doc()) }
}
/// Owner document quirks mode getter.
pub fn owner_document_quirks_mode(&self) -> QuirksMode {
self.as_node().owner_doc().mCompatMode.into()
}
}
/// Converts flags from the layout used by rust-selectors to the layout used

View File

@ -60,7 +60,7 @@ class Keyword(object):
extra_gecko_values=None, extra_servo_values=None,
aliases=None,
extra_gecko_aliases=None, extra_servo_aliases=None,
gecko_strip_moz_prefix=True,
gecko_strip_moz_prefix=None,
gecko_inexhaustive=None):
self.name = name
self.values = values.split()
@ -75,7 +75,8 @@ class Keyword(object):
self.extra_gecko_aliases = parse_aliases(extra_gecko_aliases or "")
self.extra_servo_aliases = parse_aliases(extra_servo_aliases or "")
self.consts_map = {} if custom_consts is None else custom_consts
self.gecko_strip_moz_prefix = gecko_strip_moz_prefix
self.gecko_strip_moz_prefix = True \
if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix
self.gecko_inexhaustive = gecko_inexhaustive or (gecko_enum_prefix is None)
def gecko_values(self):

View File

@ -11,6 +11,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
use app_units::Au;
use custom_properties::CustomPropertiesMap;
use gecko_bindings::bindings;
% for style_struct in data.style_structs:
use gecko_bindings::structs::${style_struct.gecko_ffi_name};
@ -53,13 +54,17 @@ use gecko::values::round_border_to_device_pixels;
use logical_geometry::WritingMode;
use media_queries::Device;
use properties::animated_properties::TransitionProperty;
use properties::{longhands, ComputedValues, LonghandId, PropertyDeclarationId};
use properties::computed_value_flags::ComputedValueFlags;
use properties::{longhands, FontComputationData, Importance, LonghandId};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
use rule_tree::StrongRuleNode;
use std::fmt::{self, Debug};
use std::mem::{forget, transmute, zeroed};
use std::ptr;
use stylearc::Arc;
use std::cmp;
use values::{Auto, CustomIdent, Either, KeyframesName};
use values::computed::ToComputedValue;
use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
use values::specified::length::Percentage;
use computed_values::border_style;
@ -70,6 +75,158 @@ pub mod style_structs {
% endfor
}
// FIXME(emilio): Unify both definitions, since they're equal now.
#[derive(Clone)]
pub struct ComputedValues {
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
custom_properties: Option<Arc<CustomPropertiesMap>>,
pub writing_mode: WritingMode,
pub font_computation_data: FontComputationData,
pub flags: ComputedValueFlags,
/// The rule node representing the ordered list of rules matched for this
/// node. Can be None for default values and text nodes. This is
/// essentially an optimization to avoid referencing the root rule node.
pub rules: Option<StrongRuleNode>,
/// The element's computed values if visited, only computed if there's a
/// relevant link for this element. A element's "relevant link" is the
/// element being matched if it is a link or the nearest ancestor link.
visited_style: Option<Arc<ComputedValues>>,
}
impl ComputedValues {
pub fn new(custom_properties: Option<Arc<CustomPropertiesMap>>,
writing_mode: WritingMode,
font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
flags: ComputedValueFlags,
rules: Option<StrongRuleNode>,
visited_style: Option<Arc<ComputedValues>>,
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
) -> Self {
ComputedValues {
custom_properties,
writing_mode,
font_computation_data: FontComputationData::new(font_size_keyword),
flags,
rules,
visited_style: visited_style,
% for style_struct in data.style_structs:
${style_struct.ident},
% endfor
}
}
pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc<Self> {
Arc::new(ComputedValues {
custom_properties: None,
writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious
font_computation_data: FontComputationData::default_values(),
flags: ComputedValueFlags::initial(),
rules: None,
visited_style: None,
% for style_struct in data.style_structs:
${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context),
% endfor
})
}
#[inline]
pub fn is_display_contents(&self) -> bool {
self.get_box().clone_display() == longhands::display::computed_value::T::contents
}
/// Returns true if the value of the `content` property would make a
/// pseudo-element not rendered.
#[inline]
pub fn ineffective_content_property(&self) -> bool {
self.get_counters().ineffective_content_property()
}
% for style_struct in data.style_structs:
#[inline]
pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
self.${style_struct.ident}.clone()
}
#[inline]
pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
&self.${style_struct.ident}
}
pub fn ${style_struct.name_lower}_arc(&self) -> &Arc<style_structs::${style_struct.name}> {
&self.${style_struct.ident}
}
#[inline]
pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
Arc::make_mut(&mut self.${style_struct.ident})
}
% endfor
/// Gets a reference to the rule node. Panic if no rule node exists.
pub fn rules(&self) -> &StrongRuleNode {
self.rules.as_ref().unwrap()
}
/// Gets a reference to the visited style, if any.
pub fn get_visited_style(&self) -> Option<<&Arc<ComputedValues>> {
self.visited_style.as_ref()
}
/// Gets a reference to the visited style. Panic if no visited style exists.
pub fn visited_style(&self) -> &Arc<ComputedValues> {
self.get_visited_style().unwrap()
}
/// Clone the visited style. Used for inheriting parent styles in
/// StyleBuilder::for_inheritance.
pub fn clone_visited_style(&self) -> Option<Arc<ComputedValues>> {
self.visited_style.clone()
}
pub fn custom_properties(&self) -> Option<Arc<CustomPropertiesMap>> {
self.custom_properties.clone()
}
#[allow(non_snake_case)]
pub fn has_moz_binding(&self) -> bool {
!self.get_box().gecko.mBinding.mPtr.mRawPtr.is_null()
}
// FIXME(bholley): Implement this properly.
#[inline]
pub fn is_multicol(&self) -> bool { false }
pub fn to_declaration_block(&self, property: PropertyDeclarationId) -> PropertyDeclarationBlock {
match property {
% for prop in data.longhands:
% if prop.animatable:
PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}) => {
PropertyDeclarationBlock::with_one(
PropertyDeclaration::${prop.camel_case}(
% if prop.boxed:
Box::new(
% endif
longhands::${prop.ident}::SpecifiedValue::from_computed_value(
&self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
% if prop.boxed:
)
% endif
),
Importance::Normal
)
},
% endif
% endfor
PropertyDeclarationId::Custom(_name) => unimplemented!(),
_ => unimplemented!()
}
}
}
<%def name="declare_style_struct(style_struct)">
pub struct ${style_struct.gecko_struct_name} {
gecko: ${style_struct.gecko_ffi_name},

View File

@ -701,7 +701,7 @@
'gecko_constant_prefix', 'gecko_enum_prefix',
'extra_gecko_values', 'extra_servo_values',
'aliases', 'extra_gecko_aliases', 'extra_servo_aliases',
'custom_consts', 'gecko_inexhaustive',
'custom_consts', 'gecko_inexhaustive', 'gecko_strip_moz_prefix',
]}
%>

View File

@ -17,11 +17,13 @@ ${helpers.single_keyword("ime-mode", "auto normal active disabled inactive",
spec="https://drafts.csswg.org/css-ui/#input-method-editor")}
${helpers.single_keyword("-moz-user-select", "auto text none all element elements" +
" toggle tri-state -moz-all -moz-none -moz-text",
" toggle tri-state -moz-all -moz-text",
products="gecko",
alias="-webkit-user-select",
gecko_ffi_name="mUserSelect",
gecko_enum_prefix="StyleUserSelect",
gecko_strip_moz_prefix=False,
aliases="-moz-none=none",
animation_value_type="none",
spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select")}

View File

@ -1807,6 +1807,9 @@ pub mod style_structs {
% endfor
#[cfg(feature = "gecko")]
pub use gecko_properties::ComputedValues;
/// A legacy alias for a servo-version of ComputedValues. Should go away soon.
#[cfg(feature = "servo")]
pub type ServoComputedValues = ComputedValues;
@ -1817,7 +1820,8 @@ pub type ServoComputedValues = ComputedValues;
/// every kind of style struct.
///
/// When needed, the structs may be copied in order to get mutated.
#[derive(Clone)]
#[cfg(feature = "servo")]
#[cfg_attr(feature = "servo", derive(Clone))]
pub struct ComputedValues {
% for style_struct in data.active_style_structs():
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
@ -1842,6 +1846,7 @@ pub struct ComputedValues {
visited_style: Option<Arc<ComputedValues>>,
}
#[cfg(feature = "servo")]
impl ComputedValues {
/// Construct a `ComputedValues` instance.
pub fn new(
@ -1869,6 +1874,9 @@ impl ComputedValues {
}
}
/// Get the initial computed values.
pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
% for style_struct in data.active_style_structs():
/// Clone the ${style_struct.name} struct.
#[inline]
@ -1895,30 +1903,6 @@ impl ComputedValues {
}
% endfor
/// Get the initial computed values.
#[cfg(feature = "servo")]
pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
/// Get the default computed values for a given document.
///
/// This takes into account zoom, etc.
#[cfg(feature = "gecko")]
pub fn default_values(
pres_context: bindings::RawGeckoPresContextBorrowed
) -> Arc<Self> {
Arc::new(ComputedValues {
custom_properties: None,
writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious
font_computation_data: FontComputationData::default_values(),
flags: ComputedValueFlags::initial(),
rules: None,
visited_style: None,
% for style_struct in data.style_structs:
${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context),
% endfor
})
}
/// Gets a reference to the rule node. Panic if no rule node exists.
pub fn rules(&self) -> &StrongRuleNode {
self.rules.as_ref().unwrap()
@ -1955,73 +1939,19 @@ impl ComputedValues {
self.custom_properties.clone()
}
/// Get a declaration block representing the computed value for a given
/// property.
///
/// Currently only implemented for animated properties.
pub fn to_declaration_block(
&self,
property: PropertyDeclarationId
) -> PropertyDeclarationBlock {
use values::computed::ToComputedValue;
match property {
% for prop in data.longhands:
% if prop.animatable:
PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}) => {
PropertyDeclarationBlock::with_one(
PropertyDeclaration::${prop.camel_case}(
% if prop.boxed:
Box::new(
% endif
longhands::${prop.ident}::SpecifiedValue::from_computed_value(
&self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
% if prop.boxed:
)
% endif
),
Importance::Normal
)
},
% endif
% endfor
PropertyDeclarationId::Custom(_name) => unimplemented!(),
_ => unimplemented!()
}
}
/// Whether this style has a -moz-binding value. This is always false for
/// Servo for obvious reasons.
#[inline]
#[cfg(feature = "servo")]
pub fn has_moz_binding(&self) -> bool { false }
/// Whether this style has a -moz-binding value.
#[inline]
#[cfg(feature = "gecko")]
pub fn has_moz_binding(&self) -> bool {
!self.get_box().gecko().mBinding.mPtr.mRawPtr.is_null()
}
/// Returns whether this style's display value is equal to contents.
///
/// Since this isn't supported in Servo, this is always false for Servo.
#[inline]
#[cfg(feature = "servo")]
pub fn is_display_contents(&self) -> bool { false }
/// Returns whether this style's display value is equal to contents.
#[inline]
#[cfg(feature = "gecko")]
pub fn is_display_contents(&self) -> bool {
self.get_box().clone_display() == longhands::display::computed_value::T::contents
}
/// Returns whether the "content" property for the given style is completely
/// ineffective, and would yield an empty `::before` or `::after`
/// pseudo-element.
#[inline]
#[cfg(feature = "servo")]
pub fn ineffective_content_property(&self) -> bool {
use properties::longhands::content::computed_value::T;
match self.get_counters().content {
@ -2030,23 +1960,8 @@ impl ComputedValues {
}
}
/// Returns true if the value of the `content` property would make a
/// pseudo-element not rendered.
#[inline]
#[cfg(feature = "gecko")]
pub fn ineffective_content_property(&self) -> bool {
self.get_counters().ineffective_content_property()
}
#[inline]
#[cfg(feature = "gecko")]
/// Whether the current style is multicolumn.
/// FIXME(bholley): Implement this properly.
pub fn is_multicol(&self) -> bool { false }
#[inline]
#[cfg(feature = "servo")]
/// Whether the current style is multicolumn.
pub fn is_multicol(&self) -> bool {
let style = self.get_column();
match style.column_width {
@ -2057,10 +1972,7 @@ impl ComputedValues {
}
}
}
}
#[cfg(feature = "servo")]
impl ComputedValues {
/// Resolves the currentColor keyword.
///
/// Any color value from computed values (except for the 'color' property

View File

@ -1408,16 +1408,44 @@ impl Drop for StrongRuleNode {
let free_list = &root.next_free;
let mut old_head = free_list.load(Ordering::Relaxed);
// If the free list is null, that means the last GC has already occurred.
// We require that any callers freeing at this point are on the main
// thread, and we drop the rule node synchronously.
// If the free list is null, that means that the rule tree has been
// formally torn down, and the last standard GC has already occurred.
// We require that any callers using the rule tree at this point are
// on the main thread only, which lets us trigger a synchronous GC
// here to avoid leaking anything. We use the GC machinery, rather
// than just dropping directly, so that we benefit from the iterative
// destruction and don't trigger unbounded recursion during drop. See
// [1] and the associated crashtest.
//
// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=439184
if old_head.is_null() {
debug_assert!(!thread_state::get().is_worker() &&
(thread_state::get().is_layout() ||
thread_state::get().is_script()));
unsafe { node.remove_from_child_list(); }
log_drop(self.ptr());
let _ = unsafe { Box::from_raw(self.ptr()) };
// Add the node as the sole entry in the free list.
debug_assert!(node.next_free.load(Ordering::Relaxed).is_null());
node.next_free.store(FREE_LIST_SENTINEL, Ordering::Relaxed);
free_list.store(node as *const _ as *mut _, Ordering::Relaxed);
// Invoke the GC.
//
// Note that we need hold a strong reference to the root so that it
// doesn't go away during the GC (which would happen if we're freeing
// the last external reference into the rule tree). This is nicely
// enforced by having the gc() method live on StrongRuleNode rather than
// RuleNode.
let strong_root: StrongRuleNode = node.root.as_ref().unwrap().upgrade();
unsafe { strong_root.gc(); }
// Leave the free list null, like we found it, such that additional
// drops for straggling rule nodes will take this same codepath.
debug_assert_eq!(root.next_free.load(Ordering::Relaxed),
FREE_LIST_SENTINEL);
root.next_free.store(ptr::null_mut(), Ordering::Relaxed);
// Return. If strong_root is the last strong reference to the root,
// this re-enter StrongRuleNode::drop, and take the root-dropping
// path earlier in this function.
return;
}

View File

@ -7,6 +7,7 @@ use cssparser::{Parser, ParserInput};
use cssparser::ToCss as ParserToCss;
use env_logger::LogBuilder;
use selectors::Element;
use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
use std::env;
use std::fmt::Write;
use std::ptr;
@ -1255,6 +1256,47 @@ pub extern "C" fn Servo_StyleRule_GetSpecificityAtIndex(
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(rule: RawServoStyleRuleBorrowed,
element: RawGeckoElementBorrowed,
index: u32,
pseudo_type: CSSPseudoElementType) -> bool {
read_locked_arc(rule, |rule: &StyleRule| {
let index = index as usize;
if index >= rule.selectors.0.len() {
return false;
}
let selector_and_hashes = &rule.selectors.0[index];
let mut matching_mode = MatchingMode::Normal;
match PseudoElement::from_pseudo_type(pseudo_type) {
Some(pseudo) => {
// We need to make sure that the requested pseudo element type
// matches the selector pseudo element type before proceeding.
match selector_and_hashes.selector.pseudo_element() {
Some(selector_pseudo) if *selector_pseudo == pseudo => {
matching_mode = MatchingMode::ForStatelessPseudoElement
},
_ => return false,
};
},
None => {
// Do not attempt to match if a pseudo element is requested and
// this is not a pseudo element selector, or vice versa.
if selector_and_hashes.selector.has_pseudo_element() {
return false;
}
},
};
let element = GeckoElement(element);
let mut ctx = MatchingContext::new(matching_mode, None, element.owner_document_quirks_mode());
matches_selector(&selector_and_hashes.selector, 0, &selector_and_hashes.hashes,
&element, &mut ctx, &mut |_, _| {})
})
}
#[no_mangle]
pub extern "C" fn Servo_ImportRule_GetHref(rule: RawServoImportRuleBorrowed, result: *mut nsAString) {
read_locked_arc(rule, |rule: &ImportRule| {

View File

@ -20,8 +20,9 @@ fn test_moz_user_select() {
assert_roundtrip_with_context!(_moz_user_select::parse, "toggle");
assert_roundtrip_with_context!(_moz_user_select::parse, "tri-state");
assert_roundtrip_with_context!(_moz_user_select::parse, "-moz-all");
assert_roundtrip_with_context!(_moz_user_select::parse, "-moz-none");
assert_roundtrip_with_context!(_moz_user_select::parse, "-moz-text");
assert_eq!(parse(_moz_user_select::parse, "-moz-none"),
Ok(_moz_user_select::SpecifiedValue::none));
assert!(parse(_moz_user_select::parse, "potato").is_err());
}

View File

@ -1 +1 @@
0.4.6
0.4.7

View File

@ -206,6 +206,7 @@ mac64 = "x86_64-apple-darwin"
mac32 = "i686-apple-darwin"
win64 = "x86_64-pc-windows-msvc"
win32 = "i686-pc-windows-msvc"
mingw32 = "i686-pc-windows-gnu"
def args():
@ -234,3 +235,4 @@ if __name__ == '__main__':
repack(linux64, [linux64, mac64], suffix='mac-cross', **args)
repack(linux64, [linux64, android, android_x86, android_aarch64],
suffix='android-cross', **args)
repack(linux64, [linux64, win32, mingw32], suffix='mingw32-cross', **args)

View File

@ -72,6 +72,9 @@ TARGETS = {
'mobile/android/config/tooltool-manifests/android-x86/releng.manifest',
'mobile/android/config/tooltool-manifests/android-gradle-dependencies/releng.manifest',
],
'x86_64-unknown-linux-gnu-mingw32-cross-repack': [
'browser/config/tooltool-manifests/mingw32/releng.manifest',
],
'x86_64-unknown-linux-gnu-mac-cross-repack': [
'browser/config/tooltool-manifests/macosx64/cross-releng.manifest',
],

View File

@ -82,10 +82,10 @@
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "cart", "tsvg_static"]
},
"perf-reftest": {
"tests": ["bloom_basic", "bloom_basic_ref"]
"tests": ["bloom_basic"]
},
"perf-reftest-e10s": {
"tests": ["bloom_basic", "bloom_basic_ref"]
"tests": ["bloom_basic"]
},
"tp5o": {
"tests": ["tp5o"],

View File

@ -42,6 +42,7 @@ DEFAULTS = dict(
firstpaint=False,
userready=False,
testeventmap=[],
base_vs_ref=False,
tpdisable_e10s=False,
tpnoisy=True,
tppagecycles=1,

View File

@ -54,8 +54,7 @@ class Output(object):
vals = []
replicates = {}
# TODO: counters!!!! we don't have any, but they suffer the
# same
# TODO: counters!!!! we don't have any, but they suffer the same
for result in test.results:
# XXX this will not work for manifests which list
# the same page name twice. It also ignores cycles
@ -88,6 +87,14 @@ class Output(object):
'value': val['filtered'],
'replicates': replicates[page],
}
# if results are from a comparison test i.e. perf-reftest, it will also
# contain replicates for 'base' and 'reference'; we wish to keep those
# to reference; actual results were calculated as the difference of those
base_runs = result.results[0].get('base_runs', None)
ref_runs = result.results[0].get('ref_runs', None)
if base_runs and ref_runs:
subtest['base_replicates'] = base_runs
subtest['ref_replicates'] = ref_runs
subtests.append(subtest)
if test.test_config.get('lower_is_better') is not None:
subtest['lowerIsBetter'] = \

View File

@ -93,7 +93,6 @@ def run_tests(config, browser_config):
tests = useBaseTestDefaults(config.get('basetest', {}), tests)
paths = ['profile_path', 'tpmanifest', 'extensions', 'setup', 'cleanup']
for test in tests:
# Check for profile_path, tpmanifest and interpolate based on Talos
# root https://bugzilla.mozilla.org/show_bug.cgi?id=727711
# Build command line from config
@ -255,10 +254,18 @@ def run_tests(config, browser_config):
# now we have three separate test results, store them
for test_result in separate_results_list:
talos_results.add(test_result)
# some tests like bloom_basic run two separate tests and then compare those values
# we want the results in perfherder to only be the actual difference between those
# and store the base and reference test replicates in results.json for upload
elif test.get('base_vs_ref', False):
# run the test, results will be reported for each page like two tests in the suite
base_and_reference_results = mytest.runTest(browser_config, test)
# now compare each test, and create a new test object for the comparison
talos_results.add(make_comparison_result(base_and_reference_results))
else:
# just expecting regular test - one result value per iteration
talos_results.add(mytest.runTest(browser_config, test))
LOG.test_end(testname, status='OK')
except TalosRegression as exc:
@ -298,6 +305,56 @@ def run_tests(config, browser_config):
return 0
def make_comparison_result(base_and_reference_results):
''' Receive a test result object meant to be used as a base vs reference test. The result
object will have one test with two subtests; instead of traditional subtests we want to
treat them as separate tests, comparing them together and reporting the comparison results.
Results with multiple pages used as subtests would look like this normally, with the overall
result value being the mean of the pages/subtests:
PERFHERDER_DATA: {"framework": {"name": "talos"}, "suites": [{"extraOptions": ["e10s"],
"name": "bloom_basic", "lowerIsBetter": true, "alertThreshold": 5.0, "value": 594.81,
"subtests": [{"name": ".html", "lowerIsBetter": true, "alertThreshold": 5.0, "replicates":
[586.52, ...], "value": 586.52], "unit": "ms"}, {"name": "-ref.html", "lowerIsBetter": true,
"alertThreshold": 5.0, "replicates": [603.225, ...], "value": 603.225, "unit": "ms"}]}]}
We want to compare the subtests against eachother (base vs ref) and create a new single test
results object with the comparison results, that will look like traditional single test results
like this:
PERFHERDER_DATA: {"framework": {"name": "talos"}, "suites": [{"lowerIsBetter": true,
"subtests": [{"name": "", "lowerIsBetter": true, "alertThreshold": 5.0, "replicates":
[16.705, ...], "value": 16.705, "unit": "ms"}], "extraOptions": ["e10s"], "name":
"bloom_basic", "alertThreshold": 5.0}]}
'''
# separate the 'base' and 'reference' result run values
base_result_runs = base_and_reference_results.results[0].results[0]['runs']
ref_result_runs = base_and_reference_results.results[0].results[1]['runs']
# create a new results object for the comparison result; keep replicates from both pages
comparison_result = copy.deepcopy(base_and_reference_results)
# remove original results from our copy as they will be replaced by one comparison result
comparison_result.results[0].results = []
# populate our new comparison result with 'base' and 'ref' replicates
comparison_result.results[0].results.append({'index': 0,
'runs': [],
'page': '',
'base_runs': base_result_runs,
'ref_runs': ref_result_runs})
# now step thru each result, compare 'base' vs 'ref', and store the difference in 'runs'
_index = 0
for next_ref in comparison_result.results[0].results[0]['ref_runs']:
diff = abs(next_ref - comparison_result.results[0].results[0]['base_runs'][_index])
comparison_result.results[0].results[0]['runs'].append(round(diff, 3))
_index += 1
return comparison_result
def convert_to_separate_test_results(multi_value_result, test_event_map):
''' Receive a test result that actually contains multiple values in a single iteration, and
parse it out in order to 'fake' three seprate test results.

View File

@ -107,6 +107,7 @@ class TsBase(Test):
'firstpaint',
'userready',
'testeventmap',
'base_vs_ref',
'extensions',
'filters',
'setup',
@ -251,7 +252,7 @@ class PageloaderTest(Test):
timeout = None
keys = ['tpmanifest', 'tpcycles', 'tppagecycles', 'tprender', 'tpchrome',
'tpmozafterpaint', 'tploadnocache', 'firstpaint', 'userready',
'testeventmap', 'rss', 'mainthread', 'resolution', 'cycles',
'testeventmap', 'base_vs_ref', 'rss', 'mainthread', 'resolution', 'cycles',
'gecko_profile', 'gecko_profile_interval', 'gecko_profile_entries',
'tptimeout', 'win_counters', 'w7_counters', 'linux_counters', 'mac_counters',
'tpscrolltest', 'xperf_counters', 'timeout', 'shutdown', 'responsiveness',
@ -801,8 +802,9 @@ class a11yr(PageloaderTest):
@register_test()
class bloom_basic(PageloaderTest):
"""
Stylo bloom_basic test
Stylo bloom_basic: runs bloom_basic and bloom_basic_ref and reports difference
"""
base_vs_ref = True # compare the two test pages with eachother and report comparison
tpmanifest = '${talos}/tests/perf-reftest/bloom_basic.manifest'
tpcycles = 1
tppagecycles = 25
@ -814,22 +816,6 @@ class bloom_basic(PageloaderTest):
alert_threshold = 5.0
@register_test()
class bloom_basic_ref(PageloaderTest):
"""
Stylo bloom_basic_ref test
"""
tpmanifest = '${talos}/tests/perf-reftest/bloom_basic_ref.manifest'
tpcycles = 1
tppagecycles = 25
gecko_profile_interval = 1
gecko_profile_entries = 2000000
filters = filter.ignore_first.prepare(5) + filter.median.prepare()
unit = 'ms'
lower_is_better = True
alert_threshold = 5.0
@register_test()
class quantum_pageload_google(QuantumPageloadTest):
"""

View File

@ -1 +1,4 @@
# base_vs_ref is set in test.py for this test, so each of these pages are run as separate
# tests, but then compared against eachother; and the reported results are the comparison
% http://localhost/tests/perf-reftest/bloom-basic.html
% http://localhost/tests/perf-reftest/bloom-basic-ref.html

View File

@ -232,7 +232,7 @@ var Bookmarks = Object.freeze({
// If it's a tag, notify OnItemChanged to all bookmarks for this URL.
if (isTagging) {
for (let entry of (await fetchBookmarksByURL(item))) {
for (let entry of (await fetchBookmarksByURL(item, true))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,
@ -642,6 +642,7 @@ var Bookmarks = Object.freeze({
updatedItem.source ]);
}
if (updateInfo.hasOwnProperty("title")) {
let isTagging = updatedItem.parentGuid == Bookmarks.tagsGuid;
notify(observers, "onItemChanged", [ updatedItem._id, "title",
false, updatedItem.title,
PlacesUtils.toPRTime(updatedItem.lastModified),
@ -649,7 +650,22 @@ var Bookmarks = Object.freeze({
updatedItem._parentId,
updatedItem.guid,
updatedItem.parentGuid, "",
updatedItem.source ]);
updatedItem.source ],
{ isTagging });
// If we're updating a tag, we must notify all the tagged bookmarks
// about the change.
if (isTagging) {
let URIs = PlacesUtils.tagging.getURIsForTag(updatedItem.title);
for (let uri of URIs) {
for (let entry of (await fetchBookmarksByURL({ url: new URL(uri.spec) }, true))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,
entry.guid, entry.parentGuid,
"", updatedItem.source ]);
}
}
}
}
if (updateInfo.hasOwnProperty("url")) {
notify(observers, "onItemChanged", [ updatedItem._id, "uri",
@ -738,7 +754,7 @@ var Bookmarks = Object.freeze({
{ isTagging: isUntagging });
if (isUntagging) {
for (let entry of (await fetchBookmarksByURL(item))) {
for (let entry of (await fetchBookmarksByURL(item, true))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,
@ -2224,7 +2240,7 @@ async function(db, folderGuids, options) {
let isUntagging = item._grandParentId == PlacesUtils.tagsFolderId;
if (isUntagging) {
for (let entry of (await fetchBookmarksByURL(item))) {
for (let entry of (await fetchBookmarksByURL(item, true))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,

View File

@ -1420,9 +1420,11 @@ function tagItem(item, tags) {
// tag IDs, we temporarily tag a dummy URI, ensuring the tags exist.
let dummyURI = PlacesUtils.toURI("about:weave#BStore_tagURI");
let bookmarkURI = PlacesUtils.toURI(item.url.href);
PlacesUtils.tagging.tagURI(dummyURI, newTags, SOURCE_SYNC);
if (newTags && newTags.length > 0)
PlacesUtils.tagging.tagURI(dummyURI, newTags, SOURCE_SYNC);
PlacesUtils.tagging.untagURI(bookmarkURI, null, SOURCE_SYNC);
PlacesUtils.tagging.tagURI(bookmarkURI, newTags, SOURCE_SYNC);
if (newTags && newTags.length > 0)
PlacesUtils.tagging.tagURI(bookmarkURI, newTags, SOURCE_SYNC);
PlacesUtils.tagging.untagURI(dummyURI, null, SOURCE_SYNC);
return newTags;

View File

@ -1270,7 +1270,7 @@ PT.EditUrl.prototype = Object.seal({
PlacesUtils.tagging.untagURI(originalURI, originalTags);
let currentNewURITags = PlacesUtils.tagging.getTagsForURI(uri);
newURIAdditionalTags = originalTags.filter(t => !currentNewURITags.includes(t));
if (newURIAdditionalTags)
if (newURIAdditionalTags && newURIAdditionalTags.length > 0)
PlacesUtils.tagging.tagURI(uri, newURIAdditionalTags);
}
}
@ -1559,12 +1559,18 @@ PT.Untag.prototype = {
} else {
tagsToRemove = tagsSet;
}
PlacesUtils.tagging.untagURI(uri, tagsToRemove);
if (tagsToRemove.length > 0) {
PlacesUtils.tagging.untagURI(uri, tagsToRemove);
}
onUndo.unshift(() => {
PlacesUtils.tagging.tagURI(uri, tagsToRemove);
if (tagsToRemove.length > 0) {
PlacesUtils.tagging.tagURI(uri, tagsToRemove);
}
});
onRedo.push(() => {
PlacesUtils.tagging.untagURI(uri, tagsToRemove);
if (tagsToRemove.length > 0) {
PlacesUtils.tagging.untagURI(uri, tagsToRemove);
}
});
}
this.undo = async function() {

View File

@ -1987,19 +1987,19 @@ nsNavBookmarks::SetItemTitle(int64_t aItemId, const nsACString& aTitle,
NS_ENSURE_SUCCESS(rv, rv);
}
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
OnItemChanged(bookmark.id,
NS_LITERAL_CSTRING("title"),
false,
title,
bookmark.lastModified,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
EmptyCString(),
aSource));
NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
SKIP_TAGS(isChangingTagFolder),
OnItemChanged(bookmark.id,
NS_LITERAL_CSTRING("title"),
false,
title,
bookmark.lastModified,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
EmptyCString(),
aSource));
return NS_OK;
}

View File

@ -128,7 +128,7 @@ TaggingService.prototype = {
// have created it.
tag.__defineGetter__("id", () => this._getItemIdForTag(tag.name));
} else {
throw Cr.NS_ERROR_INVALID_ARG;
throw Components.Exception("Invalid tag value", Cr.NS_ERROR_INVALID_ARG);
}
return tag;
});
@ -136,8 +136,8 @@ TaggingService.prototype = {
// nsITaggingService
tagURI: function TS_tagURI(aURI, aTags, aSource) {
if (!aURI || !aTags || !Array.isArray(aTags)) {
throw Cr.NS_ERROR_INVALID_ARG;
if (!aURI || !aTags || !Array.isArray(aTags) || aTags.length == 0) {
throw Components.Exception("Invalid value for tags", Cr.NS_ERROR_INVALID_ARG);
}
// This also does some input validation.
@ -215,8 +215,8 @@ TaggingService.prototype = {
// nsITaggingService
untagURI: function TS_untagURI(aURI, aTags, aSource) {
if (!aURI || (aTags && !Array.isArray(aTags))) {
throw Cr.NS_ERROR_INVALID_ARG;
if (!aURI || (aTags && (!Array.isArray(aTags) || aTags.length == 0))) {
throw Components.Exception("Invalid value for tags", Cr.NS_ERROR_INVALID_ARG);
}
if (!aTags) {
@ -257,8 +257,9 @@ TaggingService.prototype = {
// nsITaggingService
getURIsForTag: function TS_getURIsForTag(aTagName) {
if (!aTagName || aTagName.length == 0)
throw Cr.NS_ERROR_INVALID_ARG;
if (!aTagName || aTagName.length == 0) {
throw Components.Exception("Invalid tag name", Cr.NS_ERROR_INVALID_ARG);
}
if (/^\s|\s$/.test(aTagName)) {
Deprecated.warning("Tag passed to getURIsForTag was not trimmed",
@ -293,8 +294,9 @@ TaggingService.prototype = {
// nsITaggingService
getTagsForURI: function TS_getTagsForURI(aURI, aCount) {
if (!aURI)
throw Cr.NS_ERROR_INVALID_ARG;
if (!aURI) {
throw Components.Exception("Invalid uri", Cr.NS_ERROR_INVALID_ARG);
}
let tags = [];
let db = PlacesUtils.history.DBConnection;

View File

@ -325,4 +325,29 @@ this.PlacesTestUtils = Object.freeze({
dateRemoved: PlacesUtils.toDate(row.getResultByName("dateRemoved")),
}));
},
waitForNotification(notification, conditionFn = () => true, type = "bookmarks") {
let iface = type == "bookmarks" ? Ci.nsINavBookmarkObserver
: Ci.nsINavHistoryObserver;
return new Promise(resolve => {
let proxifiedObserver = new Proxy({}, {
get: (target, name) => {
if (name == "QueryInterface")
return XPCOMUtils.generateQI([iface]);
if (name == notification)
return (...args) => {
if (conditionFn.apply(this, args)) {
PlacesUtils[type].removeObserver(proxifiedObserver);
resolve();
}
}
if (name == "skipTags" || name == "skipDescendantsOnItemRemoval") {
return false;
}
return () => false;
}
});
PlacesUtils[type].addObserver(proxifiedObserver);
});
},
});

View File

@ -5,115 +5,83 @@
* events like visit or favicon additions. */
const NOW = Date.now() * 1000;
let gBookmarkGuids = [];
var observer = {
bookmarks: [],
observedBookmarks: 0,
observedVisitId: 0,
deferred: null,
/**
* Returns a promise that is resolved when the observer determines that the
* test can continue. This is required rather than calling run_next_test
* directly in the observer because there are cases where we must wait for
* other asynchronous events to be completed in addition to this.
*/
setupCompletionPromise() {
this.observedBookmarks = 0;
this.deferred = Promise.defer();
return this.deferred.promise;
},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemAdded() {},
onItemRemoved() {},
onItemMoved() {},
onItemChanged(aItemId, aProperty, aIsAnnotation, aNewValue,
aLastModified, aItemType) {
do_print("Check that we got the correct change information.");
do_check_neq(this.bookmarks.indexOf(aItemId), -1);
if (aProperty == "favicon") {
do_check_false(aIsAnnotation);
do_check_eq(aNewValue, SMALLPNG_DATA_URI.spec);
do_check_eq(aLastModified, 0);
do_check_eq(aItemType, PlacesUtils.bookmarks.TYPE_BOOKMARK);
} else if (aProperty == "cleartime") {
do_check_false(aIsAnnotation);
do_check_eq(aNewValue, "");
do_check_eq(aLastModified, 0);
do_check_eq(aItemType, PlacesUtils.bookmarks.TYPE_BOOKMARK);
} else {
do_throw("Unexpected property change " + aProperty);
}
if (++this.observedBookmarks == this.bookmarks.length) {
this.deferred.resolve();
}
},
onItemVisited(aItemId, aVisitId, aTime) {
do_print("Check that we got the correct visit information.");
do_check_neq(this.bookmarks.indexOf(aItemId), -1);
this.observedVisitId = aVisitId;
do_check_eq(aTime, NOW);
if (++this.observedBookmarks == this.bookmarks.length) {
this.deferred.resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver,
])
};
PlacesUtils.bookmarks.addObserver(observer);
add_task(async function setup() {
// Add multiple bookmarks to the same uri.
gBookmarkGuids.push((await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: "http://book.ma.rk/"
})).guid);
gBookmarkGuids.push((await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
url: "http://book.ma.rk/"
})).guid);
Assert.equal(gBookmarkGuids.length, 2);
});
add_task(async function test_add_visit() {
let observerPromise = observer.setupCompletionPromise();
// Add a visit to the bookmark and wait for the observer.
let visitId;
await new Promise((resolve, reject) => {
PlacesUtils.asyncHistory.updatePlaces({
uri: NetUtil.newURI("http://book.ma.rk/"),
visits: [{ transitionType: TRANSITION_TYPED, visitDate: NOW }]
}, {
handleError: function TAV_handleError() {
reject(new Error("Unexpected error in adding visit."));
},
handleResult(aPlaceInfo) {
visitId = aPlaceInfo.visits[0].visitId;
},
handleCompletion: function TAV_handleCompletion() {
resolve();
}
});
// Wait for both the observer and the asynchronous update, in any order.
let guids = new Set(gBookmarkGuids);
Assert.equal(guids.size, 2);
let promiseNotifications = PlacesTestUtils.waitForNotification("onItemVisited",
(id, visitId, time, transition, uri, parentId, guid, parentGuid) => {
do_print(`Got a visit notification for ${guid}.`);
Assert.ok(visitId > 0);
guids.delete(guid);
return guids.size == 0;
});
await observerPromise;
// Check that both asynchronous results are consistent.
do_check_eq(observer.observedVisitId, visitId);
await PlacesTestUtils.addVisits({
uri: "http://book.ma.rk/",
transition: TRANSITION_TYPED,
visitDate: NOW
});
await promiseNotifications;
});
add_task(async function test_add_icon() {
let observerPromise = observer.setupCompletionPromise();
// Add a visit to the bookmark and wait for the observer.
let guids = new Set(gBookmarkGuids);
Assert.equal(guids.size, 2);
let promiseNotifications = PlacesTestUtils.waitForNotification("onItemChanged",
(id, property, isAnno, newValue, lastModified, itemType, parentId, guid) => {
do_print(`Got a changed notification for ${guid}.`);
Assert.equal(property, "favicon");
Assert.ok(!isAnno);
Assert.equal(newValue, SMALLPNG_DATA_URI.spec);
Assert.equal(lastModified, 0);
Assert.equal(itemType, PlacesUtils.bookmarks.TYPE_BOOKMARK);
guids.delete(guid);
return guids.size == 0;
});
PlacesUtils.favicons.setAndFetchFaviconForPage(NetUtil.newURI("http://book.ma.rk/"),
SMALLPNG_DATA_URI, true,
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
null,
Services.scriptSecurityManager.getSystemPrincipal());
await observerPromise;
await promiseNotifications;
});
add_task(async function test_remove_page() {
let observerPromise = observer.setupCompletionPromise();
await PlacesUtils.history.remove("http://book.ma.rk/");
await observerPromise;
});
// Add a visit to the bookmark and wait for the observer.
let guids = new Set(gBookmarkGuids);
Assert.equal(guids.size, 2);
let promiseNotifications = PlacesTestUtils.waitForNotification("onItemChanged",
(id, property, isAnno, newValue, lastModified, itemType, parentId, guid) => {
do_print(`Got a changed notification for ${guid}.`);
Assert.equal(property, "cleartime");
Assert.ok(!isAnno);
Assert.equal(newValue, "");
Assert.equal(lastModified, 0);
Assert.equal(itemType, PlacesUtils.bookmarks.TYPE_BOOKMARK);
guids.delete(guid);
return guids.size == 0;
});
add_task(function cleanup() {
PlacesUtils.bookmarks.removeObserver(observer, false);
await PlacesUtils.history.remove("http://book.ma.rk/");
await promiseNotifications;
});
add_task(async function shutdown() {
@ -128,7 +96,6 @@ add_task(async function shutdown() {
// 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");
@ -143,24 +110,9 @@ add_task(async function shutdown() {
});
}, "places-will-close-connection");
shutdownPlaces();
});
});
function run_test() {
// Add multiple bookmarks to the same uri.
observer.bookmarks.push(
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
NetUtil.newURI("http://book.ma.rk/"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"Bookmark")
);
observer.bookmarks.push(
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId,
NetUtil.newURI("http://book.ma.rk/"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"Bookmark")
);
run_next_test();
}

View File

@ -32,8 +32,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",

View File

@ -105,7 +105,7 @@ function Test() {
// This maps a state name to the number of times it's been observed.
this.stateCounts = {};
// Promise object resolved when the next test can be run.
this.deferNextTest = Promise.defer();
this.deferNextTest = PromiseUtils.defer();
}
Test.prototype = {

View File

@ -319,13 +319,7 @@ function checkItem(aExpected, aNode) {
do_check_eq(aNode.uri, aExpected.url)
break;
case "icon":
let deferred = Promise.defer();
PlacesUtils.favicons.getFaviconDataForPage(
NetUtil.newURI(aExpected.url),
function(aURI, aDataLen, aData, aMimeType) {
deferred.resolve(aData);
});
let data = await deferred.promise;
let {data} = await getFaviconDataForPage(aExpected.url);
let base64Icon = "data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
do_check_true(base64Icon == aExpected.icon);

View File

@ -186,13 +186,7 @@ async function checkItem(aExpected, aNode) {
do_check_eq(aNode.uri, aExpected.url);
break;
case "icon":
let deferred = Promise.defer();
PlacesUtils.favicons.getFaviconDataForPage(
NetUtil.newURI(aExpected.url),
function(aURI, aDataLen, aData, aMimeType) {
deferred.resolve(aData);
});
let data = await deferred.promise;
let {data} = await getFaviconDataForPage(aExpected.url);
let base64Icon = "data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
do_check_eq(base64Icon, aExpected.icon);

View File

@ -71,12 +71,12 @@ AutoCompleteInput.prototype = {
}
}
function ensure_results(uris, searchTerm) {
PlacesTestUtils.promiseAsyncUpdates()
.then(() => ensure_results_internal(uris, searchTerm));
async function ensure_results(uris, searchTerm) {
await PlacesTestUtils.promiseAsyncUpdates()
await ensure_results_internal(uris, searchTerm);
}
function ensure_results_internal(uris, searchTerm) {
async function ensure_results_internal(uris, searchTerm) {
var controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
getService(Components.interfaces.nsIAutoCompleteController);
@ -92,19 +92,22 @@ function ensure_results_internal(uris, searchTerm) {
do_check_eq(numSearchesStarted, 1);
};
input.onSearchComplete = function() {
do_check_eq(numSearchesStarted, 1);
do_check_eq(controller.searchStatus,
Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
do_check_eq(controller.matchCount, uris.length);
for (var i = 0; i < controller.matchCount; i++) {
do_check_eq(controller.getValueAt(i), uris[i].spec);
}
let promise = new Promise(resolve => {
input.onSearchComplete = function() {
do_check_eq(numSearchesStarted, 1);
do_check_eq(controller.searchStatus,
Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
do_check_eq(controller.matchCount, uris.length);
for (var i = 0; i < controller.matchCount; i++) {
do_check_eq(controller.getValueAt(i), uris[i].spec);
}
deferEnsureResults.resolve();
};
resolve();
};
});
controller.startSearch(searchTerm);
await promise;
}
// Get history service
@ -156,28 +159,28 @@ async function() {
await task_setCountDate(uri1, c1, d1);
await task_setCountDate(uri2, c1, d2);
tagURI(uri1, ["site"]);
ensure_results([uri1, uri2], "");
await ensure_results([uri1, uri2], "");
},
async function() {
print("TEST-INFO | Test 1: same count, different date");
await task_setCountDate(uri1, c1, d2);
await task_setCountDate(uri2, c1, d1);
tagURI(uri1, ["site"]);
ensure_results([uri2, uri1], "");
await ensure_results([uri2, uri1], "");
},
async function() {
print("TEST-INFO | Test 2: different count, same date");
await task_setCountDate(uri1, c1, d1);
await task_setCountDate(uri2, c2, d1);
tagURI(uri1, ["site"]);
ensure_results([uri1, uri2], "");
await ensure_results([uri1, uri2], "");
},
async function() {
print("TEST-INFO | Test 3: different count, same date");
await task_setCountDate(uri1, c2, d1);
await task_setCountDate(uri2, c1, d1);
tagURI(uri1, ["site"]);
ensure_results([uri2, uri1], "");
await ensure_results([uri2, uri1], "");
},
// test things with a search term
@ -186,81 +189,75 @@ async function() {
await task_setCountDate(uri1, c1, d1);
await task_setCountDate(uri2, c1, d2);
tagURI(uri1, ["site"]);
ensure_results([uri1, uri2], "site");
await ensure_results([uri1, uri2], "site");
},
async function() {
print("TEST-INFO | Test 5: same count, different date");
await task_setCountDate(uri1, c1, d2);
await task_setCountDate(uri2, c1, d1);
tagURI(uri1, ["site"]);
ensure_results([uri2, uri1], "site");
await ensure_results([uri2, uri1], "site");
},
async function() {
print("TEST-INFO | Test 6: different count, same date");
await task_setCountDate(uri1, c1, d1);
await task_setCountDate(uri2, c2, d1);
tagURI(uri1, ["site"]);
ensure_results([uri1, uri2], "site");
await ensure_results([uri1, uri2], "site");
},
async function() {
print("TEST-INFO | Test 7: different count, same date");
await task_setCountDate(uri1, c2, d1);
await task_setCountDate(uri2, c1, d1);
tagURI(uri1, ["site"]);
ensure_results([uri2, uri1], "site");
await ensure_results([uri2, uri1], "site");
},
// There are multiple tests for 8, hence the multiple functions
// Bug 426166 section
function() {
async function() {
print("TEST-INFO | Test 8.1a: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "a");
await ensure_results([uri4, uri3], "a");
},
function() {
async function() {
print("TEST-INFO | Test 8.1b: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "aa");
await ensure_results([uri4, uri3], "aa");
},
function() {
async function() {
print("TEST-INFO | Test 8.2: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "aaa");
await ensure_results([uri4, uri3], "aaa");
},
function() {
async function() {
print("TEST-INFO | Test 8.3: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "aaaa");
await ensure_results([uri4, uri3], "aaaa");
},
function() {
async function() {
print("TEST-INFO | Test 8.4: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "aaa");
await ensure_results([uri4, uri3], "aaa");
},
function() {
async function() {
print("TEST-INFO | Test 8.5: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "aa");
await ensure_results([uri4, uri3], "aa");
},
function() {
async function() {
print("TEST-INFO | Test 8.6: same count, same date");
setBookmark(uri3);
setBookmark(uri4);
ensure_results([uri4, uri3], "a");
await ensure_results([uri4, uri3], "a");
}
];
/**
* This deferred object contains a promise that is resolved when the
* ensure_results_internal function has finished its execution.
*/
var deferEnsureResults;
add_task(async function test_frecency() {
// Disable autoFill for this test.
Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
@ -276,9 +273,7 @@ add_task(async function test_frecency() {
await PlacesUtils.bookmarks.eraseEverything();
await PlacesTestUtils.clearHistory();
deferEnsureResults = Promise.defer();
await test();
await deferEnsureResults.promise;
}
for (let type of ["history", "bookmark", "openpage"]) {
prefs.clearUserPref("browser.urlbar.suggest." + type);

View File

@ -92,6 +92,10 @@ const SCHEDULER_TICK_IDLE_INTERVAL_MS = Preferences.get("toolkit.telemetry.sched
// The tolerance we have when checking if it's midnight (15 minutes).
const SCHEDULER_MIDNIGHT_TOLERANCE_MS = 15 * 60 * 1000;
// The maximum time (ms) until the tick should moved from the idle
// queue to the regular queue if it hasn't been executed yet.
const SCHEDULER_TICK_MAX_IDLE_DELAY_MS = 60 * 1000;
// Seconds of idle time before pinging.
// On idle-daily a gather-telemetry notification is fired, during it probes can
// start asynchronous tasks to gather data.
@ -414,23 +418,25 @@ var TelemetryScheduler = {
case "active":
// User is back to work, restore the original tick interval.
this._isUserIdle = false;
return this._onSchedulerTick();
return this._onSchedulerTick(true);
case "wake_notification":
// The machine woke up from sleep, trigger a tick to avoid sessions
// spanning more than a day.
// This is needed because sleep time does not count towards timeouts
// on Mac & Linux - see bug 1262386, bug 1204823 et al.
return this._onSchedulerTick();
return this._onSchedulerTick(true);
}
return undefined;
},
/**
* Performs a scheduler tick. This function manages Telemetry recurring operations.
* @param {Boolean} [dispatchOnIdle=false] If true, the tick is dispatched in the
* next idle cycle of the main thread.
* @return {Promise} A promise, only used when testing, resolved when the scheduled
* operation completes.
*/
_onSchedulerTick() {
_onSchedulerTick(dispatchOnIdle = false) {
// This call might not be triggered from a timeout. In that case we don't want to
// leave any previously scheduled timeouts pending.
this._clearTimeout();
@ -442,7 +448,13 @@ var TelemetryScheduler = {
let promise = Promise.resolve();
try {
promise = this._schedulerTickLogic();
if (dispatchOnIdle) {
promise = new Promise((resolve, reject) =>
Services.tm.idleDispatchToMainThread(() => this._schedulerTickLogic().then(resolve, reject)),
SCHEDULER_TICK_MAX_IDLE_DELAY_MS);
} else {
promise = this._schedulerTickLogic();
}
} catch (e) {
Telemetry.getHistogramById("TELEMETRY_SCHEDULER_TICK_EXCEPTION").add(1);
this._log.error("_onSchedulerTick - There was an exception", e);

View File

@ -1869,9 +1869,10 @@ add_task(async function test_schedulerEnvironmentReschedules() {
[PREF_TEST, {what: TelemetryEnvironment.RECORD_PREF_VALUE}],
]);
await TelemetryController.testReset();
await TelemetryController.testShutdown();
await TelemetryStorage.testClearPendingPings();
PingServer.clearRequests();
await TelemetryController.testReset();
// Set a fake current date and start Telemetry.
let nowDate = fakeNow(2060, 10, 18, 0, 0, 0);
@ -2029,7 +2030,7 @@ add_task(async function test_schedulerUserIdle() {
Assert.equal(schedulerTimeout, SCHEDULER_TICK_IDLE_INTERVAL_MS);
// Send an "active" notification to the scheduler.
fakeIdleNotification("active");
await fakeIdleNotification("active");
// When user is back active, the scheduler tick should be 5 minutes again.
Assert.equal(schedulerTimeout, SCHEDULER_TICK_INTERVAL_MS);

View File

@ -1225,7 +1225,12 @@ extends="chrome://global/content/bindings/popup.xml#popup">
<![CDATA[
let existingItemsCount = this.richlistbox.childNodes.length;
for (let i = this._matchCount; i < existingItemsCount; ++i) {
this.richlistbox.childNodes[i].collapsed = true;
let item = this.richlistbox.childNodes[i];
item.collapsed = true;
if (typeof item._onCollapse == "function") {
item._onCollapse();
}
}
]]>
</body>
@ -1344,7 +1349,8 @@ extends="chrome://global/content/bindings/popup.xml#popup">
// which has different structure of <content> and overrides
// _adjustAcItem().
reusable = originalType === style ||
(style !== "autofill-profile" && originalType !== "autofill-profile");
(style !== "autofill-profile" && originalType !== "autofill-profile" &&
style !== "autofill-footer" && originalType !== "autofill-footer");
} else {
// need to create a new item
item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "richlistitem");

View File

@ -685,12 +685,19 @@
<handlers>
<handler event="touchstart">
<![CDATA[
if (event.touches.length > 1) {
function isScrollbarElement(target) {
return (target.localName == "thumb" || target.localName == "slider")
&& target.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
}
if (event.touches.length > 1 || isScrollbarElement(event.touches[0].target)) {
// Multiple touch points detected, abort. In particular this aborts
// the panning gesture when the user puts a second finger down after
// already panning with one finger. Aborting at this point prevents
// the pan gesture from being resumed until all fingers are lifted
// (as opposed to when the user is back down to one finger).
// Additionally, if the user lands on the scrollbar don't use this
// code for scrolling, instead allow gecko to handle scrollbar
// interaction normally.
this._touchY = -1;
} else {
this._touchY = event.touches[0].screenY;