зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
14c2437eec
|
@ -358,7 +358,10 @@ MARKUPMAP(
|
|||
// display style other than 'table', then create a generic table cell
|
||||
// accessible, because there's no underlying table layout and thus native
|
||||
// HTML table cell class doesn't work.
|
||||
if (!aContext->IsHTMLTableRow()) {
|
||||
// The same is true if the cell itself has CSS display:block;.
|
||||
if (!aContext->IsHTMLTableRow() ||
|
||||
(aElement->GetPrimaryFrame() &&
|
||||
aElement->GetPrimaryFrame()->AccessibleType() != eHTMLTableCellType)) {
|
||||
return new ARIAGridCellAccessibleWrap(aElement, aContext->Document());
|
||||
}
|
||||
if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::scope)) {
|
||||
|
|
|
@ -90,11 +90,11 @@ ARIAGridAccessible::RowCount()
|
|||
Accessible*
|
||||
ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
|
||||
{
|
||||
Accessible* row = GetRowAt(aRowIndex);
|
||||
Accessible* row = RowAt(aRowIndex);
|
||||
if (!row)
|
||||
return nullptr;
|
||||
|
||||
return GetCellInRowAt(row, aColumnIndex);
|
||||
return CellInRowAt(row, aColumnIndex);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -110,7 +110,7 @@ ARIAGridAccessible::IsColSelected(uint32_t aColIdx)
|
|||
|
||||
do {
|
||||
if (!nsAccUtils::IsARIASelected(row)) {
|
||||
Accessible* cell = GetCellInRowAt(row, aColIdx);
|
||||
Accessible* cell = CellInRowAt(row, aColIdx);
|
||||
if (!cell || !nsAccUtils::IsARIASelected(cell))
|
||||
return false;
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx)
|
|||
if (IsARIARole(nsGkAtoms::table))
|
||||
return false;
|
||||
|
||||
Accessible* row = GetRowAt(aRowIdx);
|
||||
Accessible* row = RowAt(aRowIdx);
|
||||
if(!row)
|
||||
return false;
|
||||
|
||||
|
@ -147,12 +147,12 @@ ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
|
|||
if (IsARIARole(nsGkAtoms::table))
|
||||
return false;
|
||||
|
||||
Accessible* row = GetRowAt(aRowIdx);
|
||||
Accessible* row = RowAt(aRowIdx);
|
||||
if(!row)
|
||||
return false;
|
||||
|
||||
if (!nsAccUtils::IsARIASelected(row)) {
|
||||
Accessible* cell = GetCellInRowAt(row, aColIdx);
|
||||
Accessible* cell = CellInRowAt(row, aColIdx);
|
||||
if (!cell || !nsAccUtils::IsARIASelected(cell))
|
||||
return false;
|
||||
}
|
||||
|
@ -416,7 +416,7 @@ ARIAGridAccessible::SelectCol(uint32_t aColIdx)
|
|||
NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
|
||||
|
||||
// Select cell at the column index.
|
||||
Accessible* cell = GetCellInRowAt(row, aColIdx);
|
||||
Accessible* cell = CellInRowAt(row, aColIdx);
|
||||
if (cell)
|
||||
SetARIASelected(cell, true);
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ ARIAGridAccessible::UnselectRow(uint32_t aRowIdx)
|
|||
if (IsARIARole(nsGkAtoms::table))
|
||||
return;
|
||||
|
||||
Accessible* row = GetRowAt(aRowIdx);
|
||||
Accessible* row = RowAt(aRowIdx);
|
||||
if (row)
|
||||
SetARIASelected(row, false);
|
||||
}
|
||||
|
@ -443,7 +443,7 @@ ARIAGridAccessible::UnselectCol(uint32_t aColIdx)
|
|||
|
||||
Accessible* row = nullptr;
|
||||
while ((row = rowIter.Next())) {
|
||||
Accessible* cell = GetCellInRowAt(row, aColIdx);
|
||||
Accessible* cell = CellInRowAt(row, aColIdx);
|
||||
if (cell)
|
||||
SetARIASelected(cell, false);
|
||||
}
|
||||
|
@ -452,33 +452,6 @@ ARIAGridAccessible::UnselectCol(uint32_t aColIdx)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected
|
||||
|
||||
Accessible*
|
||||
ARIAGridAccessible::GetRowAt(int32_t aRow)
|
||||
{
|
||||
int32_t rowIdx = aRow;
|
||||
|
||||
AccIterator rowIter(this, filters::GetRow);
|
||||
|
||||
Accessible* row = rowIter.Next();
|
||||
while (rowIdx != 0 && (row = rowIter.Next()))
|
||||
rowIdx--;
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
ARIAGridAccessible::GetCellInRowAt(Accessible* aRow, int32_t aColumn)
|
||||
{
|
||||
int32_t colIdx = aColumn;
|
||||
|
||||
AccIterator cellIter(aRow, filters::GetCell);
|
||||
Accessible* cell = cellIter.Next();
|
||||
while (colIdx != 0 && (cell = cellIter.Next()))
|
||||
colIdx--;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
|
||||
bool aIsSelected, bool aNotify)
|
||||
|
|
|
@ -52,16 +52,6 @@ public:
|
|||
protected:
|
||||
virtual ~ARIAGridAccessible() {}
|
||||
|
||||
/**
|
||||
* Return row accessible at the given row index.
|
||||
*/
|
||||
Accessible* GetRowAt(int32_t aRow);
|
||||
|
||||
/**
|
||||
* Return cell accessible at the given column index in the row.
|
||||
*/
|
||||
Accessible* GetCellInRowAt(Accessible* aRow, int32_t aColumn);
|
||||
|
||||
/**
|
||||
* Set aria-selected attribute value on DOM node of the given accessible.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "TableAccessible.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccIterator.h"
|
||||
|
||||
#include "nsTableCellFrame.h"
|
||||
#include "nsTableWrapperFrame.h"
|
||||
|
@ -238,3 +239,32 @@ TableAccessible::IsProbablyLayoutTable()
|
|||
false, "No layout factor strong enough, so will guess data"
|
||||
);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TableAccessible::RowAt(int32_t aRow)
|
||||
{
|
||||
int32_t rowIdx = aRow;
|
||||
|
||||
AccIterator rowIter(this->AsAccessible(), filters::GetRow);
|
||||
|
||||
Accessible* row = rowIter.Next();
|
||||
while (rowIdx != 0 && (row = rowIter.Next())) {
|
||||
rowIdx--;
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TableAccessible::CellInRowAt(Accessible* aRow, int32_t aColumn)
|
||||
{
|
||||
int32_t colIdx = aColumn;
|
||||
|
||||
AccIterator cellIter(aRow, filters::GetCell);
|
||||
Accessible* cell = cellIter.Next();
|
||||
while (colIdx != 0 && (cell = cellIter.Next())) {
|
||||
colIdx--;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
|
|
@ -179,6 +179,18 @@ public:
|
|||
* Convert the table to an Accessible*.
|
||||
*/
|
||||
virtual Accessible* AsAccessible() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Return row accessible at the given row index.
|
||||
*/
|
||||
Accessible* RowAt(int32_t aRow);
|
||||
|
||||
/**
|
||||
* Return cell accessible at the given column index in the row.
|
||||
*/
|
||||
Accessible* CellInRowAt(Accessible* aRow, int32_t aColumn);
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -619,6 +619,14 @@ HTMLTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx)
|
|||
nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx);
|
||||
Accessible* cell = mDoc->GetAccessible(cellContent);
|
||||
|
||||
// Sometimes, the accessible returned here is a row accessible instead of
|
||||
// a cell accessible, for example when a cell has CSS display:block; set.
|
||||
// In such cases, iterate through the cells in this row differently to find it.
|
||||
if (cell && cell->IsTableRow()) {
|
||||
Accessible* row = RowAt(aRowIdx);
|
||||
return CellInRowAt(row, aColIdx);
|
||||
}
|
||||
|
||||
// XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may
|
||||
// return itself as a cell what makes Orca hang.
|
||||
return cell == this ? nullptr : cell;
|
||||
|
@ -762,6 +770,9 @@ HTMLTableAccessible::UnselectCol(uint32_t aColIdx)
|
|||
RemoveRowsOrColumnsFromSelection(aColIdx, TableSelection::Column, false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTMLTableAccessible: protected implementation
|
||||
|
||||
nsresult
|
||||
HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, TableSelection aTarget)
|
||||
{
|
||||
|
|
|
@ -127,6 +127,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=410052
|
|||
];
|
||||
testTableIndexes("tableinsane6", idxes);
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////
|
||||
// Table with a cell that has display: block; style
|
||||
idxes = [
|
||||
[0, 1]
|
||||
];
|
||||
testTableIndexes("tablewithcelldisplayblock", idxes);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -405,5 +412,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=410052
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="tablewithcelldisplayblock">
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<td style="display: block;">b</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -66,6 +66,15 @@
|
|||
|
||||
testTableStruct("table4", cellsArray);
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////
|
||||
// Table with a cell that has display: block; style
|
||||
|
||||
cellsArray = [
|
||||
[kRowHeaderCell, kDataCell]
|
||||
];
|
||||
|
||||
testTableStruct("table5", cellsArray);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -198,5 +207,12 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="table5">
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<td style="display: block;">b</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -218,6 +218,21 @@
|
|||
] };
|
||||
testAccessibleTree("table_containing_inlinetable", accTree);
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////////
|
||||
// table with a cell that has display:block
|
||||
accTree =
|
||||
{ TABLE: [
|
||||
{ ROW: [
|
||||
{ CELL: [
|
||||
{ TEXT_LEAF: [ ] }
|
||||
] },
|
||||
{ CELL: [
|
||||
{ TEXT_LEAF: [ ] }
|
||||
] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("table_containing_block_cell", accTree);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -343,5 +358,12 @@
|
|||
</tr>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
|
||||
<table id="table_containing_block_cell">
|
||||
<tr>
|
||||
<td>Normal cell</td>
|
||||
<td style="display: block;">Block cell</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1498,11 +1498,6 @@ pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.
|
|||
// Enable GMP support in the addon manager.
|
||||
pref("media.gmp-provider.enabled", true);
|
||||
|
||||
// Enable blocking access to storage from tracking resources by default on Nightly
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
|
||||
#endif
|
||||
|
||||
pref("browser.contentblocking.allowlist.storage.enabled", true);
|
||||
|
||||
pref("browser.contentblocking.global-toggle.enabled", true);
|
||||
|
|
|
@ -281,6 +281,12 @@ class BasePopup {
|
|||
|
||||
stack.appendChild(browser);
|
||||
viewNode.appendChild(stack);
|
||||
if (!this.extension.remote) {
|
||||
// FIXME: bug 1494029 - this code used to rely on the browser binding
|
||||
// accessing browser.contentWindow. This is a stopgap to continue doing
|
||||
// that, but we should get rid of it in the long term.
|
||||
browser.contentWindow; // eslint-disable-line no-unused-expressions
|
||||
}
|
||||
|
||||
ExtensionParent.apiManager.emit("extension-browser-inserted", browser);
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ prefs =
|
|||
dom.animations-api.timelines.enabled=true
|
||||
support-files =
|
||||
silence.ogg
|
||||
head.js
|
||||
head_pageAction.js
|
||||
head_sessions.js
|
||||
head_webNavigation.js
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
install-to-subdir = test-oop-extensions
|
||||
tags = webextensions remote-webextensions
|
||||
skip-if = !e10s
|
||||
|
||||
[browser_ext_popup_select.js]
|
||||
skip-if = debug || os != 'win' # FIXME: re-enable on debug build (bug 1442822)
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[browser_ext_popup_select.js]
|
||||
skip-if = debug || os != 'win' # FIXME: re-enable on debug build (bug 1442822)
|
||||
|
||||
[include:browser-common.ini]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = webextensions in-process-webextensions
|
||||
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
|
|
|
@ -1,76 +1,196 @@
|
|||
var win;
|
||||
var feedItem;
|
||||
var container;
|
||||
|
||||
SimpleTest.requestCompleteLog();
|
||||
ChromeUtils.import("resource://testing-common/HandlerServiceTestUtils.jsm", this);
|
||||
|
||||
let gHandlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
|
||||
|
||||
let gOldMailHandlers = [];
|
||||
let gDummyHandlers = [];
|
||||
let gOriginalPreferredMailHandler;
|
||||
let gOriginalPreferredPDFHandler;
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
function removeDummyHandlers(handlers) {
|
||||
// Remove any of the dummy handlers we created.
|
||||
for (let i = handlers.Count() - 1; i >= 0; i--) {
|
||||
try {
|
||||
if (gDummyHandlers.some(h => h.uriTemplate == handlers.queryElementAt(i, Ci.nsIWebHandlerApp).uriTemplate)) {
|
||||
handlers.removeElementAt(i);
|
||||
}
|
||||
} catch (ex) { /* ignore non-web-app handlers */ }
|
||||
}
|
||||
}
|
||||
// Re-add the original protocol handlers:
|
||||
let mailHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("mailto");
|
||||
let mailHandlers = mailHandlerInfo.possibleApplicationHandlers;
|
||||
for (let h of gOldMailHandlers) {
|
||||
mailHandlers.appendElement(h);
|
||||
}
|
||||
removeDummyHandlers(mailHandlers);
|
||||
mailHandlerInfo.preferredApplicationHandler = gOriginalPreferredMailHandler;
|
||||
gHandlerService.store(mailHandlerInfo);
|
||||
|
||||
let pdfHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("application/pdf");
|
||||
removeDummyHandlers(pdfHandlerInfo.possibleApplicationHandlers);
|
||||
pdfHandlerInfo.preferredApplicationHandler = gOriginalPreferredPDFHandler;
|
||||
gHandlerService.store(pdfHandlerInfo);
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function scrubMailtoHandlers(handlerInfo) {
|
||||
// Remove extant web handlers because they have icons that
|
||||
// we fetch from the web, which isn't allowed in tests.
|
||||
let handlers = handlerInfo.possibleApplicationHandlers;
|
||||
for (let i = handlers.Count() - 1; i >= 0; i--) {
|
||||
try {
|
||||
let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
|
||||
gOldMailHandlers.push(handler);
|
||||
// If we get here, this is a web handler app. Remove it:
|
||||
handlers.removeElementAt(i);
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
// Create our dummy handlers
|
||||
let handler1 = Cc["@mozilla.org/uriloader/web-handler-app;1"]
|
||||
.createInstance(Ci.nsIWebHandlerApp);
|
||||
handler1.name = "Handler 1";
|
||||
handler1.uriTemplate = "https://example.com/first/%s";
|
||||
|
||||
let handler2 = Cc["@mozilla.org/uriloader/web-handler-app;1"]
|
||||
.createInstance(Ci.nsIWebHandlerApp);
|
||||
handler2.name = "Handler 2";
|
||||
handler2.uriTemplate = "http://example.org/second/%s";
|
||||
gDummyHandlers.push(handler1, handler2);
|
||||
|
||||
function substituteWebHandlers(handlerInfo) {
|
||||
// Append the dummy handlers to replace them:
|
||||
let handlers = handlerInfo.possibleApplicationHandlers;
|
||||
handlers.appendElement(handler1);
|
||||
handlers.appendElement(handler2);
|
||||
gHandlerService.store(handlerInfo);
|
||||
}
|
||||
// Set up our mailto handler test infrastructure.
|
||||
let mailtoHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("mailto");
|
||||
scrubMailtoHandlers(mailtoHandlerInfo);
|
||||
gOriginalPreferredMailHandler = mailtoHandlerInfo.preferredApplicationHandler;
|
||||
substituteWebHandlers(mailtoHandlerInfo);
|
||||
|
||||
// Now do the same for pdf handler:
|
||||
let pdfHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("application/pdf");
|
||||
// PDF doesn't have built-in web handlers, so no need to scrub.
|
||||
gOriginalPreferredPDFHandler = pdfHandlerInfo.preferredApplicationHandler;
|
||||
substituteWebHandlers(pdfHandlerInfo);
|
||||
|
||||
await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
|
||||
info("Preferences page opened on the general pane.");
|
||||
|
||||
await gBrowser.selectedBrowser.contentWindow.promiseLoadHandlersList;
|
||||
info("Apps list loaded.");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function getFeedItem() {
|
||||
win = gBrowser.selectedBrowser.contentWindow;
|
||||
async function selectStandardOptions(itemToUse) {
|
||||
async function selectItemInPopup(item) {
|
||||
let popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
// Synthesizing the mouse on the .actionsMenu menulist somehow just selects
|
||||
// the top row. Probably something to do with the multiple layers of anon
|
||||
// content - workaround by using the `.open` setter instead.
|
||||
list.open = true;
|
||||
await popupShown;
|
||||
let popupHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
|
||||
if (typeof item == "function") {
|
||||
item = item();
|
||||
}
|
||||
item.click();
|
||||
popup.hidePopup();
|
||||
await popupHidden;
|
||||
return item;
|
||||
}
|
||||
|
||||
container = win.document.getElementById("handlersView");
|
||||
feedItem = container.querySelector("richlistitem[type='application/vnd.mozilla.maybe.feed']");
|
||||
Assert.ok(feedItem, "feedItem is present in handlersView.");
|
||||
});
|
||||
let itemType = itemToUse.getAttribute("type");
|
||||
// Center the item. Center rather than top so it doesn't get blocked by
|
||||
// the search header.
|
||||
itemToUse.scrollIntoView({block: "center"});
|
||||
itemToUse.closest("richlistbox").selectItem(itemToUse);
|
||||
Assert.ok(itemToUse.selected, "Should be able to select our item.");
|
||||
// Force reflow to make sure it's visible and the container dropdown isn't
|
||||
// hidden.
|
||||
itemToUse.getBoundingClientRect().top;
|
||||
let list = itemToUse.querySelector(".actionsMenu");
|
||||
let popup = list.menupopup;
|
||||
|
||||
add_task(async function selectInternalOptionForFeed() {
|
||||
// Select the item.
|
||||
feedItem.scrollIntoView();
|
||||
container.selectItem(feedItem);
|
||||
Assert.ok(feedItem.selected, "Should be able to select our item.");
|
||||
// select one of our test cases:
|
||||
let handlerItem = list.querySelector("menuitem[label*='Handler 1']");
|
||||
await selectItemInPopup(handlerItem);
|
||||
let {preferredAction, alwaysAskBeforeHandling} = HandlerServiceTestUtils.getHandlerInfo(itemType);
|
||||
Assert.notEqual(preferredAction, Ci.nsIHandlerInfo.alwaysAsk,
|
||||
"Should have selected something other than 'always ask' (" + itemType + ")");
|
||||
Assert.ok(!alwaysAskBeforeHandling, "Should have turned off asking before handling (" + itemType + ")");
|
||||
|
||||
let list = feedItem.querySelector(".actionsMenu");
|
||||
// Test the alwaysAsk option
|
||||
let alwaysAskItem = list.getElementsByAttribute("action", Ci.nsIHandlerInfo.alwaysAsk)[0];
|
||||
await selectItemInPopup(alwaysAskItem);
|
||||
Assert.equal(list.selectedItem, alwaysAskItem, "Should have selected always ask item (" + itemType + ")");
|
||||
alwaysAskBeforeHandling = HandlerServiceTestUtils.getHandlerInfo(itemType).alwaysAskBeforeHandling;
|
||||
Assert.ok(alwaysAskBeforeHandling, "Should have turned on asking before handling (" + itemType + ")");
|
||||
|
||||
// Find the "Add Live bookmarks option".
|
||||
let chooseItems = list.getElementsByAttribute("action", Ci.nsIHandlerInfo.handleInternally);
|
||||
Assert.equal(chooseItems.length, 1, "Should only be one action to handle internally");
|
||||
let useDefaultItem = list.getElementsByAttribute("action", Ci.nsIHandlerInfo.useSystemDefault);
|
||||
useDefaultItem = useDefaultItem && useDefaultItem[0];
|
||||
if (useDefaultItem) {
|
||||
await selectItemInPopup(useDefaultItem);
|
||||
Assert.equal(list.selectedItem, useDefaultItem, "Should have selected always ask item (" + itemType + ")");
|
||||
preferredAction = HandlerServiceTestUtils.getHandlerInfo(itemType).preferredAction;
|
||||
Assert.equal(preferredAction, Ci.nsIHandlerInfo.useSystemDefault, "Should have selected 'use default' (" + itemType + ")");
|
||||
} else {
|
||||
// Whether there's a "use default" item depends on the OS, so it's not
|
||||
// possible to rely on it being the case or not.
|
||||
info("No 'Use default' item, so not testing (" + itemType + ")");
|
||||
}
|
||||
|
||||
// Select the option.
|
||||
let cmdEvent = win.document.createEvent("xulcommandevent");
|
||||
cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
|
||||
chooseItems[0].dispatchEvent(cmdEvent);
|
||||
// Select a web app item.
|
||||
let webAppItems = Array.from(popup.getElementsByAttribute("action", Ci.nsIHandlerInfo.useHelperApp));
|
||||
webAppItems = webAppItems.filter(item => (item.handlerApp instanceof Ci.nsIWebHandlerApp));
|
||||
Assert.equal(webAppItems.length, 2, "Should have 2 web application handler. (" + itemType + ")");
|
||||
Assert.notEqual(webAppItems[0].label, webAppItems[1].label, "Should have 2 different web app handlers");
|
||||
let selectedItem = await selectItemInPopup(webAppItems[0]);
|
||||
|
||||
// Check that we display the correct result.
|
||||
Assert.ok(list.selectedItem, "Should have a selected item.");
|
||||
Assert.equal(list.selectedItem.getAttribute("action"),
|
||||
Ci.nsIHandlerInfo.handleInternally,
|
||||
"Newly selected item should be the expected one.");
|
||||
});
|
||||
Assert.equal(selectedItem.label, itemToUse.querySelector(".actionContainer label").value,
|
||||
"Should have selected correct item (" + itemType + ")");
|
||||
let {preferredApplicationHandler} = HandlerServiceTestUtils.getHandlerInfo(itemType);
|
||||
preferredApplicationHandler.QueryInterface(Ci.nsIWebHandlerApp);
|
||||
Assert.equal(selectedItem.handlerApp.uriTemplate, preferredApplicationHandler.uriTemplate,
|
||||
"App should actually be selected in the backend. (" + itemType + ")");
|
||||
|
||||
// This builds on the previous selectInternalOptionForFeed task.
|
||||
add_task(async function reselectInternalOptionForFeed() {
|
||||
// Now select a different option in the list - use the pdf item as that doesn't
|
||||
// need to load any favicons.
|
||||
let anotherItem = container.querySelector("richlistitem[type='application/pdf']");
|
||||
// select the other web app item
|
||||
selectedItem = await selectItemInPopup(webAppItems[1]);
|
||||
|
||||
container.selectItem(anotherItem);
|
||||
Assert.equal(selectedItem.label, itemToUse.querySelector(".actionContainer label").value,
|
||||
"Should have selected correct item (" + itemType + ")");
|
||||
preferredApplicationHandler = HandlerServiceTestUtils.getHandlerInfo(itemType).preferredApplicationHandler;
|
||||
preferredApplicationHandler.QueryInterface(Ci.nsIWebHandlerApp);
|
||||
Assert.equal(selectedItem.handlerApp.uriTemplate, preferredApplicationHandler.uriTemplate,
|
||||
"App should actually be selected in the backend. (" + itemType + ")");
|
||||
}
|
||||
|
||||
// Now select the feed item again, and check what it is displaying.
|
||||
container.selectItem(feedItem);
|
||||
add_task(async function checkDropdownBehavior() {
|
||||
let win = gBrowser.selectedBrowser.contentWindow;
|
||||
|
||||
let list = feedItem.querySelector(".actionsMenu");
|
||||
let container = win.document.getElementById("handlersView");
|
||||
|
||||
Assert.ok(list.selectedItem,
|
||||
"Should have a selected item");
|
||||
Assert.equal(list.selectedItem.getAttribute("action"),
|
||||
Ci.nsIHandlerInfo.handleInternally,
|
||||
"Selected item should still be the same as the previously selected item.");
|
||||
// First check a protocol handler item.
|
||||
let mailItem = container.querySelector("richlistitem[type='mailto']");
|
||||
Assert.ok(mailItem, "mailItem is present in handlersView.");
|
||||
await selectStandardOptions(mailItem);
|
||||
|
||||
// Then check a content menu item.
|
||||
let pdfItem = container.querySelector("richlistitem[type='application/pdf']");
|
||||
Assert.ok(pdfItem, "pdfItem is present in handlersView.");
|
||||
await selectStandardOptions(pdfItem);
|
||||
});
|
||||
|
||||
add_task(async function sortingCheck() {
|
||||
win = gBrowser.selectedBrowser.contentWindow;
|
||||
|
||||
let win = gBrowser.selectedBrowser.contentWindow;
|
||||
const handlerView = win.document.getElementById("handlersView");
|
||||
const typeColumn = win.document.getElementById("typeColumn");
|
||||
Assert.ok(typeColumn, "typeColumn is present in handlersView.");
|
||||
|
|
|
@ -35,7 +35,8 @@ function debug(s) {
|
|||
|
||||
function _updateCurrentContentOuterWindowID(browser) {
|
||||
if (!browser.outerWindowID ||
|
||||
browser.outerWindowID === _lastTopLevelWindowID) {
|
||||
browser.outerWindowID === _lastTopLevelWindowID ||
|
||||
browser.ownerGlobal != _trackedWindows[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,9 @@ class DebugTargetList extends PureComponent {
|
|||
id: "about-debugging-debug-target-list-empty"
|
||||
},
|
||||
dom.span(
|
||||
{},
|
||||
{
|
||||
className: "js-debug-target-list-empty"
|
||||
},
|
||||
"Nothing yet."
|
||||
)
|
||||
);
|
||||
|
|
|
@ -60,7 +60,7 @@ class TemporaryExtensionAction extends PureComponent {
|
|||
},
|
||||
dom.button(
|
||||
{
|
||||
className: "aboutdebugging-button",
|
||||
className: "aboutdebugging-button js-temporary-extension-remove-button",
|
||||
onClick: e => this.remove()
|
||||
},
|
||||
"Remove",
|
||||
|
|
|
@ -37,7 +37,7 @@ class TemporaryExtensionInstaller extends PureComponent {
|
|||
},
|
||||
dom.button(
|
||||
{
|
||||
className: "aboutdebugging-button",
|
||||
className: "aboutdebugging-button js-temporary-extension-install-button",
|
||||
onClick: e => this.install()
|
||||
},
|
||||
"Load Temporary Add-on…"
|
||||
|
|
|
@ -3,13 +3,16 @@ tags = devtools
|
|||
subsuite = devtools
|
||||
support-files =
|
||||
debug-target-pane_collapsibilities_head.js
|
||||
head-addons-script.js
|
||||
head.js
|
||||
resources/test-temporary-extension/*
|
||||
!/devtools/client/shared/test/shared-head.js
|
||||
!/devtools/client/shared/test/telemetry-test-helpers.js
|
||||
|
||||
[browser_aboutdebugging_connect_networklocations.js]
|
||||
[browser_aboutdebugging_debug-target-pane_collapsibilities_interaction.js]
|
||||
[browser_aboutdebugging_debug-target-pane_collapsibilities_preference.js]
|
||||
[browser_aboutdebugging_debug-target-pane_empty.js]
|
||||
[browser_aboutdebugging_navigate.js]
|
||||
[browser_aboutdebugging_sidebar_network_runtimes.js]
|
||||
[browser_aboutdebugging_thisfirefox.js]
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from head-addons-script.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Load addons helpers
|
||||
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "head-addons-script.js", this);
|
||||
|
||||
/**
|
||||
* Test that an "empty" message is displayed when there are no debug targets in a debug
|
||||
* target pane.
|
||||
*/
|
||||
|
||||
const EXTENSION_PATH = "resources/test-temporary-extension/manifest.json";
|
||||
const EXTENSION_NAME = "test-temporary-extension";
|
||||
|
||||
add_task(async function() {
|
||||
prepareCollapsibilitiesTest();
|
||||
|
||||
const { document, tab } = await openAboutDebugging();
|
||||
|
||||
info("Check that the temporary extensions pane is empty");
|
||||
const temporaryExtensionPane = getDebugTargetPane("Temporary Extensions", document);
|
||||
ok(!temporaryExtensionPane.querySelector(".js-debug-target-item"),
|
||||
"Temporary Extensions pane contains no debug target");
|
||||
|
||||
info("Check an empty target pane message is displayed");
|
||||
ok(temporaryExtensionPane.querySelector(".js-debug-target-list-empty"),
|
||||
"An empty target list message is displayed");
|
||||
|
||||
info("Install a temporary extension");
|
||||
await installTemporaryExtension(EXTENSION_PATH, EXTENSION_NAME, document);
|
||||
|
||||
info("Wait until a debug target item appears");
|
||||
await waitUntil(() => temporaryExtensionPane.querySelector(".js-debug-target-item"));
|
||||
|
||||
info("Check the empty target pane message is no longer displayed");
|
||||
ok(!temporaryExtensionPane.querySelector(".js-debug-target-list-empty"),
|
||||
"The empty target list message is no longer displayed");
|
||||
|
||||
const temporaryExtensionItem =
|
||||
temporaryExtensionPane.querySelector(".js-debug-target-item");
|
||||
ok(temporaryExtensionItem, "Temporary Extensions pane now shows debug target");
|
||||
|
||||
info("Remove the temporary extension");
|
||||
temporaryExtensionItem.querySelector(".js-temporary-extension-remove-button").click();
|
||||
|
||||
info("Wait until the debug target item disappears");
|
||||
await waitUntil(() => !temporaryExtensionPane.querySelector(".js-debug-target-item"));
|
||||
|
||||
info("Check the empty target pane message is displayed again");
|
||||
ok(temporaryExtensionPane.querySelector(".js-debug-target-list-empty"),
|
||||
"An empty target list message is displayed again");
|
||||
|
||||
await removeTab(tab);
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* import-globals-from ../../../shared/test/shared-head.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { Management } = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
function getSupportsFile(path) {
|
||||
const cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
||||
.getService(Ci.nsIChromeRegistry);
|
||||
const uri = Services.io.newURI(CHROME_URL_ROOT + path);
|
||||
const fileurl = cr.convertChromeURL(uri);
|
||||
return fileurl.QueryInterface(Ci.nsIFileURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a temporary extension at the provided path, with the provided name.
|
||||
* Will use a mock file picker to select the file.
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function installTemporaryExtension(path, name, document) {
|
||||
// Mock the file picker to select a test addon
|
||||
const MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window);
|
||||
const file = getSupportsFile(path);
|
||||
MockFilePicker.setFiles([file.file]);
|
||||
|
||||
const onAddonInstalled = new Promise(done => {
|
||||
Management.on("startup", function listener(event, extension) {
|
||||
if (extension.name != name) {
|
||||
return;
|
||||
}
|
||||
|
||||
Management.off("startup", listener);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// Trigger the file picker by clicking on the button
|
||||
document.querySelector(".js-temporary-extension-install-button").click();
|
||||
|
||||
info("Wait for addon to be installed");
|
||||
await onAddonInstalled;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "test-temporary-extension",
|
||||
"version": "1.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "test-temporary-extension@mozilla.org"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["script.js"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-env browser */
|
||||
/* global browser */
|
||||
|
||||
"use strict";
|
||||
|
||||
document.body.innerText = "Background Page Body Test Content";
|
|
@ -162,6 +162,13 @@
|
|||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* If there is a source editor shows up in the last row of TreeView,
|
||||
* its height should not collapse into zero
|
||||
*/
|
||||
.network-monitor .tree-container .treeTable tr:last-child.editor-row-container {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.network-monitor .source-editor-mount {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -68,6 +68,8 @@ add_task(async function() {
|
|||
const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
|
||||
is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
|
||||
"The response json view has the intended visibility.");
|
||||
is(tabpanel.querySelector(".editor-row-container").clientHeight !== 0, true,
|
||||
"The source editor container has visible height.");
|
||||
is(tabpanel.querySelector(".CodeMirror-code") === null, false,
|
||||
"The response editor has the intended visibility.");
|
||||
is(tabpanel.querySelector(".response-image-box") === null, true,
|
||||
|
|
|
@ -1141,17 +1141,30 @@ class JSTerm extends Component {
|
|||
|
||||
if (this._autocompleteQuery && input.startsWith(this._autocompleteQuery)) {
|
||||
let filterBy = input;
|
||||
// Find the last non-alphanumeric other than "_", ":", or "$" if it exists.
|
||||
const lastNonAlpha = input.match(/[^a-zA-Z0-9_$:][a-zA-Z0-9_$:]*$/);
|
||||
// If input contains non-alphanumerics, use the part after the last one
|
||||
// to filter the cache.
|
||||
if (lastNonAlpha) {
|
||||
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
|
||||
if (this._autocompleteCache.isElementAccess) {
|
||||
// if we're performing an element access, we can simply retrieve whatever comes
|
||||
// after the last opening bracket.
|
||||
filterBy = input.substring(input.lastIndexOf("[") + 1);
|
||||
} else {
|
||||
// Find the last non-alphanumeric other than "_", ":", or "$" if it exists.
|
||||
const lastNonAlpha = input.match(/[^a-zA-Z0-9_$:][a-zA-Z0-9_$:]*$/);
|
||||
// If input contains non-alphanumerics, use the part after the last one
|
||||
// to filter the cache.
|
||||
if (lastNonAlpha) {
|
||||
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const stripWrappingQuotes = s => s.replace(/^['"`](.+(?=['"`]$))['"`]$/g, "$1");
|
||||
const filterByLc = filterBy.toLocaleLowerCase();
|
||||
const looseMatching = !filterBy || filterBy[0].toLocaleLowerCase() === filterBy[0];
|
||||
const newList = this._autocompleteCache.filter(l => {
|
||||
const needStripQuote = this._autocompleteCache.isElementAccess
|
||||
&& !/^[`"']/.test(filterBy);
|
||||
const newList = this._autocompleteCache.matches.filter(l => {
|
||||
if (needStripQuote) {
|
||||
l = stripWrappingQuotes(l);
|
||||
}
|
||||
|
||||
if (looseMatching) {
|
||||
return l.toLocaleLowerCase().startsWith(filterByLc);
|
||||
}
|
||||
|
@ -1161,7 +1174,8 @@ class JSTerm extends Component {
|
|||
|
||||
this._receiveAutocompleteProperties(null, {
|
||||
matches: newList,
|
||||
matchProp: filterBy
|
||||
matchProp: filterBy,
|
||||
isElementAccess: this._autocompleteCache.isElementAccess,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -1191,28 +1205,49 @@ class JSTerm extends Component {
|
|||
this.currentAutoCompletionRequestId = null;
|
||||
|
||||
// Cache whatever came from the server if the last char is
|
||||
// alphanumeric or '.'
|
||||
// alphanumeric, '.' or '['.
|
||||
const inputUntilCursor = this.getInputValueBeforeCursor();
|
||||
|
||||
if (requestId != null && /[a-zA-Z0-9.]$/.test(inputUntilCursor)) {
|
||||
this._autocompleteCache = message.matches;
|
||||
if (requestId != null && /[a-zA-Z0-9.\[]$/.test(inputUntilCursor)) {
|
||||
this._autocompleteCache = {
|
||||
matches: message.matches,
|
||||
matchProp: message.matchProp,
|
||||
isElementAccess: message.isElementAccess,
|
||||
};
|
||||
this._autocompleteQuery = inputUntilCursor;
|
||||
}
|
||||
|
||||
const {matches, matchProp} = message;
|
||||
const {matches, matchProp, isElementAccess} = message;
|
||||
if (!matches.length) {
|
||||
this.clearCompletion();
|
||||
this.emit("autocomplete-updated");
|
||||
return;
|
||||
}
|
||||
|
||||
const items = matches.map(match => ({
|
||||
preLabel: match.substring(0, matchProp.length),
|
||||
label: match
|
||||
}));
|
||||
const items = matches.map(label => {
|
||||
let preLabel = label.substring(0, matchProp.length);
|
||||
// If the user is performing an element access, and if they did not typed a quote,
|
||||
// then we need to adjust the preLabel to match the quote from the label + what
|
||||
// the user entered.
|
||||
if (isElementAccess && /^['"`]/.test(matchProp) === false) {
|
||||
preLabel = label.substring(0, matchProp.length + 1);
|
||||
}
|
||||
return {preLabel, label, isElementAccess};
|
||||
});
|
||||
|
||||
if (items.length > 0) {
|
||||
const suffix = items[0].label.substring(matchProp.length);
|
||||
const {preLabel, label} = items[0];
|
||||
let suffix = label.substring(preLabel.length);
|
||||
if (isElementAccess) {
|
||||
if (!matchProp) {
|
||||
suffix = label;
|
||||
}
|
||||
const inputAfterCursor = this.getInputValue().substring(inputUntilCursor.length);
|
||||
// If there's not a bracket after the cursor, add it to the completionText.
|
||||
if (!inputAfterCursor.trimLeft().startsWith("]")) {
|
||||
suffix = suffix + "]";
|
||||
}
|
||||
}
|
||||
this.setAutoCompletionText(suffix);
|
||||
}
|
||||
|
||||
|
@ -1245,7 +1280,7 @@ class JSTerm extends Component {
|
|||
|
||||
if (this.editor) {
|
||||
popupAlignElement = this.node.querySelector(".CodeMirror-cursor");
|
||||
// We need to show the popup at the ".".
|
||||
// We need to show the popup at the "." or "[".
|
||||
xOffset = -1 * matchProp.length * this._inputCharWidth;
|
||||
yOffset = 5;
|
||||
} else if (this.inputNode) {
|
||||
|
@ -1274,7 +1309,23 @@ class JSTerm extends Component {
|
|||
onAutocompleteSelect() {
|
||||
const {selectedItem} = this.autocompletePopup;
|
||||
if (selectedItem) {
|
||||
const suffix = selectedItem.label.substring(selectedItem.preLabel.length);
|
||||
const {preLabel, label, isElementAccess} = selectedItem;
|
||||
let suffix = label.substring(preLabel.length);
|
||||
|
||||
// If the user is performing an element access, we need to check if we should add
|
||||
// starting and ending quotes, as well as a closing bracket.
|
||||
if (isElementAccess) {
|
||||
const inputBeforeCursor = this.getInputValueBeforeCursor();
|
||||
if (inputBeforeCursor.trim().endsWith("[")) {
|
||||
suffix = label;
|
||||
}
|
||||
|
||||
const inputAfterCursor = this.getInputValue().substring(inputBeforeCursor.length);
|
||||
// If there's no closing bracket after the cursor, add it to the completionText.
|
||||
if (!inputAfterCursor.trimLeft().startsWith("]")) {
|
||||
suffix = suffix + "]";
|
||||
}
|
||||
}
|
||||
this.setAutoCompletionText(suffix);
|
||||
} else {
|
||||
this.setAutoCompletionText("");
|
||||
|
@ -1306,10 +1357,6 @@ class JSTerm extends Component {
|
|||
|
||||
/**
|
||||
* Accept the proposed input completion.
|
||||
*
|
||||
* @return boolean
|
||||
* True if there was a selected completion item and the input value
|
||||
* was updated, false otherwise.
|
||||
*/
|
||||
acceptProposedCompletion() {
|
||||
let completionText = this.getAutoCompletionText();
|
||||
|
@ -1320,8 +1367,27 @@ class JSTerm extends Component {
|
|||
// autocomplete to `document`, but the autocompletion text only shows `t`).
|
||||
if (this.autocompletePopup.isOpen && this.autocompletePopup.selectedItem) {
|
||||
const {selectedItem} = this.autocompletePopup;
|
||||
completionText = selectedItem.label;
|
||||
numberOfCharsToReplaceCharsBeforeCursor = selectedItem.preLabel.length;
|
||||
const {label, preLabel, isElementAccess} = selectedItem;
|
||||
|
||||
completionText = label;
|
||||
numberOfCharsToReplaceCharsBeforeCursor = preLabel.length;
|
||||
|
||||
// If the user is performing an element access, we need to check if we should add
|
||||
// starting and ending quotes, as well as a closing bracket.
|
||||
if (isElementAccess) {
|
||||
const inputBeforeCursor = this.getInputValueBeforeCursor();
|
||||
const lastOpeningBracketIndex = inputBeforeCursor.lastIndexOf("[");
|
||||
if (lastOpeningBracketIndex > -1) {
|
||||
numberOfCharsToReplaceCharsBeforeCursor =
|
||||
inputBeforeCursor.substring(lastOpeningBracketIndex + 1).length;
|
||||
}
|
||||
|
||||
const inputAfterCursor = this.getInputValue().substring(inputBeforeCursor.length);
|
||||
// If there's not a bracket after the cursor, add it.
|
||||
if (!inputAfterCursor.trimLeft().startsWith("]")) {
|
||||
completionText = completionText + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.clearCompletion();
|
||||
|
|
|
@ -205,6 +205,8 @@ skip-if = verify
|
|||
[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
|
||||
[browser_jsterm_await_paused.js]
|
||||
[browser_jsterm_await.js]
|
||||
[browser_jsterm_completion_bracket_cached_results.js]
|
||||
[browser_jsterm_completion_bracket.js]
|
||||
[browser_jsterm_completion_case_sensitivity.js]
|
||||
[browser_jsterm_completion.js]
|
||||
[browser_jsterm_content_defined_helpers.js]
|
||||
|
|
|
@ -94,6 +94,29 @@ async function performTests() {
|
|||
|
||||
ok(!getPopupLabels(popup).includes("docfoobar"),
|
||||
"autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
|
||||
info("Ensure filtering from the cache does work");
|
||||
await jsterm.execute(`
|
||||
window.testObject = Object.create(null);
|
||||
window.testObject.zz = "zz";
|
||||
window.testObject.zzz = "zzz";
|
||||
window.testObject.zzzz = "zzzz";
|
||||
`);
|
||||
await jstermComplete("window.testObject.");
|
||||
await jstermComplete("window.testObject.z");
|
||||
is(getPopupLabels(popup).join("-"), "zz-zzz-zzzz", "results are the expected ones");
|
||||
|
||||
onUpdated = jsterm.once("autocomplete-updated");
|
||||
EventUtils.sendString("z");
|
||||
await onUpdated;
|
||||
is(getPopupLabels(popup).join("-"), "zz-zzz-zzzz",
|
||||
"filtering from the cache works - step 1");
|
||||
|
||||
onUpdated = jsterm.once("autocomplete-updated");
|
||||
EventUtils.sendString("z");
|
||||
await onUpdated;
|
||||
is(getPopupLabels(popup).join("-"), "zzz-zzzz",
|
||||
"filtering from the cache works - step 2");
|
||||
}
|
||||
|
||||
function getPopupLabels(popup) {
|
||||
|
|
|
@ -33,12 +33,12 @@ async function performTests() {
|
|||
await onPopupOpen;
|
||||
|
||||
ok(popup.isOpen, "popup is open");
|
||||
is(popup.itemCount, jsterm._autocompleteCache.length, "popup.itemCount is correct");
|
||||
ok(jsterm._autocompleteCache.includes("addEventListener"),
|
||||
const cacheMatches = jsterm._autocompleteCache.matches;
|
||||
is(popup.itemCount, cacheMatches.length, "popup.itemCount is correct");
|
||||
ok(cacheMatches.includes("addEventListener"),
|
||||
"addEventListener is in the list of suggestions");
|
||||
ok(jsterm._autocompleteCache.includes("bgColor"),
|
||||
"bgColor is in the list of suggestions");
|
||||
ok(jsterm._autocompleteCache.includes("ATTRIBUTE_NODE"),
|
||||
ok(cacheMatches.includes("bgColor"), "bgColor is in the list of suggestions");
|
||||
ok(cacheMatches.includes("ATTRIBUTE_NODE"),
|
||||
"ATTRIBUTE_NODE is in the list of suggestions");
|
||||
|
||||
const onPopupClose = popup.once("popup-closed");
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that code completion works properly with `[`
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = `data:text/html;charset=utf8,<p>test [ completion.
|
||||
<script>
|
||||
window.testObject = Object.create(null, Object.getOwnPropertyDescriptors({
|
||||
bar: 0,
|
||||
dataTest: 1,
|
||||
"data-test": 2,
|
||||
'da"ta"test': 3,
|
||||
"da\`ta\`test": 4,
|
||||
"da'ta'test": 5,
|
||||
"DATA-TEST": 6,
|
||||
}));
|
||||
</script>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
await testInputs(jsterm);
|
||||
await testCompletionTextUpdateOnPopupNavigate(jsterm);
|
||||
await testAcceptCompletionExistingClosingBracket(jsterm);
|
||||
}
|
||||
|
||||
async function testInputs(jsterm) {
|
||||
const tests = [{
|
||||
description: "Check that the popup is opened when typing `[`",
|
||||
input: "window.testObject[",
|
||||
expectedItems: [
|
||||
`"bar"`,
|
||||
`"da'ta'test"`,
|
||||
`"da\\"ta\\"test"`,
|
||||
`"da\`ta\`test"`,
|
||||
`"data-test"`,
|
||||
`"dataTest"`,
|
||||
`"DATA-TEST"`,
|
||||
],
|
||||
expectedCompletionText: `"bar"]`,
|
||||
expectedInputAfterCompletion: `window.testObject["bar"]`
|
||||
}, {
|
||||
description: "Test that the list can be filtered even without quote",
|
||||
input: "window.testObject[d",
|
||||
expectedItems: [
|
||||
`"da'ta'test"`,
|
||||
`"da\\"ta\\"test"`,
|
||||
`"da\`ta\`test"`,
|
||||
`"data-test"`,
|
||||
`"dataTest"`,
|
||||
`"DATA-TEST"`,
|
||||
],
|
||||
expectedCompletionText: `a'ta'test"]`,
|
||||
expectedInputAfterCompletion: `window.testObject["da'ta'test"]`,
|
||||
}, {
|
||||
description: "Test filtering with quote and string",
|
||||
input: `window.testObject["d`,
|
||||
expectedItems: [
|
||||
`"da'ta'test"`,
|
||||
`"da\\"ta\\"test"`,
|
||||
`"da\`ta\`test"`,
|
||||
`"data-test"`,
|
||||
`"dataTest"`,
|
||||
`"DATA-TEST"`,
|
||||
],
|
||||
expectedCompletionText: `a'ta'test"]`,
|
||||
expectedInputAfterCompletion: `window.testObject["da'ta'test"]`,
|
||||
}, {
|
||||
description: "Test filtering with simple quote and string",
|
||||
input: `window.testObject['d`,
|
||||
expectedItems: [
|
||||
`'da"ta"test'`,
|
||||
`'da\\'ta\\'test'`,
|
||||
`'da\`ta\`test'`,
|
||||
`'data-test'`,
|
||||
`'dataTest'`,
|
||||
`'DATA-TEST'`,
|
||||
],
|
||||
expectedCompletionText: `a"ta"test']`,
|
||||
expectedInputAfterCompletion: `window.testObject['da"ta"test']`,
|
||||
}, {
|
||||
description: "Test filtering with template literal and string",
|
||||
input: "window.testObject[`d",
|
||||
expectedItems: [
|
||||
"`da\"ta\"test`",
|
||||
"`da'ta'test`",
|
||||
"`da\\`ta\\`test`",
|
||||
"`data-test`",
|
||||
"`dataTest`",
|
||||
"`DATA-TEST`",
|
||||
],
|
||||
expectedCompletionText: 'a"ta"test`]',
|
||||
expectedInputAfterCompletion: 'window.testObject[`da"ta"test`]',
|
||||
}, {
|
||||
description: "Test that filtering is case insensitive",
|
||||
input: "window.testObject[data-t",
|
||||
expectedItems: [
|
||||
`"data-test"`,
|
||||
`"DATA-TEST"`,
|
||||
],
|
||||
expectedCompletionText: `est"]`,
|
||||
expectedInputAfterCompletion: `window.testObject["data-test"]`,
|
||||
}, {
|
||||
description:
|
||||
"Test that filtering without quote displays the popup when there's only 1 match",
|
||||
input: "window.testObject[DATA-",
|
||||
expectedItems: [
|
||||
`"DATA-TEST"`
|
||||
],
|
||||
expectedCompletionText: `TEST"]`,
|
||||
expectedInputAfterCompletion: `window.testObject["DATA-TEST"]`,
|
||||
}];
|
||||
|
||||
for (const test of tests) {
|
||||
await testInput(jsterm, test);
|
||||
}
|
||||
}
|
||||
|
||||
async function testInput(jsterm, {
|
||||
description,
|
||||
input,
|
||||
expectedItems,
|
||||
expectedCompletionText,
|
||||
expectedInputAfterCompletion
|
||||
}) {
|
||||
const {autocompletePopup} = jsterm;
|
||||
|
||||
info(`${description} - test popup opening`);
|
||||
const onPopUpOpen = autocompletePopup.once("popup-opened");
|
||||
EventUtils.sendString(input);
|
||||
await onPopUpOpen;
|
||||
|
||||
is(getAutocompletePopupLabels(autocompletePopup).join("|"), expectedItems.join("|"),
|
||||
`${description} - popup has expected item, in expected order`);
|
||||
checkJsTermCompletionValue(jsterm, " ".repeat(input.length) + expectedCompletionText,
|
||||
`${description} - completeNode has expected value`);
|
||||
|
||||
info(`${description} - test accepting completion`);
|
||||
const onPopupClose = autocompletePopup.once("popup-closed");
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
await onPopupClose;
|
||||
checkJsTermValueAndCursor(jsterm, expectedInputAfterCompletion + "|",
|
||||
`${description} - input was completed as expected`);
|
||||
checkJsTermCompletionValue(jsterm, "", `${description} - completeNode is empty`);
|
||||
|
||||
jsterm.setInputValue("");
|
||||
}
|
||||
|
||||
async function testCompletionTextUpdateOnPopupNavigate(jsterm) {
|
||||
const {autocompletePopup} = jsterm;
|
||||
|
||||
info("Test that navigating the popup list update the completionText as expected");
|
||||
const onPopUpOpen = autocompletePopup.once("popup-opened");
|
||||
const input = `window.testObject[data`;
|
||||
EventUtils.sendString(input);
|
||||
await onPopUpOpen;
|
||||
|
||||
is(getAutocompletePopupLabels(autocompletePopup).join("|"),
|
||||
`"data-test"|"dataTest"|"DATA-TEST"`, `popup has expected items, in expected order`);
|
||||
checkJsTermCompletionValue(jsterm, " ".repeat(input.length) + `-test"]`,
|
||||
`completeNode has expected value`);
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
checkJsTermCompletionValue(jsterm, " ".repeat(input.length) + `Test"]`,
|
||||
`completeNode has expected value`);
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
checkJsTermCompletionValue(jsterm, " ".repeat(input.length) + `-TEST"]`,
|
||||
`completeNode has expected value`);
|
||||
|
||||
const onPopupClose = autocompletePopup.once("popup-closed");
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
await onPopupClose;
|
||||
checkJsTermValueAndCursor(jsterm, `window.testObject["DATA-TEST"]|`,
|
||||
`input was completed as expected after navigating the popup`);
|
||||
}
|
||||
|
||||
async function testAcceptCompletionExistingClosingBracket(jsterm) {
|
||||
const {autocompletePopup} = jsterm;
|
||||
|
||||
info("Check that accepting completion when there's a closing bracket does not append " +
|
||||
"another closing bracket");
|
||||
await setInputValueForAutocompletion(jsterm, "window.testObject[]", -1);
|
||||
const onPopUpOpen = autocompletePopup.once("popup-opened");
|
||||
EventUtils.sendString("b");
|
||||
await onPopUpOpen;
|
||||
is(getAutocompletePopupLabels(autocompletePopup).join("|"), `"bar"`,
|
||||
`popup has expected item`);
|
||||
|
||||
const onPopupClose = autocompletePopup.once("popup-closed");
|
||||
EventUtils.synthesizeKey("KEY_Tab");
|
||||
await onPopupClose;
|
||||
checkJsTermValueAndCursor(jsterm, `window.testObject["bar"|]`,
|
||||
`input was completed as expected, without adding a closing bracket`);
|
||||
}
|
||||
|
||||
function getAutocompletePopupLabels(autocompletePopup) {
|
||||
return autocompletePopup.items.map(i => i.label);
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that code completion works properly with `[` and cached results
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = `data:text/html;charset=utf8,<p>test [ completion cached results.
|
||||
<script>
|
||||
window.testObject = Object.create(null, Object.getOwnPropertyDescriptors({
|
||||
bar: 0,
|
||||
dataTest: 1,
|
||||
"data-test": 2,
|
||||
'da"ta"test': 3,
|
||||
"da\`ta\`test": 4,
|
||||
"da'ta'test": 5,
|
||||
"DATA-TEST": 6,
|
||||
}));
|
||||
</script>`;
|
||||
|
||||
add_task(async function() {
|
||||
// Run test with legacy JsTerm
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
|
||||
await performTests();
|
||||
// And then run it with the CodeMirror-powered one.
|
||||
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
|
||||
await performTests();
|
||||
});
|
||||
|
||||
async function performTests() {
|
||||
const {jsterm} = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Test that the autocomplete cache works with brackets");
|
||||
const {autocompletePopup} = jsterm;
|
||||
|
||||
const tests = [{
|
||||
description: "Test that it works if the user did not type a quote",
|
||||
initialInput: `window.testObject[dat`,
|
||||
expectedItems: [
|
||||
`"data-test"`,
|
||||
`"dataTest"`,
|
||||
`"DATA-TEST"`,
|
||||
],
|
||||
expectedCompletionText: `a-test"]`,
|
||||
sequence: [{
|
||||
char: "a",
|
||||
expectedItems: [
|
||||
`"data-test"`,
|
||||
`"dataTest"`,
|
||||
`"DATA-TEST"`,
|
||||
],
|
||||
expectedCompletionText: `-test"]`,
|
||||
}, {
|
||||
char: "-",
|
||||
expectedItems: [
|
||||
`"data-test"`,
|
||||
`"DATA-TEST"`
|
||||
],
|
||||
expectedCompletionText: `test"]`,
|
||||
}, {
|
||||
char: "t",
|
||||
expectedItems: [
|
||||
`"data-test"`,
|
||||
`"DATA-TEST"`
|
||||
],
|
||||
expectedCompletionText: `est"]`,
|
||||
}, {
|
||||
char: "e",
|
||||
expectedItems: [
|
||||
`"data-test"`,
|
||||
`"DATA-TEST"`
|
||||
],
|
||||
expectedCompletionText: `st"]`,
|
||||
}]
|
||||
}, {
|
||||
description: "Test that it works if the user did type a quote",
|
||||
initialInput: `window.testObject['dat`,
|
||||
expectedItems: [
|
||||
`'data-test'`,
|
||||
`'dataTest'`,
|
||||
`'DATA-TEST'`,
|
||||
],
|
||||
expectedCompletionText: `a-test']`,
|
||||
sequence: [{
|
||||
char: "a",
|
||||
expectedItems: [
|
||||
`'data-test'`,
|
||||
`'dataTest'`,
|
||||
`'DATA-TEST'`,
|
||||
],
|
||||
expectedCompletionText: `-test']`,
|
||||
}, {
|
||||
char: "-",
|
||||
expectedItems: [
|
||||
`'data-test'`,
|
||||
`'DATA-TEST'`
|
||||
],
|
||||
expectedCompletionText: `test']`,
|
||||
}, {
|
||||
char: "t",
|
||||
expectedItems: [
|
||||
`'data-test'`,
|
||||
`'DATA-TEST'`
|
||||
],
|
||||
expectedCompletionText: `est']`,
|
||||
}, {
|
||||
char: "e",
|
||||
expectedItems: [
|
||||
`'data-test'`,
|
||||
`'DATA-TEST'`
|
||||
],
|
||||
expectedCompletionText: `st']`,
|
||||
}]
|
||||
}];
|
||||
|
||||
for (const test of tests) {
|
||||
info(test.description);
|
||||
|
||||
const onPopUpOpen = autocompletePopup.once("popup-opened");
|
||||
EventUtils.sendString(test.initialInput);
|
||||
await onPopUpOpen;
|
||||
|
||||
is(getAutocompletePopupLabels(autocompletePopup).join("|"),
|
||||
test.expectedItems.join("|"), `popup has expected items, in expected order`);
|
||||
checkJsTermCompletionValue(jsterm,
|
||||
" ".repeat(test.initialInput.length) + test.expectedCompletionText,
|
||||
`completeNode has expected value`);
|
||||
for (const {char, expectedItems, expectedCompletionText} of test.sequence) {
|
||||
const onPopupUpdate = jsterm.once("autocomplete-updated");
|
||||
EventUtils.sendString(char);
|
||||
await onPopupUpdate;
|
||||
|
||||
is(getAutocompletePopupLabels(autocompletePopup).join("|"), expectedItems.join("|"),
|
||||
`popup has expected items, in expected order`);
|
||||
checkJsTermCompletionValue(jsterm,
|
||||
" ".repeat(jsterm.getInputValue().length) + expectedCompletionText,
|
||||
`completeNode has expected value`);
|
||||
}
|
||||
|
||||
jsterm.setInputValue("");
|
||||
const onPopupClose = autocompletePopup.once("popup-closed");
|
||||
EventUtils.synthesizeKey("KEY_Escape");
|
||||
await onPopupClose;
|
||||
}
|
||||
}
|
||||
|
||||
function getAutocompletePopupLabels(autocompletePopup) {
|
||||
return autocompletePopup.items.map(i => i.label);
|
||||
}
|
|
@ -507,6 +507,98 @@ const proto = {
|
|||
return { descriptor: this._propertyDescriptor(name) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to provide the value of the object's
|
||||
* specified property.
|
||||
*
|
||||
* Note: Since this will evaluate getters, it can trigger execution of
|
||||
* content code and may cause side effects. This endpoint should only be used
|
||||
* when you are confident that the side-effects will be safe, or the user
|
||||
* is expecting the effects.
|
||||
*
|
||||
* @param {string} name
|
||||
* The property we want the value of.
|
||||
*/
|
||||
propertyValue: function(name) {
|
||||
if (!name) {
|
||||
return this.throwError("missingParameter", "no property name was specified");
|
||||
}
|
||||
|
||||
const value = this.obj.getProperty(name);
|
||||
|
||||
return { value: this._buildCompletion(value) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to evaluate a function and provide the value of
|
||||
* the result.
|
||||
*
|
||||
* Note: Since this will evaluate the function, it can trigger execution of
|
||||
* content code and may cause side effects. This endpoint should only be used
|
||||
* when you are confident that the side-effects will be safe, or the user
|
||||
* is expecting the effects.
|
||||
*
|
||||
* @param {any} context
|
||||
* The 'this' value to call the function with.
|
||||
* @param {Array<any>} args
|
||||
* The array of un-decoded actor objects, or primitives.
|
||||
*/
|
||||
apply: function(context, args) {
|
||||
if (!this.obj.callable) {
|
||||
return this.throwError("notCallable", "debugee object is not callable");
|
||||
}
|
||||
|
||||
const debugeeContext = this._getValueFromGrip(context);
|
||||
const debugeeArgs = args && args.map(this._getValueFromGrip, this);
|
||||
|
||||
const value = this.obj.apply(debugeeContext, debugeeArgs);
|
||||
|
||||
return { value: this._buildCompletion(value) };
|
||||
},
|
||||
|
||||
_getValueFromGrip(grip) {
|
||||
if (typeof grip !== "object" || !grip) {
|
||||
return grip;
|
||||
}
|
||||
|
||||
if (typeof grip.actor !== "string") {
|
||||
return this.throwError("invalidGrip", "grip argument did not include actor ID");
|
||||
}
|
||||
|
||||
const actor = this.conn.getActor(grip.actor);
|
||||
|
||||
if (!actor) {
|
||||
return this.throwError("unknownActor", "grip actor did not match a known object");
|
||||
}
|
||||
|
||||
return actor.obj;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a Debugger API completion value record into an eqivalent
|
||||
* object grip for use by the API.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Tools/Debugger-API/Conventions#completion-values
|
||||
* for more specifics on the expected behavior.
|
||||
*/
|
||||
_buildCompletion(value) {
|
||||
let completionGrip = null;
|
||||
|
||||
// .apply result will be falsy if the script being executed is terminated
|
||||
// via the "slow script" dialog.
|
||||
if (value) {
|
||||
completionGrip = {};
|
||||
if ("return" in value) {
|
||||
completionGrip.return = this.hooks.createValueGrip(value.return);
|
||||
}
|
||||
if ("throw" in value) {
|
||||
completionGrip.throw = this.hooks.createValueGrip(value.throw);
|
||||
}
|
||||
}
|
||||
|
||||
return completionGrip;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to provide the display string for the object.
|
||||
*/
|
||||
|
|
|
@ -1610,7 +1610,13 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
getGripDepth: () => this._gripDepth,
|
||||
incrementGripDepth: () => this._gripDepth++,
|
||||
decrementGripDepth: () => this._gripDepth--,
|
||||
createValueGrip: v => createValueGrip(v, this._pausePool, this.pauseObjectGrip),
|
||||
createValueGrip: v => {
|
||||
if (this._pausePool) {
|
||||
return createValueGrip(v, this._pausePool, this.pauseObjectGrip);
|
||||
}
|
||||
|
||||
return createValueGrip(v, this.threadLifetimePool, this.objectGrip);
|
||||
},
|
||||
sources: () => this.sources,
|
||||
createEnvironmentActor: (e, p) => this.createEnvironmentActor(e, p),
|
||||
promote: () => this.threadObjectGrip(actor),
|
||||
|
|
|
@ -1137,6 +1137,8 @@ WebConsoleActor.prototype =
|
|||
let hadDebuggee = false;
|
||||
let matches = [];
|
||||
let matchProp;
|
||||
let isElementAccess;
|
||||
|
||||
const reqText = request.text.substr(0, request.cursor);
|
||||
|
||||
if (isCommand(reqText)) {
|
||||
|
@ -1175,12 +1177,15 @@ WebConsoleActor.prototype =
|
|||
|
||||
matches = result.matches || new Set();
|
||||
matchProp = result.matchProp;
|
||||
isElementAccess = result.isElementAccess;
|
||||
|
||||
// We consider '$' as alphanumeric because it is used in the names of some
|
||||
// helper functions; we also consider whitespace as alphanum since it should not
|
||||
// be seen as break in the evaled string.
|
||||
const lastNonAlphaIsDot = /[.][a-zA-Z0-9$\s]*$/.test(reqText);
|
||||
if (!lastNonAlphaIsDot) {
|
||||
|
||||
// We only return command when we are not dealing with a property or element access.
|
||||
if (!lastNonAlphaIsDot && !isElementAccess) {
|
||||
this._getWebConsoleCommandsCache().forEach(n => {
|
||||
// filter out `screenshot` command as it is inaccessible without the `:` prefix
|
||||
if (n !== "screenshot" && n.startsWith(result.matchProp)) {
|
||||
|
@ -1193,8 +1198,11 @@ WebConsoleActor.prototype =
|
|||
// display `document` then `Document` as we loosely match the user input if the
|
||||
// first letter they typed was lowercase).
|
||||
matches = Array.from(matches).sort((a, b) => {
|
||||
const lA = a[0].toLocaleLowerCase() === a[0];
|
||||
const lB = b[0].toLocaleLowerCase() === b[0];
|
||||
const startingQuoteRegex = /^('|"|`)/;
|
||||
const aFirstMeaningfulChar = startingQuoteRegex.test(a) ? a[1] : a[0];
|
||||
const bFirstMeaningfulChar = startingQuoteRegex.test(b) ? b[1] : b[0];
|
||||
const lA = aFirstMeaningfulChar.toLocaleLowerCase() === aFirstMeaningfulChar;
|
||||
const lB = bFirstMeaningfulChar.toLocaleLowerCase() === bFirstMeaningfulChar;
|
||||
if (lA === lB) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
@ -1206,6 +1214,7 @@ WebConsoleActor.prototype =
|
|||
from: this.actorID,
|
||||
matches,
|
||||
matchProp,
|
||||
isElementAccess: isElementAccess === true,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
await assert_object_argument(
|
||||
debuggee,
|
||||
threadClient,
|
||||
`
|
||||
stopMe({
|
||||
obj1: {},
|
||||
obj2: {},
|
||||
context(arg) {
|
||||
return this === arg ? "correct context" : "wrong context";
|
||||
},
|
||||
sum(...parts) {
|
||||
return parts.reduce((acc, v) => acc + v, 0);
|
||||
},
|
||||
error() {
|
||||
throw "an error";
|
||||
},
|
||||
});
|
||||
`,
|
||||
async objClient => {
|
||||
const obj1 = (await objClient.getPropertyValue("obj1")).value.return;
|
||||
const obj2 = (await objClient.getPropertyValue("obj2")).value.return;
|
||||
|
||||
const context = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("context")).value.return,
|
||||
);
|
||||
const sum = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("sum")).value.return,
|
||||
);
|
||||
const error = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("error")).value.return,
|
||||
);
|
||||
|
||||
assert_response(await context.apply(obj1, [obj1]), {
|
||||
return: "correct context",
|
||||
});
|
||||
assert_response(await context.apply(obj2, [obj2]), {
|
||||
return: "correct context",
|
||||
});
|
||||
assert_response(await context.apply(obj1, [obj2]), {
|
||||
return: "wrong context",
|
||||
});
|
||||
assert_response(await context.apply(obj2, [obj1]), {
|
||||
return: "wrong context",
|
||||
});
|
||||
// eslint-disable-next-line no-useless-call
|
||||
assert_response(await sum.apply(null, [1, 2, 3, 4, 5, 6, 7]), {
|
||||
return: 1 + 2 + 3 + 4 + 5 + 6 + 7,
|
||||
});
|
||||
// eslint-disable-next-line no-useless-call
|
||||
assert_response(await error.apply(null, []), {
|
||||
throw: "an error",
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function assert_object_argument(debuggee, threadClient, code, objectHandler) {
|
||||
return eval_and_resume(debuggee, threadClient, code, async frame => {
|
||||
const arg1 = frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
await objectHandler(threadClient.pauseGrip(arg1));
|
||||
});
|
||||
}
|
||||
|
||||
function eval_and_resume(debuggee, threadClient, code, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wait_for_pause(threadClient, callback).then(resolve, reject);
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function wait_for_pause(threadClient, callback = () => {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
return await callback(packet.frame);
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function assert_response({ value }, expected) {
|
||||
assert_completion(value, expected);
|
||||
}
|
||||
|
||||
function assert_completion(value, expected) {
|
||||
if (expected && "return" in expected) {
|
||||
assert_value(value.return, expected.return);
|
||||
}
|
||||
if (expected && "throw" in expected) {
|
||||
assert_value(value.throw, expected.throw);
|
||||
}
|
||||
if (!expected) {
|
||||
assert_value(value, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function assert_value(actual, expected) {
|
||||
Assert.equal(typeof actual, typeof expected);
|
||||
|
||||
if (typeof expected === "object") {
|
||||
// Note: We aren't using deepEqual here because we're only doing a cursory
|
||||
// check of a few properties, not a full comparison of the result, since
|
||||
// the full outputs includes stuff like preview info that we don't need.
|
||||
for (const key of Object.keys(expected)) {
|
||||
assert_value(actual[key], expected[key]);
|
||||
}
|
||||
} else {
|
||||
Assert.equal(actual, expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
const code = `
|
||||
stopMe({
|
||||
method(){
|
||||
debugger;
|
||||
},
|
||||
});
|
||||
`;
|
||||
const obj = await eval_and_resume(debuggee, threadClient, code, async frame => {
|
||||
const arg1 = frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
await threadClient.pauseGrip(arg1).threadGrip();
|
||||
return arg1;
|
||||
});
|
||||
const objClient = threadClient.pauseGrip(obj);
|
||||
|
||||
const method = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("method")).value.return,
|
||||
);
|
||||
|
||||
// Ensure that we actually paused at the `debugger;` line.
|
||||
await Promise.all([
|
||||
wait_for_pause(threadClient, frame => {
|
||||
Assert.equal(frame.where.line, 4);
|
||||
Assert.equal(frame.where.column, 8);
|
||||
}),
|
||||
method.apply(obj, []),
|
||||
]);
|
||||
}
|
||||
|
||||
function eval_and_resume(debuggee, threadClient, code, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wait_for_pause(threadClient, callback).then(resolve, reject);
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function wait_for_pause(threadClient, callback = () => {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
return await callback(packet.frame);
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
|
||||
// TODO: The server currently doesn't handle request errors
|
||||
// in workers well, so this test doesn't work with the worker server.
|
||||
// await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
const code = `
|
||||
stopMe({
|
||||
method: {},
|
||||
});
|
||||
`;
|
||||
const obj = await eval_and_resume(debuggee, threadClient, code, async frame => {
|
||||
const arg1 = frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
await threadClient.pauseGrip(arg1).threadGrip();
|
||||
return arg1;
|
||||
});
|
||||
const objClient = threadClient.pauseGrip(obj);
|
||||
|
||||
const method = threadClient.pauseGrip(
|
||||
(await objClient.getPropertyValue("method")).value.return,
|
||||
);
|
||||
|
||||
try {
|
||||
await method.apply(obj, []);
|
||||
Assert.ok(false, "expected exception");
|
||||
} catch (err) {
|
||||
Assert.equal(err.message, "debugee object is not callable");
|
||||
}
|
||||
}
|
||||
|
||||
function eval_and_resume(debuggee, threadClient, code, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wait_for_pause(threadClient, callback).then(resolve, reject);
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function wait_for_pause(threadClient, callback = () => {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
return await callback(packet.frame);
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
await assert_object_argument(
|
||||
debuggee,
|
||||
threadClient,
|
||||
`
|
||||
var obj = {
|
||||
stringProp: "a value",
|
||||
get stringNormal(){
|
||||
return "a value";
|
||||
},
|
||||
get stringAbrupt() {
|
||||
throw "a value";
|
||||
},
|
||||
get objectNormal() {
|
||||
return { prop: 4 };
|
||||
},
|
||||
get objectAbrupt() {
|
||||
throw { prop: 4 };
|
||||
},
|
||||
get context(){
|
||||
return this === obj ? "correct context" : "wrong context";
|
||||
},
|
||||
method() {
|
||||
return "a value";
|
||||
},
|
||||
};
|
||||
stopMe(obj);
|
||||
`,
|
||||
async objClient => {
|
||||
const expectedValues = {
|
||||
stringProp: {
|
||||
return: "a value",
|
||||
},
|
||||
stringNormal: {
|
||||
return: "a value",
|
||||
},
|
||||
stringAbrupt: {
|
||||
throw: "a value",
|
||||
},
|
||||
objectNormal: {
|
||||
return: {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
ownPropertyLength: 1,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {
|
||||
prop: {
|
||||
value: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
objectAbrupt: {
|
||||
throw: {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
ownPropertyLength: 1,
|
||||
preview: {
|
||||
kind: "Object",
|
||||
ownProperties: {
|
||||
prop: {
|
||||
value: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
context: {
|
||||
return: "correct context",
|
||||
},
|
||||
method: {
|
||||
return: {
|
||||
type: "object",
|
||||
class: "Function",
|
||||
name: "method",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const [key, expected] of Object.entries(expectedValues)) {
|
||||
const { value } = await objClient.getPropertyValue(key);
|
||||
|
||||
assert_completion(value, expected);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function assert_object_argument(debuggee, threadClient, code, objectHandler) {
|
||||
return eval_and_resume(debuggee, threadClient, code, async frame => {
|
||||
const arg1 = frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
await objectHandler(threadClient.pauseGrip(arg1));
|
||||
});
|
||||
}
|
||||
|
||||
function eval_and_resume(debuggee, threadClient, code, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wait_for_pause(threadClient, callback).then(resolve, reject);
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function wait_for_pause(threadClient, callback = () => {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
return await callback(packet.frame);
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function assert_completion(value, expected) {
|
||||
if (expected && "return" in expected) {
|
||||
assert_value(value.return, expected.return);
|
||||
}
|
||||
if (expected && "throw" in expected) {
|
||||
assert_value(value.throw, expected.throw);
|
||||
}
|
||||
if (!expected) {
|
||||
assert_value(value, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function assert_value(actual, expected) {
|
||||
Assert.equal(typeof actual, typeof expected);
|
||||
|
||||
if (typeof expected === "object") {
|
||||
// Note: We aren't using deepEqual here because we're only doing a cursory
|
||||
// check of a few properties, not a full comparison of the result, since
|
||||
// the full outputs includes stuff like preview info that we don't need.
|
||||
for (const key of Object.keys(expected)) {
|
||||
assert_value(actual[key], expected[key]);
|
||||
}
|
||||
} else {
|
||||
Assert.equal(actual, expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow, max-nested-callbacks */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function run_test() {
|
||||
try {
|
||||
do_test_pending();
|
||||
await run_test_with_server(DebuggerServer);
|
||||
await run_test_with_server(WorkerDebuggerServer);
|
||||
} finally {
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
async function run_test_with_server(server) {
|
||||
initTestDebuggerServer(server);
|
||||
const debuggee = addTestGlobal("test-grips", server);
|
||||
debuggee.eval(`
|
||||
function stopMe(arg1) {
|
||||
debugger;
|
||||
}
|
||||
`);
|
||||
|
||||
const dbgClient = new DebuggerClient(server.connectPipe());
|
||||
await dbgClient.connect();
|
||||
const [,, threadClient] = await attachTestTabAndResume(dbgClient, "test-grips");
|
||||
|
||||
await test_object_grip(debuggee, threadClient);
|
||||
|
||||
await dbgClient.close();
|
||||
}
|
||||
|
||||
async function test_object_grip(debuggee, threadClient) {
|
||||
const code = `
|
||||
stopMe({
|
||||
get prop(){
|
||||
debugger;
|
||||
},
|
||||
});
|
||||
`;
|
||||
const objClient = await eval_and_resume(debuggee, threadClient, code, async frame => {
|
||||
const arg1 = frame.arguments[0];
|
||||
Assert.equal(arg1.class, "Object");
|
||||
|
||||
const obj = threadClient.pauseGrip(arg1);
|
||||
await obj.threadGrip();
|
||||
return obj;
|
||||
});
|
||||
|
||||
// Ensure that we actually paused at the `debugger;` line.
|
||||
await Promise.all([
|
||||
wait_for_pause(threadClient, frame => {
|
||||
Assert.equal(frame.where.line, 4);
|
||||
Assert.equal(frame.where.column, 8);
|
||||
}),
|
||||
objClient.getPropertyValue("prop"),
|
||||
]);
|
||||
}
|
||||
|
||||
function eval_and_resume(debuggee, threadClient, code, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wait_for_pause(threadClient, callback).then(resolve, reject);
|
||||
|
||||
// This synchronously blocks until 'threadClient.resume()' above runs
|
||||
// because the 'paused' event runs everthing in a new event loop.
|
||||
debuggee.eval(code);
|
||||
});
|
||||
}
|
||||
|
||||
function wait_for_pause(threadClient, callback = () => {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
threadClient.addOneTimeListener("paused", function(event, packet) {
|
||||
(async () => {
|
||||
try {
|
||||
return await callback(packet.frame);
|
||||
} finally {
|
||||
await threadClient.resume();
|
||||
}
|
||||
})().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -177,6 +177,11 @@ reason = bug 1104838
|
|||
[test_objectgrips-21.js]
|
||||
[test_objectgrips-22.js]
|
||||
[test_objectgrips-array-like-object.js]
|
||||
[test_objectgrips-property-value-01.js]
|
||||
[test_objectgrips-property-value-02.js]
|
||||
[test_objectgrips-fn-apply-01.js]
|
||||
[test_objectgrips-fn-apply-02.js]
|
||||
[test_objectgrips-fn-apply-03.js]
|
||||
[test_promise_state-01.js]
|
||||
[test_promise_state-02.js]
|
||||
[test_promise_state-03.js]
|
||||
|
|
|
@ -42,6 +42,10 @@ ObjectClient.prototype = {
|
|||
return this._grip.extensible;
|
||||
},
|
||||
|
||||
threadGrip: DebuggerClient.requester({
|
||||
type: "threadGrip",
|
||||
}),
|
||||
|
||||
getDefinitionSite: DebuggerClient.requester({
|
||||
type: "definitionSite"
|
||||
}, {
|
||||
|
@ -181,6 +185,17 @@ ObjectClient.prototype = {
|
|||
name: arg(0)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the value of the object's specified property.
|
||||
*
|
||||
* @param name string The name of the requested property.
|
||||
* @param onResponse function Called with the request's response.
|
||||
*/
|
||||
getPropertyValue: DebuggerClient.requester({
|
||||
type: "propertyValue",
|
||||
name: arg(0)
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the prototype of the object.
|
||||
*
|
||||
|
@ -190,6 +205,19 @@ ObjectClient.prototype = {
|
|||
type: "prototype"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Evaluate a callable object with context and arguments.
|
||||
*
|
||||
* @param context any The value to use as the function context.
|
||||
* @param arguments Array<any> An array of values to use as the function's arguments.
|
||||
* @param onResponse function Called with the request's response.
|
||||
*/
|
||||
apply: DebuggerClient.requester({
|
||||
type: "apply",
|
||||
context: arg(0),
|
||||
arguments: arg(1),
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the display string of the object.
|
||||
*
|
||||
|
|
|
@ -24,6 +24,11 @@ types.addDictType("object.descriptor", {
|
|||
set: "nullable:json",
|
||||
});
|
||||
|
||||
types.addDictType("object.completion", {
|
||||
return: "nullable:json",
|
||||
throw: "nullable:json"
|
||||
});
|
||||
|
||||
types.addDictType("object.definitionSite", {
|
||||
source: "source",
|
||||
line: "number",
|
||||
|
@ -45,9 +50,17 @@ types.addDictType("object.property", {
|
|||
descriptor: "nullable:object.descriptor"
|
||||
});
|
||||
|
||||
types.addDictType("object.propertyValue", {
|
||||
value: "nullable:object.completion"
|
||||
});
|
||||
|
||||
types.addDictType("object.apply", {
|
||||
value: "nullable:object.completion"
|
||||
});
|
||||
|
||||
types.addDictType("object.bindings", {
|
||||
arguments: "array:json",
|
||||
variables: "json",
|
||||
variables: "json"
|
||||
});
|
||||
|
||||
types.addDictType("object.scope", {
|
||||
|
@ -165,6 +178,19 @@ const objectSpec = generateActorSpec({
|
|||
},
|
||||
response: RetVal("object.property")
|
||||
},
|
||||
propertyValue: {
|
||||
request: {
|
||||
name: Arg(0, "string")
|
||||
},
|
||||
response: RetVal("object.propertyValue")
|
||||
},
|
||||
apply: {
|
||||
request: {
|
||||
context: Arg(0, "nullable:json"),
|
||||
arguments: Arg(1, "nullable:array:json"),
|
||||
},
|
||||
response: RetVal("object.apply")
|
||||
},
|
||||
rejectionStack: {
|
||||
request: {},
|
||||
response: {
|
||||
|
|
|
@ -52,10 +52,12 @@ function hasArrayIndex(str) {
|
|||
*
|
||||
* {
|
||||
* state: STATE_NORMAL|STATE_QUOTE|STATE_DQUOTE,
|
||||
* lastStatement: the last statement in the string
|
||||
* lastStatement: the last statement in the string,
|
||||
* isElementAccess: boolean that indicates if the lastStatement has an open
|
||||
* element access (e.g. `x["match`).
|
||||
* }
|
||||
*/
|
||||
function findCompletionBeginning(str) {
|
||||
function analyzeInputString(str) {
|
||||
const bodyStack = [];
|
||||
|
||||
let state = STATE_NORMAL;
|
||||
|
@ -109,7 +111,7 @@ function findCompletionBeginning(str) {
|
|||
} else if (OPEN_BODY.includes(c)) {
|
||||
bodyStack.push({
|
||||
token: c,
|
||||
start: start
|
||||
start
|
||||
});
|
||||
start = i + 1;
|
||||
} else if (CLOSE_BODY.includes(c)) {
|
||||
|
@ -164,9 +166,19 @@ function findCompletionBeginning(str) {
|
|||
}
|
||||
}
|
||||
|
||||
let isElementAccess = false;
|
||||
if (bodyStack.length === 1 && bodyStack[0].token === "[") {
|
||||
start = bodyStack[0].start;
|
||||
isElementAccess = true;
|
||||
if ([STATE_DQUOTE, STATE_QUOTE, STATE_TEMPLATE_LITERAL].includes(state)) {
|
||||
state = STATE_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state: state,
|
||||
lastStatement: characters.slice(start).join("")
|
||||
state,
|
||||
lastStatement: characters.slice(start).join(""),
|
||||
isElementAccess,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -195,6 +207,8 @@ function findCompletionBeginning(str) {
|
|||
* matches: Set<string>
|
||||
* matchProp: Last part of the inputValue that was used to find
|
||||
* the matches-strings.
|
||||
* isElementAccess: Boolean set to true if the evaluation is an element
|
||||
* access (e.g. `window["addEvent`).
|
||||
* }
|
||||
*/
|
||||
function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
||||
|
@ -206,7 +220,12 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
|||
|
||||
// Analyse the inputValue and find the beginning of the last part that
|
||||
// should be completed.
|
||||
const {err, state, lastStatement} = findCompletionBeginning(inputValue);
|
||||
const {
|
||||
err,
|
||||
state,
|
||||
lastStatement,
|
||||
isElementAccess
|
||||
} = analyzeInputString(inputValue);
|
||||
|
||||
// There was an error analysing the string.
|
||||
if (err) {
|
||||
|
@ -218,23 +237,24 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
|||
if (state != STATE_NORMAL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const completionPart = lastStatement;
|
||||
const lastDot = completionPart.lastIndexOf(".");
|
||||
const lastDotIndex = completionPart.lastIndexOf(".");
|
||||
const lastOpeningBracketIndex = isElementAccess ? completionPart.lastIndexOf("[") : -1;
|
||||
const lastCompletionCharIndex = Math.max(lastDotIndex, lastOpeningBracketIndex);
|
||||
const startQuoteRegex = /^('|"|`)/;
|
||||
|
||||
// Don't complete on just an empty string.
|
||||
if (completionPart.trim() == "") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Catch literals like [1,2,3] or "foo" and return the matches from
|
||||
// their prototypes.
|
||||
// Don't run this is a worker, migrating to acorn should allow this
|
||||
// to run in a worker - Bug 1217198.
|
||||
if (!isWorker && lastDot > 0) {
|
||||
if (!isWorker && lastCompletionCharIndex > 0) {
|
||||
const parser = new Parser();
|
||||
parser.logExceptions = false;
|
||||
const syntaxTree = parser.get(completionPart.slice(0, lastDot));
|
||||
const syntaxTree = parser.get(completionPart.slice(0, lastCompletionCharIndex));
|
||||
const lastTree = syntaxTree.getLastSyntaxTree();
|
||||
const lastBody = lastTree && lastTree.AST.body[lastTree.AST.body.length - 1];
|
||||
|
||||
|
@ -242,19 +262,62 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
|||
// If there were parse errors this won't exist.
|
||||
if (lastBody) {
|
||||
const expression = lastBody.expression;
|
||||
const matchProp = completionPart.slice(lastDot + 1).trimLeft();
|
||||
const matchProp = completionPart.slice(lastCompletionCharIndex + 1).trimLeft();
|
||||
let search = matchProp;
|
||||
|
||||
let elementAccessQuote;
|
||||
if (isElementAccess && startQuoteRegex.test(matchProp)) {
|
||||
elementAccessQuote = matchProp[0];
|
||||
search = matchProp.replace(startQuoteRegex, "");
|
||||
}
|
||||
|
||||
if (expression.type === "ArrayExpression") {
|
||||
return getMatchedProps(Array.prototype, matchProp);
|
||||
} else if (expression.type === "Literal" &&
|
||||
(typeof expression.value === "string")) {
|
||||
return getMatchedProps(String.prototype, matchProp);
|
||||
let arrayProtoProps = getMatchedProps(Array.prototype, search);
|
||||
if (isElementAccess) {
|
||||
arrayProtoProps = wrapMatchesInQuotes(arrayProtoProps, elementAccessQuote);
|
||||
}
|
||||
|
||||
return {
|
||||
isElementAccess,
|
||||
matchProp,
|
||||
matches: arrayProtoProps
|
||||
};
|
||||
}
|
||||
|
||||
if (expression.type === "Literal" && typeof expression.value === "string") {
|
||||
let stringProtoProps = getMatchedProps(String.prototype, search);
|
||||
if (isElementAccess) {
|
||||
stringProtoProps = wrapMatchesInQuotes(stringProtoProps, elementAccessQuote);
|
||||
}
|
||||
|
||||
return {
|
||||
isElementAccess,
|
||||
matchProp,
|
||||
matches: stringProtoProps,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We are completing a variable / a property lookup.
|
||||
const properties = completionPart.split(".");
|
||||
const matchProp = properties.pop().trimLeft();
|
||||
let matchProp;
|
||||
if (isElementAccess) {
|
||||
const lastPart = properties[properties.length - 1];
|
||||
const openBracketIndex = lastPart.lastIndexOf("[");
|
||||
matchProp = lastPart.substr(openBracketIndex + 1);
|
||||
properties[properties.length - 1] = lastPart.substring(0, openBracketIndex);
|
||||
} else {
|
||||
matchProp = properties.pop().trimLeft();
|
||||
}
|
||||
|
||||
let search = matchProp;
|
||||
let elementAccessQuote;
|
||||
if (isElementAccess && startQuoteRegex.test(search)) {
|
||||
elementAccessQuote = search[0];
|
||||
search = search.replace(startQuoteRegex, "");
|
||||
}
|
||||
|
||||
let obj = dbgObject;
|
||||
|
||||
// The first property must be found in the environment of the paused debugger
|
||||
|
@ -262,7 +325,11 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
|||
const env = anEnvironment || obj.asEnvironment();
|
||||
|
||||
if (properties.length === 0) {
|
||||
return getMatchedPropsInEnvironment(env, matchProp);
|
||||
return {
|
||||
isElementAccess,
|
||||
matchProp,
|
||||
matches: getMatchedPropsInEnvironment(env, search)
|
||||
};
|
||||
}
|
||||
|
||||
const firstProp = properties.shift().trim();
|
||||
|
@ -286,8 +353,8 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
|||
|
||||
// We get the rest of the properties recursively starting from the
|
||||
// Debugger.Object that wraps the first property
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const prop = properties[i].trim();
|
||||
for (let prop of properties) {
|
||||
prop = prop.trim();
|
||||
if (!prop) {
|
||||
return null;
|
||||
}
|
||||
|
@ -307,10 +374,25 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
|
|||
|
||||
// If the final property is a primitive
|
||||
if (typeof obj != "object") {
|
||||
return getMatchedProps(obj, matchProp);
|
||||
return {
|
||||
isElementAccess,
|
||||
matchProp,
|
||||
matches: getMatchedProps(obj, search)
|
||||
};
|
||||
}
|
||||
|
||||
return getMatchedPropsInDbgObject(obj, matchProp);
|
||||
let matches = getMatchedPropsInDbgObject(obj, search);
|
||||
if (isElementAccess) {
|
||||
// If it's an element access, we need to wrap properties in quotes (either the one
|
||||
// the user already typed, or `"`).
|
||||
matches = wrapMatchesInQuotes(matches, elementAccessQuote);
|
||||
}
|
||||
return {isElementAccess, matchProp, matches};
|
||||
}
|
||||
|
||||
function wrapMatchesInQuotes(matches, quote = `"`) {
|
||||
return new Set([...matches].map(p =>
|
||||
`${quote}${p.replace(new RegExp(`${quote}`, "g"), `\\${quote}`)}${quote}`));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -423,9 +505,7 @@ function getMatchedProps(obj, match) {
|
|||
* Object whose properties we want to filter.
|
||||
* @param {string} match
|
||||
* Filter for properties that match this string.
|
||||
* @returns {object} which holds the following properties:
|
||||
* - {string} matchProp.
|
||||
* - {Set} matches: List of matched properties.
|
||||
* @returns {Set} List of matched properties.
|
||||
*/
|
||||
function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
|
||||
const matches = new Set();
|
||||
|
@ -460,9 +540,7 @@ function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
|
|||
if (!propertyMatches(prop)) {
|
||||
continue;
|
||||
}
|
||||
if (prop.indexOf("-") > -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it is an array index, we can't take it.
|
||||
// This uses a trick: converting a string to a number yields NaN if
|
||||
// the operation failed, and NaN is not equal to itself.
|
||||
|
@ -477,10 +555,7 @@ function getMatchedPropsImpl(obj, match, {chainIterator, getProperties}) {
|
|||
}
|
||||
}
|
||||
|
||||
return {
|
||||
matchProp: match,
|
||||
matches,
|
||||
};
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,6 +75,16 @@
|
|||
PRÖP: "",
|
||||
pröp: "",
|
||||
}));
|
||||
|
||||
window.elementAccessTestCase = Object.create(null, Object.getOwnPropertyDescriptors({
|
||||
bar: "",
|
||||
BAR: "",
|
||||
dataTest: "",
|
||||
"data-test": "",
|
||||
'da"ta"test': "",
|
||||
'da\`ta\`test': "",
|
||||
"da'ta'test": "",
|
||||
}));
|
||||
`;
|
||||
await state.client.evaluateJSAsync(script);
|
||||
|
||||
|
@ -90,13 +100,18 @@
|
|||
doAutocompleteDotSurroundedBySpaces,
|
||||
doAutocompleteAfterOr,
|
||||
doInsensitiveAutocomplete,
|
||||
doElementAccessAutocomplete,
|
||||
];
|
||||
|
||||
if (!isWorker) {
|
||||
// `Cu` is not defined in workers, then we can't test `Cu.Sandbox`
|
||||
tests.push(doAutocompleteSandbox);
|
||||
// Array literal completion isn't handled in Workers yet.
|
||||
tests.push(doAutocompleteArray);
|
||||
// Array literal, string and commands completion aren't handled in Workers yet.
|
||||
tests.push(
|
||||
doAutocompleteArray,
|
||||
doAutocompleteString,
|
||||
doAutocompleteCommands,
|
||||
);
|
||||
}
|
||||
|
||||
for (const test of tests) {
|
||||
|
@ -210,6 +225,46 @@
|
|||
ok(matches.includes("length") && matches.includes("filter"),
|
||||
"Array autocomplete contains expected results");
|
||||
ok(!matches.includes("copy"), "Array autocomplete does not contain helpers");
|
||||
|
||||
info("test autocomplete for '[1,2,3]['");
|
||||
matches = (await client.autocomplete("[1,2,3][")).matches;
|
||||
ok(matches.length > 1);
|
||||
ok(matches.includes('"length"') && matches.includes('"filter"'),
|
||||
"Array autocomplete contains expected results, surrounded by quotes");
|
||||
|
||||
info("test autocomplete for '[1,2,3]['");
|
||||
matches = (await client.autocomplete("[1,2,3]['")).matches;
|
||||
ok(matches.length > 1);
|
||||
ok(matches.includes("'length'") && matches.includes("'filter'"),
|
||||
"Array autocomplete contains expected results, surrounded by quotes");
|
||||
|
||||
info("test autocomplete for '[1,2,3][l");
|
||||
matches = (await client.autocomplete("[1,2,3][l")).matches;
|
||||
ok(matches.length >= 1);
|
||||
ok(matches.includes('"length"'),
|
||||
"Array autocomplete contains expected results, surrounded by quotes");
|
||||
|
||||
info("test autocomplete for '[1,2,3]['l");
|
||||
matches = (await client.autocomplete("[1,2,3]['l")).matches;
|
||||
ok(matches.length >= 1);
|
||||
ok(matches.includes("'length'"),
|
||||
"Array autocomplete contains expected results, surrounded by quotes");
|
||||
}
|
||||
|
||||
async function doAutocompleteString(client) {
|
||||
info(`test autocomplete for "foo".`);
|
||||
let response = await client.autocomplete(`"foo".`);
|
||||
let {matches} = response;
|
||||
|
||||
ok(matches.length > 0, "There are completion results for the string");
|
||||
ok(matches.includes("substr") && matches.includes("trim"),
|
||||
"String autocomplete contains expected results");
|
||||
|
||||
info("test autocomplete for `foo`[");
|
||||
matches = (await client.autocomplete("`foo`[")).matches;
|
||||
ok(matches.length > 1, "autocomplete string with bracket works");
|
||||
ok(matches.includes('"substr"') && matches.includes('"trim"'),
|
||||
"String autocomplete contains expected results, surrounded by quotes");
|
||||
}
|
||||
|
||||
async function doAutocompleteDotSurroundedBySpaces(client) {
|
||||
|
@ -287,6 +342,112 @@
|
|||
matches = (await client.autocomplete("window.insensitiveTestCase.PRÖ")).matches;
|
||||
is(matches.join("-"), "PRÖP", "expected result with uppercase diacritic");
|
||||
}
|
||||
|
||||
async function doElementAccessAutocomplete(client) {
|
||||
info("test autocomplete for 'window.elementAccessTestCase['");
|
||||
let res = (await client.autocomplete("window.elementAccessTestCase["));
|
||||
is(
|
||||
res.matches.join("|"),
|
||||
`"bar"|"da'ta'test"|"da\\"ta\\"test"|"da\`ta\`test"|"data-test"|"dataTest"|"BAR"`,
|
||||
"autocomplete returns the expected items, wrapped in quotes");
|
||||
is(res.isElementAccess, true);
|
||||
|
||||
info("test autocomplete for 'window.elementAccessTestCase[d'");
|
||||
res = await client.autocomplete("window.elementAccessTestCase[d");
|
||||
is(
|
||||
res.matches.join("|"),
|
||||
`"da'ta'test"|"da\\"ta\\"test"|"da\`ta\`test"|"data-test"|"dataTest"`,
|
||||
"autocomplete returns the expected filtered items");
|
||||
is(res.isElementAccess, true);
|
||||
|
||||
info(`test autocomplete for 'window.elementAccessTestCase["d'`);
|
||||
res = await client.autocomplete(`window.elementAccessTestCase["d`);
|
||||
is(
|
||||
res.matches.join("|"),
|
||||
`"da'ta'test"|"da\\"ta\\"test"|"da\`ta\`test"|"data-test"|"dataTest"`,
|
||||
"autocomplete returns the expected items, wrapped in quotes");
|
||||
is(res.isElementAccess, true);
|
||||
|
||||
info(`test autocomplete for 'window.elementAccessTestCase["data-`);
|
||||
res = await client.autocomplete(`window.elementAccessTestCase["data-`);
|
||||
is(res.matches.join("|"), `"data-test"`,
|
||||
"autocomplete returns the expected items, wrapped in quotes");
|
||||
is(res.isElementAccess, true);
|
||||
|
||||
info(`test autocomplete for 'window.elementAccessTestCase['d'`);
|
||||
res = await client.autocomplete(`window.elementAccessTestCase['d`);
|
||||
is(
|
||||
res.matches.join("|"),
|
||||
`'da"ta"test'|'da\\'ta\\'test'|'da\`ta\`test'|'data-test'|'dataTest'`,
|
||||
"autocomplete returns the expected items, wrapped in the same quotes the user entered");
|
||||
is(res.isElementAccess, true);
|
||||
|
||||
info("test autocomplete for 'window.elementAccessTestCase[`d'");
|
||||
res = await client.autocomplete("window.elementAccessTestCase[`d");
|
||||
is(
|
||||
res.matches.join("|"),
|
||||
"`da\"ta\"test`|`da'ta'test`|`da\\`ta\\`test`|`data-test`|`dataTest`",
|
||||
"autocomplete returns the expected items, wrapped in the same quotes the user entered");
|
||||
is(res.isElementAccess, true);
|
||||
|
||||
info(`test autocomplete for '['`);
|
||||
res = await client.autocomplete(`[`);
|
||||
is(res.matches.length, 0, "it does not return anything");
|
||||
is(res.isElementAccess, false);
|
||||
|
||||
info(`test autocomplete for '[1,2,3'`);
|
||||
res = await client.autocomplete(`[1,2,3`);
|
||||
is(res.matches.length, 0, "it does not return anything");
|
||||
is(res.isElementAccess, false);
|
||||
|
||||
info(`test autocomplete for '["'`);
|
||||
res = await client.autocomplete(`["`);
|
||||
is(res.matches.length, 0, "it does not return anything");
|
||||
is(res.isElementAccess, false);
|
||||
|
||||
info(`test autocomplete for '[;'`);
|
||||
res = await client.autocomplete(`[;`);
|
||||
is(res.matches.length, 0, "it does not return anything");
|
||||
is(res.isElementAccess, false);
|
||||
}
|
||||
|
||||
async function doAutocompleteCommands(client) {
|
||||
info("test autocomplete for 'c'");
|
||||
let matches = (await client.autocomplete("c")).matches;
|
||||
ok(matches.includes("cd") && matches.includes("clear"), "commands are returned");
|
||||
|
||||
info("test autocomplete for 's'");
|
||||
matches = (await client.autocomplete("s")).matches;
|
||||
is(matches.includes("screenshot"), false, "screenshot is not returned");
|
||||
|
||||
info("test autocomplete for ':s'");
|
||||
matches = (await client.autocomplete(":s")).matches;
|
||||
is(matches.includes(":screenshot"), true, "screenshot is returned");
|
||||
|
||||
info("test autocomplete for 'window.c'");
|
||||
matches = (await client.autocomplete("window.c")).matches;
|
||||
ok(!matches.includes("cd") && !matches.includes("clear"), "commands are not returned");
|
||||
|
||||
info("test autocomplete for 'window[c'");
|
||||
matches = (await client.autocomplete("window[c")).matches;
|
||||
ok(!matches.includes("cd") && !matches.includes("clear"), "commands are not returned");
|
||||
|
||||
info(`test autocomplete for 'window["c'`);
|
||||
matches = (await client.autocomplete(`window["c`)).matches;
|
||||
ok(!matches.includes("cd") && !matches.includes("clear"), "commands are not returned");
|
||||
|
||||
info(`test autocomplete for 'window["c'`);
|
||||
matches = (await client.autocomplete(`window["c`)).matches;
|
||||
ok(!matches.includes("cd") && !matches.includes("clear"), "commands are not returned");
|
||||
|
||||
info(`test autocomplete for 'window[";c'`);
|
||||
matches = (await client.autocomplete(`window[";c`)).matches;
|
||||
ok(!matches.includes("cd") && !matches.includes("clear"), "commands are not returned");
|
||||
|
||||
info(`test autocomplete for 'window[;c'`);
|
||||
matches = (await client.autocomplete(`window[;c`)).matches;
|
||||
ok(!matches.includes("cd") && !matches.includes("clear"), "commands are not returned");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2251,7 +2251,6 @@ nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI)
|
|||
MOZ_ASSERT(!mIsBeingDestroyed);
|
||||
|
||||
mSecurityUI = aSecurityUI;
|
||||
mSecurityUI->SetDocShell(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ Location::GetURI(nsIURI** aURI, bool aGetInnermostURI)
|
|||
*aURI = nullptr;
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
|
||||
if (!mDocShell) {
|
||||
if (!docShell) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -1624,6 +1624,26 @@ nsDOMWindowUtils::GetScrollXYFloat(bool aFlushLayout, float* aScrollX, float* aS
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetVisualViewportOffsetRelativeToLayoutViewport(float* aOffsetX, float* aOffsetY)
|
||||
{
|
||||
*aOffsetX = 0;
|
||||
*aOffsetY = 0;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = GetDocument();
|
||||
NS_ENSURE_STATE(doc);
|
||||
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsPoint offset = presShell->GetVisualViewportOffsetRelativeToLayoutViewport();
|
||||
*aOffsetX = nsPresContext::AppUnitsToFloatCSSPixels(offset.x);
|
||||
*aOffsetY = nsPresContext::AppUnitsToFloatCSSPixels(offset.y);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetScrollbarSize(bool aFlushLayout, int32_t* aWidth,
|
||||
int32_t* aHeight)
|
||||
|
|
|
@ -254,7 +254,7 @@
|
|||
.createInstance(Ci.nsISecureBrowserUI);
|
||||
|
||||
try {
|
||||
secureUI.setDocShell(docshell);
|
||||
secureUI.init(docshell);
|
||||
ok(false, "expected exception passing CPOW to C++");
|
||||
} catch (e) {
|
||||
is(e.result, Cr.NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,
|
||||
|
|
|
@ -148,7 +148,7 @@ BrowserElementChild.prototype = {
|
|||
// This is necessary to get security web progress notifications.
|
||||
var securityUI = Cc['@mozilla.org/secure_browser_ui;1']
|
||||
.createInstance(Ci.nsISecureBrowserUI);
|
||||
securityUI.init(content);
|
||||
securityUI.init(docShell);
|
||||
|
||||
// A cache of the menuitem dom objects keyed by the id we generate
|
||||
// and pass to the embedder
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
width="640" height="480">
|
||||
|
||||
<browser id="browser" type="content" primary="true" flex="1" src="about:blank"
|
||||
disablehistory="true" disablesecurity="true"/>
|
||||
disablehistory="true"/>
|
||||
|
||||
</window>
|
||||
|
|
|
@ -844,6 +844,13 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
*/
|
||||
DOMRect getBoundsWithoutFlushing(in Element aElement);
|
||||
|
||||
/**
|
||||
* Returns the offset of the window's visual viewport relative to the
|
||||
* layout viewport.
|
||||
*/
|
||||
void getVisualViewportOffsetRelativeToLayoutViewport(out float aOffsetX,
|
||||
out float aOffsetY);
|
||||
|
||||
const long FLUSH_NONE = -1;
|
||||
const long FLUSH_STYLE = 0;
|
||||
const long FLUSH_LAYOUT = 1;
|
||||
|
|
|
@ -133,7 +133,6 @@ TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
|
|||
NS_IMPL_ISUPPORTS(TabParent,
|
||||
nsITabParent,
|
||||
nsIAuthPromptProvider,
|
||||
nsISecureBrowserUI,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
TabParent::TabParent(nsIContentParent* aManager,
|
||||
|
@ -878,38 +877,6 @@ TabParent::Deactivate()
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::Init(mozIDOMWindowProxy *window)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::GetState(uint32_t *aState)
|
||||
{
|
||||
NS_ENSURE_ARG(aState);
|
||||
NS_WARNING("SecurityState not valid here");
|
||||
*aState = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::GetSecInfo(nsITransportSecurityInfo** _result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_result);
|
||||
NS_WARNING("TransportSecurityInfo not valid here");
|
||||
*_result = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::SetDocShell(nsIDocShell *aDocShell)
|
||||
{
|
||||
NS_ENSURE_ARG(aDocShell);
|
||||
NS_WARNING("No mDocShell member in TabParent so there is no docShell to set");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
a11y::PDocAccessibleParent*
|
||||
TabParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent,
|
||||
const uint64_t&, const uint32_t&,
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "nsIBrowserDOMWindow.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIKeyEventInPluginCallback.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsITabParent.h"
|
||||
#include "nsIXULBrowserWindow.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
@ -83,7 +82,6 @@ class TabParent final : public PBrowserParent
|
|||
, public nsIDOMEventListener
|
||||
, public nsITabParent
|
||||
, public nsIAuthPromptProvider
|
||||
, public nsISecureBrowserUI
|
||||
, public nsIKeyEventInPluginCallback
|
||||
, public nsSupportsWeakReference
|
||||
, public TabContext
|
||||
|
@ -487,7 +485,6 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIAUTHPROMPTPROVIDER
|
||||
NS_DECL_NSISECUREBROWSERUI
|
||||
|
||||
void StartPersistence(uint64_t aOuterWindowID,
|
||||
nsIWebBrowserPersistDocumentReceiver* aRecv,
|
||||
|
|
|
@ -684,6 +684,32 @@ ConvertToCdmEncryptionScheme(const GMPEncryptionScheme& aEncryptionScheme)
|
|||
}
|
||||
}
|
||||
|
||||
static cdm::EncryptionScheme
|
||||
ConvertToCdmEncryptionScheme(const GMPEncryptionScheme& aEncryptionScheme,
|
||||
uint64_t aNumCipherBytes)
|
||||
{
|
||||
if (aNumCipherBytes == 0) {
|
||||
// Starting at CDM10, if fed a sample marked as encrypted that has no
|
||||
// encrypted bytes, the CDM will give a decryption error. So we mark these
|
||||
// as unencrypted to attempt to avoid such errors -- though ideally our
|
||||
// demuxers should not emit such data, so log it.
|
||||
if (aEncryptionScheme != GMPEncryptionScheme::kGMPEncryptionNone) {
|
||||
GMP_LOG(
|
||||
"ChromiumCDMChild::ConvertToCdmEncryptionScheme() got scheme marked "
|
||||
"as encrypted, but with no cipher bytes! This should be caught "
|
||||
"earlier, preferably by the demuxer! Returning "
|
||||
"cdm::EncryptionScheme::kUnencrypted");
|
||||
}
|
||||
return cdm::EncryptionScheme::kUnencrypted;
|
||||
}
|
||||
if (aEncryptionScheme == GMPEncryptionScheme::kGMPEncryptionNone) {
|
||||
GMP_LOG("ChromiumCDMChild::ConvertToCdmEncryptionScheme() got scheme "
|
||||
"marked as unecrypted but with > 0 cipher bytes! Something is "
|
||||
"buggy to emit such data -- likey a demuxer");
|
||||
}
|
||||
return ConvertToCdmEncryptionScheme(aEncryptionScheme);
|
||||
}
|
||||
|
||||
static void
|
||||
InitInputBuffer(const CDMInputBuffer& aBuffer,
|
||||
nsTArray<cdm::SubsampleEntry>& aSubSamples,
|
||||
|
@ -703,15 +729,17 @@ InitInputBuffer(const CDMInputBuffer& aBuffer,
|
|||
aInputBuffer.iv = aBuffer.mIV().Elements();
|
||||
aInputBuffer.iv_size = aBuffer.mIV().Length();
|
||||
|
||||
uint64_t numCipherBytes = 0;
|
||||
aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
|
||||
for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
|
||||
aSubSamples.AppendElement(cdm::SubsampleEntry{
|
||||
aBuffer.mClearBytes()[i], aBuffer.mCipherBytes()[i] });
|
||||
numCipherBytes += aBuffer.mCipherBytes()[i];
|
||||
}
|
||||
aInputBuffer.subsamples = aSubSamples.Elements();
|
||||
aInputBuffer.num_subsamples = aSubSamples.Length();
|
||||
aInputBuffer.encryption_scheme =
|
||||
ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme());
|
||||
ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme(), numCipherBytes);
|
||||
}
|
||||
aInputBuffer.timestamp = aBuffer.mTimestamp();
|
||||
}
|
||||
|
|
|
@ -367,18 +367,22 @@ mozilla::ipc::IPCResult
|
|||
GMPContentChild::RecvPChromiumCDMConstructor(PChromiumCDMChild* aActor)
|
||||
{
|
||||
ChromiumCDMChild* child = static_cast<ChromiumCDMChild*>(aActor);
|
||||
// TODO: Once we support CDM10, create one here, for now try and create CDM9
|
||||
cdm::Host_9* host9 = child;
|
||||
cdm::Host_10* host10 = child;
|
||||
|
||||
void* cdm = nullptr;
|
||||
GMPErr err = mGMPChild->GetAPI(CHROMIUM_CDM_API_BACKWARD_COMPAT, host9, &cdm);
|
||||
GMPErr err = mGMPChild->GetAPI(CHROMIUM_CDM_API, host10, &cdm);
|
||||
if (err != GMPNoErr || !cdm) {
|
||||
NS_WARNING("GMPGetAPI call failed trying to get CDM.");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
// Try to create older version 9 CDM.
|
||||
cdm::Host_9* host9 = child;
|
||||
GMPErr err =
|
||||
mGMPChild->GetAPI(CHROMIUM_CDM_API_BACKWARD_COMPAT, host9, &cdm);
|
||||
if (err != GMPNoErr || !cdm) {
|
||||
NS_WARNING("GMPGetAPI call failed trying to get CDM.");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
cdm = new ChromiumCDM9BackwardsCompat(
|
||||
host10, static_cast<cdm::ContentDecryptionModule_9*>(cdm));
|
||||
}
|
||||
cdm::Host_10* host10 = child;
|
||||
cdm = new ChromiumCDM9BackwardsCompat(
|
||||
host10, static_cast<cdm::ContentDecryptionModule_9*>(cdm));
|
||||
|
||||
child->Init(static_cast<cdm::ContentDecryptionModule_10*>(cdm),
|
||||
mGMPChild->mStorageId);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "nsClassHashtable.h"
|
||||
|
||||
#define CHROMIUM_CDM_API_BACKWARD_COMPAT "chromium-cdm9-host4"
|
||||
// TODO: The following should be used as we switch to cdm10
|
||||
#define CHROMIUM_CDM_API "chromium-cdm10-host4"
|
||||
|
||||
class nsIFile;
|
||||
|
|
|
@ -81,15 +81,10 @@ protected:
|
|||
bool SupportsColorDepth(gfx::ColorDepth aColorDepth,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const override
|
||||
{
|
||||
// We don't support bitDepth > 8 when compositor backend is D3D11.
|
||||
// But we don't have KnowsCompositor or any object
|
||||
// that we can ask for the layersbackend type.
|
||||
// We should remove this restriction until
|
||||
// we solve the D3D11 compositor backend issue.
|
||||
#if defined(XP_LINUX) || defined(XP_MACOSX)
|
||||
return true;
|
||||
#endif
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
return aColorDepth == gfx::ColorDepth::COLOR_8;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -237,22 +237,6 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(MediaRawData* aSample,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if ((mCodecContext->pix_fmt == AV_PIX_FMT_YUV420P10LE ||
|
||||
mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P10LE
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 57
|
||||
|| mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P12LE
|
||||
#endif
|
||||
) &&
|
||||
(!mImageAllocator || (mImageAllocator->GetCompositorBackendType()
|
||||
!= layers::LayersBackend::LAYERS_BASIC &&
|
||||
mImageAllocator->GetCompositorBackendType()
|
||||
!= layers::LayersBackend::LAYERS_OPENGL &&
|
||||
mImageAllocator->GetCompositorBackendType()
|
||||
!= layers::LayersBackend::LAYERS_D3D11))) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("unsupported format type (hdr)"));
|
||||
}
|
||||
|
||||
// If we've decoded a frame then we need to output it
|
||||
int64_t pts = mPtsContext.GuessCorrectPts(mFrame->pkt_pts, mFrame->pkt_dts);
|
||||
// Retrieve duration from dts.
|
||||
|
|
|
@ -656,8 +656,14 @@ BufferTextureHost::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
|||
MOZ_ASSERT(aImageKeys.length() == 3);
|
||||
|
||||
const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
|
||||
wr::ImageDescriptor yDescriptor(desc.ySize(), desc.yStride(), gfx::SurfaceFormat::A8);
|
||||
wr::ImageDescriptor cbcrDescriptor(desc.cbCrSize(), desc.cbCrStride(), gfx::SurfaceFormat::A8);
|
||||
wr::ImageDescriptor yDescriptor(
|
||||
desc.ySize(),
|
||||
desc.yStride(),
|
||||
SurfaceFormatForColorDepth(desc.colorDepth()));
|
||||
wr::ImageDescriptor cbcrDescriptor(
|
||||
desc.cbCrSize(),
|
||||
desc.cbCrStride(),
|
||||
SurfaceFormatForColorDepth(desc.colorDepth()));
|
||||
(aResources.*method)(aImageKeys[0], yDescriptor, aExtID, bufferType, 0);
|
||||
(aResources.*method)(aImageKeys[1], cbcrDescriptor, aExtID, bufferType, 1);
|
||||
(aResources.*method)(aImageKeys[2], cbcrDescriptor, aExtID, bufferType, 2);
|
||||
|
@ -683,6 +689,7 @@ BufferTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
|
|||
aImageKeys[0],
|
||||
aImageKeys[1],
|
||||
aImageKeys[2],
|
||||
wr::ToWrColorDepth(desc.colorDepth()),
|
||||
wr::ToWrYuvColorSpace(desc.yUVColorSpace()),
|
||||
aFilter);
|
||||
}
|
||||
|
|
|
@ -1164,6 +1164,7 @@ DXGITextureHostD3D11::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
|
|||
true,
|
||||
aImageKeys[0],
|
||||
aImageKeys[1],
|
||||
wr::ColorDepth::Color8,
|
||||
wr::ToWrYuvColorSpace(YUVColorSpace::BT601),
|
||||
aFilter);
|
||||
break;
|
||||
|
@ -1396,6 +1397,7 @@ DXGIYCbCrTextureHostD3D11::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
|
|||
aImageKeys[0],
|
||||
aImageKeys[1],
|
||||
aImageKeys[2],
|
||||
wr::ToWrColorDepth(mColorDepth),
|
||||
wr::ToWrYuvColorSpace(mYUVColorSpace),
|
||||
aFilter);
|
||||
}
|
||||
|
|
|
@ -228,10 +228,13 @@ MacIOSurfaceTextureHostOGL::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
|
|||
case gfx::SurfaceFormat::YUV422: {
|
||||
MOZ_ASSERT(aImageKeys.length() == 1);
|
||||
MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
|
||||
// Those images can only be generated at present by the Apple H264 decoder
|
||||
// which only supports 8 bits color depth.
|
||||
aBuilder.PushYCbCrInterleavedImage(aBounds,
|
||||
aClip,
|
||||
true,
|
||||
aImageKeys[0],
|
||||
wr::ColorDepth::Color8,
|
||||
wr::ToWrYuvColorSpace(YUVColorSpace::BT601),
|
||||
aFilter);
|
||||
break;
|
||||
|
@ -239,11 +242,14 @@ MacIOSurfaceTextureHostOGL::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
|
|||
case gfx::SurfaceFormat::NV12: {
|
||||
MOZ_ASSERT(aImageKeys.length() == 2);
|
||||
MOZ_ASSERT(mSurface->GetPlaneCount() == 2);
|
||||
// Those images can only be generated at present by the Apple H264 decoder
|
||||
// which only supports 8 bits color depth.
|
||||
aBuilder.PushNV12Image(aBounds,
|
||||
aClip,
|
||||
true,
|
||||
aImageKeys[0],
|
||||
aImageKeys[1],
|
||||
wr::ColorDepth::Color8,
|
||||
wr::ToWrYuvColorSpace(YUVColorSpace::BT601),
|
||||
aFilter);
|
||||
break;
|
||||
|
|
|
@ -1129,6 +1129,7 @@ DisplayListBuilder::PushYCbCrPlanarImage(const wr::LayoutRect& aBounds,
|
|||
wr::ImageKey aImageChannel0,
|
||||
wr::ImageKey aImageChannel1,
|
||||
wr::ImageKey aImageChannel2,
|
||||
wr::WrColorDepth aColorDepth,
|
||||
wr::WrYuvColorSpace aColorSpace,
|
||||
wr::ImageRendering aRendering)
|
||||
{
|
||||
|
@ -1139,6 +1140,7 @@ DisplayListBuilder::PushYCbCrPlanarImage(const wr::LayoutRect& aBounds,
|
|||
aImageChannel0,
|
||||
aImageChannel1,
|
||||
aImageChannel2,
|
||||
aColorDepth,
|
||||
aColorSpace,
|
||||
aRendering);
|
||||
}
|
||||
|
@ -1149,6 +1151,7 @@ DisplayListBuilder::PushNV12Image(const wr::LayoutRect& aBounds,
|
|||
bool aIsBackfaceVisible,
|
||||
wr::ImageKey aImageChannel0,
|
||||
wr::ImageKey aImageChannel1,
|
||||
wr::WrColorDepth aColorDepth,
|
||||
wr::WrYuvColorSpace aColorSpace,
|
||||
wr::ImageRendering aRendering)
|
||||
{
|
||||
|
@ -1158,6 +1161,7 @@ DisplayListBuilder::PushNV12Image(const wr::LayoutRect& aBounds,
|
|||
aIsBackfaceVisible,
|
||||
aImageChannel0,
|
||||
aImageChannel1,
|
||||
aColorDepth,
|
||||
aColorSpace,
|
||||
aRendering);
|
||||
}
|
||||
|
@ -1167,6 +1171,7 @@ DisplayListBuilder::PushYCbCrInterleavedImage(const wr::LayoutRect& aBounds,
|
|||
const wr::LayoutRect& aClip,
|
||||
bool aIsBackfaceVisible,
|
||||
wr::ImageKey aImageChannel0,
|
||||
wr::WrColorDepth aColorDepth,
|
||||
wr::WrYuvColorSpace aColorSpace,
|
||||
wr::ImageRendering aRendering)
|
||||
{
|
||||
|
@ -1175,6 +1180,7 @@ DisplayListBuilder::PushYCbCrInterleavedImage(const wr::LayoutRect& aBounds,
|
|||
MergeClipLeaf(aClip),
|
||||
aIsBackfaceVisible,
|
||||
aImageChannel0,
|
||||
aColorDepth,
|
||||
aColorSpace,
|
||||
aRendering);
|
||||
}
|
||||
|
|
|
@ -408,6 +408,7 @@ public:
|
|||
wr::ImageKey aImageChannel0,
|
||||
wr::ImageKey aImageChannel1,
|
||||
wr::ImageKey aImageChannel2,
|
||||
wr::WrColorDepth aColorDepth,
|
||||
wr::WrYuvColorSpace aColorSpace,
|
||||
wr::ImageRendering aFilter);
|
||||
|
||||
|
@ -416,6 +417,7 @@ public:
|
|||
bool aIsBackfaceVisible,
|
||||
wr::ImageKey aImageChannel0,
|
||||
wr::ImageKey aImageChannel1,
|
||||
wr::WrColorDepth aColorDepth,
|
||||
wr::WrYuvColorSpace aColorSpace,
|
||||
wr::ImageRendering aFilter);
|
||||
|
||||
|
@ -423,6 +425,7 @@ public:
|
|||
const wr::LayoutRect& aClip,
|
||||
bool aIsBackfaceVisible,
|
||||
wr::ImageKey aImageChannel0,
|
||||
wr::WrColorDepth aColorDepth,
|
||||
wr::WrYuvColorSpace aColorSpace,
|
||||
wr::ImageRendering aFilter);
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat) {
|
|||
return Some(wr::ImageFormat::BGRA8);
|
||||
case gfx::SurfaceFormat::A8:
|
||||
return Some(wr::ImageFormat::R8);
|
||||
case gfx::SurfaceFormat::A16:
|
||||
return Some(wr::ImageFormat::R16);
|
||||
case gfx::SurfaceFormat::R8G8:
|
||||
return Some(wr::ImageFormat::RG8);
|
||||
case gfx::SurfaceFormat::UNKNOWN:
|
||||
|
@ -88,6 +90,8 @@ ImageFormatToSurfaceFormat(ImageFormat aFormat) {
|
|||
return gfx::SurfaceFormat::B8G8R8A8;
|
||||
case ImageFormat::R8:
|
||||
return gfx::SurfaceFormat::A8;
|
||||
case ImageFormat::R16:
|
||||
return gfx::SurfaceFormat::A16;
|
||||
default:
|
||||
return gfx::SurfaceFormat::UNKNOWN;
|
||||
}
|
||||
|
@ -844,6 +848,20 @@ static inline wr::WrYuvColorSpace ToWrYuvColorSpace(YUVColorSpace aYUVColorSpace
|
|||
return wr::WrYuvColorSpace::Rec601;
|
||||
}
|
||||
|
||||
static inline wr::WrColorDepth ToWrColorDepth(gfx::ColorDepth aColorDepth) {
|
||||
switch (aColorDepth) {
|
||||
case gfx::ColorDepth::COLOR_8:
|
||||
return wr::WrColorDepth::Color8;
|
||||
case gfx::ColorDepth::COLOR_10:
|
||||
return wr::WrColorDepth::Color10;
|
||||
case gfx::ColorDepth::COLOR_12:
|
||||
return wr::WrColorDepth::Color12;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Tried to convert invalid color depth value.");
|
||||
}
|
||||
return wr::WrColorDepth::Color8;
|
||||
}
|
||||
|
||||
static inline wr::SyntheticItalics DegreesToSyntheticItalics(float aDegrees) {
|
||||
wr::SyntheticItalics synthetic_italics;
|
||||
synthetic_italics.angle = int16_t(std::min(std::max(aDegrees, -89.0f), 89.0f) * 256.0f);
|
||||
|
|
|
@ -92,6 +92,8 @@ pub type WrFontKey = FontKey;
|
|||
pub type WrFontInstanceKey = FontInstanceKey;
|
||||
/// cbindgen:field-names=[mNamespace, mHandle]
|
||||
type WrYuvColorSpace = YuvColorSpace;
|
||||
/// cbindgen:field-names=[mNamespace, mHandle]
|
||||
type WrColorDepth = ColorDepth;
|
||||
|
||||
fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
|
||||
if ptr.is_null() {
|
||||
|
@ -2055,6 +2057,7 @@ pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState,
|
|||
image_key_0: WrImageKey,
|
||||
image_key_1: WrImageKey,
|
||||
image_key_2: WrImageKey,
|
||||
color_depth: WrColorDepth,
|
||||
color_space: WrYuvColorSpace,
|
||||
image_rendering: ImageRendering) {
|
||||
debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
|
||||
|
@ -2066,7 +2069,7 @@ pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState,
|
|||
.dl_builder
|
||||
.push_yuv_image(&prim_info,
|
||||
YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2),
|
||||
ColorDepth::Color8,
|
||||
color_depth,
|
||||
color_space,
|
||||
image_rendering);
|
||||
}
|
||||
|
@ -2079,6 +2082,7 @@ pub extern "C" fn wr_dp_push_yuv_NV12_image(state: &mut WrState,
|
|||
is_backface_visible: bool,
|
||||
image_key_0: WrImageKey,
|
||||
image_key_1: WrImageKey,
|
||||
color_depth: WrColorDepth,
|
||||
color_space: WrYuvColorSpace,
|
||||
image_rendering: ImageRendering) {
|
||||
debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
|
||||
|
@ -2090,7 +2094,7 @@ pub extern "C" fn wr_dp_push_yuv_NV12_image(state: &mut WrState,
|
|||
.dl_builder
|
||||
.push_yuv_image(&prim_info,
|
||||
YuvData::NV12(image_key_0, image_key_1),
|
||||
ColorDepth::Color8,
|
||||
color_depth,
|
||||
color_space,
|
||||
image_rendering);
|
||||
}
|
||||
|
@ -2102,6 +2106,7 @@ pub extern "C" fn wr_dp_push_yuv_interleaved_image(state: &mut WrState,
|
|||
clip: LayoutRect,
|
||||
is_backface_visible: bool,
|
||||
image_key_0: WrImageKey,
|
||||
color_depth: WrColorDepth,
|
||||
color_space: WrYuvColorSpace,
|
||||
image_rendering: ImageRendering) {
|
||||
debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
|
||||
|
@ -2113,7 +2118,7 @@ pub extern "C" fn wr_dp_push_yuv_interleaved_image(state: &mut WrState,
|
|||
.dl_builder
|
||||
.push_yuv_image(&prim_info,
|
||||
YuvData::InterleavedYCbCr(image_key_0),
|
||||
ColorDepth::Color8,
|
||||
color_depth,
|
||||
color_space,
|
||||
image_rendering);
|
||||
}
|
||||
|
|
|
@ -69,6 +69,18 @@ enum class ClipMode {
|
|||
Sentinel /* this must be last for serialization purposes. */
|
||||
};
|
||||
|
||||
// Specifies the color depth of an image. Currently only used for YUV images.
|
||||
enum class ColorDepth : uint8_t {
|
||||
// 8 bits image (most common)
|
||||
Color8,
|
||||
// 10 bits image
|
||||
Color10,
|
||||
// 12 bits image
|
||||
Color12,
|
||||
|
||||
Sentinel /* this must be last for serialization purposes. */
|
||||
};
|
||||
|
||||
enum class ExtendMode : uint32_t {
|
||||
Clamp,
|
||||
Repeat,
|
||||
|
@ -942,6 +954,8 @@ struct GlyphOptions {
|
|||
}
|
||||
};
|
||||
|
||||
using WrColorDepth = ColorDepth;
|
||||
|
||||
using WrYuvColorSpace = YuvColorSpace;
|
||||
|
||||
struct ByteSlice {
|
||||
|
@ -1499,6 +1513,7 @@ void wr_dp_push_yuv_NV12_image(WrState *aState,
|
|||
bool aIsBackfaceVisible,
|
||||
WrImageKey aImageKey0,
|
||||
WrImageKey aImageKey1,
|
||||
WrColorDepth aColorDepth,
|
||||
WrYuvColorSpace aColorSpace,
|
||||
ImageRendering aImageRendering)
|
||||
WR_FUNC;
|
||||
|
@ -1510,6 +1525,7 @@ void wr_dp_push_yuv_interleaved_image(WrState *aState,
|
|||
LayoutRect aClip,
|
||||
bool aIsBackfaceVisible,
|
||||
WrImageKey aImageKey0,
|
||||
WrColorDepth aColorDepth,
|
||||
WrYuvColorSpace aColorSpace,
|
||||
ImageRendering aImageRendering)
|
||||
WR_FUNC;
|
||||
|
@ -1523,6 +1539,7 @@ void wr_dp_push_yuv_planar_image(WrState *aState,
|
|||
WrImageKey aImageKey0,
|
||||
WrImageKey aImageKey1,
|
||||
WrImageKey aImageKey2,
|
||||
WrColorDepth aColorDepth,
|
||||
WrYuvColorSpace aColorSpace,
|
||||
ImageRendering aImageRendering)
|
||||
WR_FUNC;
|
||||
|
|
|
@ -36,8 +36,9 @@ class BitArray
|
|||
|
||||
void clear(bool value) {
|
||||
memset(map, value ? 0xFF : 0, sizeof(map));
|
||||
if (value)
|
||||
if (value) {
|
||||
map[numSlots - 1] &= paddingMask;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool get(size_t offset) const {
|
||||
|
@ -63,8 +64,9 @@ class BitArray
|
|||
|
||||
bool isAllClear() const {
|
||||
for (size_t i = 0; i < numSlots; i++) {
|
||||
if (map[i])
|
||||
if (map[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,16 +12,18 @@ using namespace js;
|
|||
|
||||
SparseBitmap::~SparseBitmap()
|
||||
{
|
||||
for (Data::Range r(data.all()); !r.empty(); r.popFront())
|
||||
for (Data::Range r(data.all()); !r.empty(); r.popFront()) {
|
||||
js_delete(r.front().value());
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
SparseBitmap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
size_t size = data.shallowSizeOfExcludingThis(mallocSizeOf);
|
||||
for (Data::Range r(data.all()); !r.empty(); r.popFront())
|
||||
for (Data::Range r(data.all()); !r.empty(); r.popFront()) {
|
||||
size += mallocSizeOf(r.front().value());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -30,8 +32,9 @@ SparseBitmap::createBlock(Data::AddPtr p, size_t blockId, AutoEnterOOMUnsafeRegi
|
|||
{
|
||||
MOZ_ASSERT(!p);
|
||||
BitBlock* block = js_new<BitBlock>();
|
||||
if (!block || !data.add(p, blockId, block))
|
||||
if (!block || !data.add(p, blockId, block)) {
|
||||
oomUnsafe.crash("Bitmap OOM");
|
||||
}
|
||||
std::fill(block->begin(), block->end(), 0);
|
||||
return *block;
|
||||
}
|
||||
|
@ -43,8 +46,9 @@ SparseBitmap::getBit(size_t bit) const
|
|||
size_t blockWord = blockStartWord(word);
|
||||
|
||||
BitBlock* block = getBlock(blockWord / WordsInBlock);
|
||||
if (block)
|
||||
if (block) {
|
||||
return (*block)[word - blockWord] & (uintptr_t(1) << (bit % JS_BITS_PER_WORD));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -73,8 +77,9 @@ SparseBitmap::bitwiseOrWith(const SparseBitmap& other)
|
|||
for (Data::Range r(other.data.all()); !r.empty(); r.popFront()) {
|
||||
const BitBlock& otherBlock = *r.front().value();
|
||||
BitBlock& block = getOrCreateBlock(r.front().key());
|
||||
for (size_t i = 0; i < WordsInBlock; i++)
|
||||
for (size_t i = 0; i < WordsInBlock; i++) {
|
||||
block[i] |= otherBlock[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,11 +92,13 @@ SparseBitmap::bitwiseOrInto(DenseBitmap& other) const
|
|||
size_t numWords = wordIntersectCount(blockWord, other);
|
||||
#ifdef DEBUG
|
||||
// Any words out of range in other should be zero in this bitmap.
|
||||
for (size_t i = numWords; i < WordsInBlock; i++)
|
||||
for (size_t i = numWords; i < WordsInBlock; i++) {
|
||||
MOZ_ASSERT(!block[i]);
|
||||
}
|
||||
#endif
|
||||
for (size_t i = 0; i < numWords; i++)
|
||||
for (size_t i = 0; i < numWords; i++) {
|
||||
other.word(blockWord + i) |= block[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +112,8 @@ SparseBitmap::bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* t
|
|||
|
||||
BitBlock* block = getBlock(blockWord / WordsInBlock);
|
||||
if (block) {
|
||||
for (size_t i = 0; i < numWords; i++)
|
||||
for (size_t i = 0; i < numWords; i++) {
|
||||
target[i] |= (*block)[wordStart - blockWord + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,8 +59,9 @@ class DenseBitmap
|
|||
}
|
||||
|
||||
void bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const {
|
||||
for (size_t i = 0; i < numWords; i++)
|
||||
for (size_t i = 0; i < numWords; i++) {
|
||||
target[i] |= data[wordStart + i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -100,8 +101,9 @@ class SparseBitmap
|
|||
// the add() within createBlock().
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
Data::AddPtr p = data.lookupForAdd(blockId);
|
||||
if (p)
|
||||
if (p) {
|
||||
return *p->value();
|
||||
}
|
||||
return createBlock(p, blockId, oomUnsafe);
|
||||
}
|
||||
|
||||
|
|
|
@ -124,8 +124,9 @@ class Fifo
|
|||
// |const T&| or a |T&&|.
|
||||
template <typename U>
|
||||
MOZ_MUST_USE bool pushBack(U&& u) {
|
||||
if (!rear_.append(std::forward<U>(u)))
|
||||
if (!rear_.append(std::forward<U>(u))) {
|
||||
return false;
|
||||
}
|
||||
fixup();
|
||||
return true;
|
||||
}
|
||||
|
@ -133,8 +134,9 @@ class Fifo
|
|||
// Construct a T in-place at the back of the queue.
|
||||
template <typename... Args>
|
||||
MOZ_MUST_USE bool emplaceBack(Args&&... args) {
|
||||
if (!rear_.emplaceBack(std::forward<Args>(args)...))
|
||||
if (!rear_.emplaceBack(std::forward<Args>(args)...)) {
|
||||
return false;
|
||||
}
|
||||
fixup();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -75,8 +75,9 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
InlineEntry* end = inlineEnd();
|
||||
for (InlineEntry* it = inlineStart(); it != end; ++it) {
|
||||
if (it->key && !it->moveTo(table_))
|
||||
if (it->key && !it->moveTo(table_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inlNext_ = InlineEntries + 1;
|
||||
|
@ -87,8 +88,9 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
MOZ_NEVER_INLINE
|
||||
MOZ_MUST_USE bool switchAndAdd(const InlineEntry& entry) {
|
||||
if (!switchToTable())
|
||||
if (!switchToTable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return entry.putNew(table_);
|
||||
}
|
||||
|
@ -148,10 +150,12 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
bool operator==(const Ptr& other) const {
|
||||
MOZ_ASSERT(found() && other.found());
|
||||
if (isInlinePtr_ != other.isInlinePtr_)
|
||||
if (isInlinePtr_ != other.isInlinePtr_) {
|
||||
return false;
|
||||
if (isInlinePtr_)
|
||||
}
|
||||
if (isInlinePtr_) {
|
||||
return inlPtr_ == other.inlPtr_;
|
||||
}
|
||||
return tablePtr_ == other.tablePtr_;
|
||||
}
|
||||
|
||||
|
@ -208,10 +212,12 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
bool operator==(const AddPtr& other) const {
|
||||
MOZ_ASSERT(found() && other.found());
|
||||
if (isInlinePtr_ != other.isInlinePtr_)
|
||||
if (isInlinePtr_ != other.isInlinePtr_) {
|
||||
return false;
|
||||
if (isInlinePtr_)
|
||||
}
|
||||
if (isInlinePtr_) {
|
||||
return inlAddPtr_ == other.inlAddPtr_;
|
||||
}
|
||||
return tableAddPtr_ == other.tableAddPtr_;
|
||||
}
|
||||
|
||||
|
@ -247,13 +253,15 @@ class InlineTable : private AllocPolicy
|
|||
Ptr lookup(const Lookup& l) {
|
||||
MOZ_ASSERT(keyNonZero(l));
|
||||
|
||||
if (usingTable())
|
||||
if (usingTable()) {
|
||||
return Ptr(table_.lookup(l));
|
||||
}
|
||||
|
||||
InlineEntry* end = inlineEnd();
|
||||
for (InlineEntry* it = inlineStart(); it != end; ++it) {
|
||||
if (it->key && HashPolicy::match(it->key, l))
|
||||
if (it->key && HashPolicy::match(it->key, l)) {
|
||||
return Ptr(it);
|
||||
}
|
||||
}
|
||||
|
||||
return Ptr(nullptr);
|
||||
|
@ -263,13 +271,15 @@ class InlineTable : private AllocPolicy
|
|||
AddPtr lookupForAdd(const Lookup& l) {
|
||||
MOZ_ASSERT(keyNonZero(l));
|
||||
|
||||
if (usingTable())
|
||||
if (usingTable()) {
|
||||
return AddPtr(table_.lookupForAdd(l));
|
||||
}
|
||||
|
||||
InlineEntry* end = inlineEnd();
|
||||
for (InlineEntry* it = inlineStart(); it != end; ++it) {
|
||||
if (it->key && HashPolicy::match(it->key, l))
|
||||
if (it->key && HashPolicy::match(it->key, l)) {
|
||||
return AddPtr(it, true);
|
||||
}
|
||||
}
|
||||
|
||||
// The add pointer that's returned here may indicate the limit entry of
|
||||
|
@ -291,8 +301,9 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
// Switching to table mode before we add this pointer.
|
||||
if (addPtr == inlineStart() + InlineEntries) {
|
||||
if (!switchToTable())
|
||||
if (!switchToTable()) {
|
||||
return false;
|
||||
}
|
||||
return table_.putNew(std::forward<KeyInput>(key),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
@ -300,8 +311,9 @@ class InlineTable : private AllocPolicy
|
|||
MOZ_ASSERT(!p.found());
|
||||
MOZ_ASSERT(uintptr_t(inlineEnd()) == uintptr_t(p.inlAddPtr_));
|
||||
|
||||
if (!this->checkSimulatedOOM())
|
||||
if (!this->checkSimulatedOOM()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addPtr->update(std::forward<KeyInput>(key),
|
||||
std::forward<Args>(args)...);
|
||||
|
@ -329,8 +341,9 @@ class InlineTable : private AllocPolicy
|
|||
}
|
||||
|
||||
void remove(const Lookup& l) {
|
||||
if (Ptr p = lookup(l))
|
||||
if (Ptr p = lookup(l)) {
|
||||
remove(p);
|
||||
}
|
||||
}
|
||||
|
||||
class Range
|
||||
|
@ -375,8 +388,9 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
void advancePastNulls(InlineEntry* begin) {
|
||||
InlineEntry* newCur = begin;
|
||||
while (newCur < end_ && nullptr == newCur->key)
|
||||
while (newCur < end_ && nullptr == newCur->key) {
|
||||
++newCur;
|
||||
}
|
||||
MOZ_ASSERT(uintptr_t(newCur) <= uintptr_t(end_));
|
||||
cur_ = newCur;
|
||||
}
|
||||
|
@ -393,17 +407,19 @@ class InlineTable : private AllocPolicy
|
|||
|
||||
Entry front() {
|
||||
MOZ_ASSERT(!empty());
|
||||
if (isInlineRange())
|
||||
if (isInlineRange()) {
|
||||
return Entry(cur_);
|
||||
}
|
||||
return Entry(&tableRange_->front());
|
||||
}
|
||||
|
||||
void popFront() {
|
||||
MOZ_ASSERT(!empty());
|
||||
if (isInlineRange())
|
||||
if (isInlineRange()) {
|
||||
bumpCurPtr();
|
||||
else
|
||||
} else {
|
||||
tableRange_->popFront();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -467,15 +483,17 @@ class InlineMap
|
|||
|
||||
const Key& key() const {
|
||||
MOZ_ASSERT(!!mapEntry_ != !!inlineEntry_);
|
||||
if (mapEntry_)
|
||||
if (mapEntry_) {
|
||||
return mapEntry_->key();
|
||||
}
|
||||
return inlineEntry_->key;
|
||||
}
|
||||
|
||||
Value& value() {
|
||||
MOZ_ASSERT(!!mapEntry_ != !!inlineEntry_);
|
||||
if (mapEntry_)
|
||||
if (mapEntry_) {
|
||||
return mapEntry_->value();
|
||||
}
|
||||
return inlineEntry_->value;
|
||||
}
|
||||
};
|
||||
|
@ -605,8 +623,9 @@ class InlineSet
|
|||
|
||||
operator T() const {
|
||||
MOZ_ASSERT(!!setEntry_ != !!inlineEntry_);
|
||||
if (setEntry_)
|
||||
if (setEntry_) {
|
||||
return *setEntry_;
|
||||
}
|
||||
return inlineEntry_->key;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -30,8 +30,9 @@ BumpChunk::newWithCapacity(size_t size)
|
|||
MOZ_DIAGNOSTIC_ASSERT(RoundUpPow2(size) == size);
|
||||
MOZ_DIAGNOSTIC_ASSERT(size >= sizeof(BumpChunk));
|
||||
void* mem = js_malloc(size);
|
||||
if (!mem)
|
||||
if (!mem) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePtr<BumpChunk> result(new (mem) BumpChunk(size));
|
||||
|
||||
|
@ -110,10 +111,12 @@ LifoAlloc::reset(size_t defaultChunkSize)
|
|||
{
|
||||
MOZ_ASSERT(mozilla::IsPowerOfTwo(defaultChunkSize));
|
||||
|
||||
while (!chunks_.empty())
|
||||
while (!chunks_.empty()) {
|
||||
chunks_.popFirst();
|
||||
while (!unused_.empty())
|
||||
}
|
||||
while (!unused_.empty()) {
|
||||
unused_.popFirst();
|
||||
}
|
||||
defaultChunkSize_ = defaultChunkSize;
|
||||
markCount = 0;
|
||||
curSize_ = 0;
|
||||
|
@ -157,8 +160,9 @@ LifoAlloc::newChunkWithCapacity(size_t n)
|
|||
|
||||
// Create a new BumpChunk, and allocate space for it.
|
||||
UniqueBumpChunk result = detail::BumpChunk::newWithCapacity(chunkSize);
|
||||
if (!result)
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(result->computedSizeOfIncludingThis() == chunkSize);
|
||||
return result;
|
||||
}
|
||||
|
@ -190,8 +194,9 @@ LifoAlloc::getOrCreateChunk(size_t n)
|
|||
|
||||
// Allocate a new BumpChunk with enough space for the next allocation.
|
||||
UniqueBumpChunk newChunk = newChunkWithCapacity(n);
|
||||
if (!newChunk)
|
||||
if (!newChunk) {
|
||||
return false;
|
||||
}
|
||||
size_t size = newChunk->computedSizeOfIncludingThis();
|
||||
chunks_.append(std::move(newChunk));
|
||||
incrementCurSize(size);
|
||||
|
@ -216,8 +221,9 @@ LifoAlloc::transferUnusedFrom(LifoAlloc* other)
|
|||
MOZ_ASSERT(!markCount);
|
||||
|
||||
size_t size = 0;
|
||||
for (detail::BumpChunk& bc : other->unused_)
|
||||
for (detail::BumpChunk& bc : other->unused_) {
|
||||
size += bc.computedSizeOfIncludingThis();
|
||||
}
|
||||
|
||||
appendUnused(std::move(other->unused_));
|
||||
incrementCurSize(size);
|
||||
|
|
|
@ -153,8 +153,9 @@ class SingleLinkedList
|
|||
}
|
||||
|
||||
void pushFront(UniquePtr<T, D>&& elem) {
|
||||
if (!last_)
|
||||
if (!last_) {
|
||||
last_ = elem.get();
|
||||
}
|
||||
elem->next_ = std::move(head_);
|
||||
head_ = std::move(elem);
|
||||
assertInvariants();
|
||||
|
@ -171,12 +172,14 @@ class SingleLinkedList
|
|||
assertInvariants();
|
||||
}
|
||||
void appendAll(SingleLinkedList&& list) {
|
||||
if (list.empty())
|
||||
if (list.empty()) {
|
||||
return;
|
||||
if (last_)
|
||||
}
|
||||
if (last_) {
|
||||
last_->next_ = std::move(list.head_);
|
||||
else
|
||||
} else {
|
||||
head_ = std::move(list.head_);
|
||||
}
|
||||
last_ = list.last_;
|
||||
list.last_ = nullptr;
|
||||
assertInvariants();
|
||||
|
@ -186,8 +189,9 @@ class SingleLinkedList
|
|||
MOZ_ASSERT(head_);
|
||||
UniquePtr<T, D> result = std::move(head_);
|
||||
head_ = std::move(result->next_);
|
||||
if (!head_)
|
||||
if (!head_) {
|
||||
last_ = nullptr;
|
||||
}
|
||||
assertInvariants();
|
||||
return result;
|
||||
}
|
||||
|
@ -443,8 +447,9 @@ class BumpChunk : public SingleLinkedListElement<BumpChunk>
|
|||
// Space remaining in the current chunk.
|
||||
size_t unused() const {
|
||||
uint8_t* aligned = nextAllocBase(end());
|
||||
if (aligned < capacity_)
|
||||
if (aligned < capacity_) {
|
||||
return capacity_ - aligned;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -454,12 +459,14 @@ class BumpChunk : public SingleLinkedListElement<BumpChunk>
|
|||
uint8_t* aligned = nextAllocBase(end());
|
||||
uint8_t* newBump = nextAllocEnd(aligned, n);
|
||||
|
||||
if (newBump > capacity_)
|
||||
if (newBump > capacity_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check for overflow.
|
||||
if (MOZ_UNLIKELY(newBump < bump_))
|
||||
if (MOZ_UNLIKELY(newBump < bump_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(canAlloc(n)); // Ensure consistency between "can" and "try".
|
||||
setBump(newBump);
|
||||
|
@ -552,8 +559,9 @@ class LifoAlloc
|
|||
// Append unused chunks to the end of this LifoAlloc.
|
||||
void appendUnused(BumpChunkList&& otherUnused) {
|
||||
#ifdef DEBUG
|
||||
for (detail::BumpChunk& bc: otherUnused)
|
||||
for (detail::BumpChunk& bc: otherUnused) {
|
||||
MOZ_ASSERT(bc.empty());
|
||||
}
|
||||
#endif
|
||||
unused_.appendAll(std::move(otherUnused));
|
||||
}
|
||||
|
@ -567,8 +575,9 @@ class LifoAlloc
|
|||
// Track the amount of space allocated in used and unused chunks.
|
||||
void incrementCurSize(size_t size) {
|
||||
curSize_ += size;
|
||||
if (curSize_ > peakSize_)
|
||||
if (curSize_ > peakSize_) {
|
||||
peakSize_ = curSize_;
|
||||
}
|
||||
}
|
||||
void decrementCurSize(size_t size) {
|
||||
MOZ_ASSERT(curSize_ >= size);
|
||||
|
@ -578,11 +587,13 @@ class LifoAlloc
|
|||
MOZ_ALWAYS_INLINE
|
||||
void* allocImpl(size_t n) {
|
||||
void* result;
|
||||
if (!chunks_.empty() && (result = chunks_.last()->tryAlloc(n)))
|
||||
if (!chunks_.empty() && (result = chunks_.last()->tryAlloc(n))) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!getOrCreateChunk(n))
|
||||
if (!getOrCreateChunk(n)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Since we just created a large enough chunk, this can't fail.
|
||||
result = chunks_.last()->tryAlloc(n);
|
||||
|
@ -636,8 +647,9 @@ class LifoAlloc
|
|||
|
||||
static const unsigned HUGE_ALLOCATION = 50 * 1024 * 1024;
|
||||
void freeAllIfHugeAndUnused() {
|
||||
if (markCount == 0 && curSize_ > HUGE_ALLOCATION)
|
||||
if (markCount == 0 && curSize_ > HUGE_ALLOCATION) {
|
||||
freeAll();
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE
|
||||
|
@ -645,8 +657,9 @@ class LifoAlloc
|
|||
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
|
||||
// Only simulate OOMs when we are not using the LifoAlloc as an
|
||||
// infallible allocator.
|
||||
if (fallibleScope_)
|
||||
if (fallibleScope_) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
}
|
||||
#endif
|
||||
return allocImpl(n);
|
||||
}
|
||||
|
@ -659,8 +672,9 @@ class LifoAlloc
|
|||
static_assert(alignof(T) <= detail::LIFO_ALLOC_ALIGN,
|
||||
"LifoAlloc must provide enough alignment to store T");
|
||||
void* ptr = alloc(n);
|
||||
if (!ptr)
|
||||
if (!ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new (ptr) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
@ -668,8 +682,9 @@ class LifoAlloc
|
|||
MOZ_ALWAYS_INLINE
|
||||
void* allocInfallible(size_t n) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (void* result = allocImpl(n))
|
||||
if (void* result = allocImpl(n)) {
|
||||
return result;
|
||||
}
|
||||
oomUnsafe.crash("LifoAlloc::allocInfallible");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -683,19 +698,22 @@ class LifoAlloc
|
|||
size_t total = 0;
|
||||
if (!chunks_.empty()) {
|
||||
total += chunks_.last()->unused();
|
||||
if (total >= n)
|
||||
if (total >= n) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (detail::BumpChunk& bc : unused_) {
|
||||
total += bc.unused();
|
||||
if (total >= n)
|
||||
if (total >= n) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
UniqueBumpChunk newChunk = newChunkWithCapacity(n);
|
||||
if (!newChunk)
|
||||
if (!newChunk) {
|
||||
return false;
|
||||
}
|
||||
size_t size = newChunk->computedSizeOfIncludingThis();
|
||||
unused_.pushFront(std::move(newChunk));
|
||||
incrementCurSize(size);
|
||||
|
@ -745,8 +763,9 @@ class LifoAlloc
|
|||
template <typename T>
|
||||
T* newArrayUninitialized(size_t count) {
|
||||
size_t bytes;
|
||||
if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
|
||||
if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes))) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<T*>(alloc(bytes));
|
||||
}
|
||||
|
||||
|
@ -754,8 +773,9 @@ class LifoAlloc
|
|||
|
||||
Mark mark() {
|
||||
markCount++;
|
||||
if (chunks_.empty())
|
||||
if (chunks_.empty()) {
|
||||
return Mark();
|
||||
}
|
||||
return chunks_.last()->mark();
|
||||
}
|
||||
|
||||
|
@ -764,41 +784,49 @@ class LifoAlloc
|
|||
|
||||
// Move the blocks which are after the mark to the set of unused chunks.
|
||||
BumpChunkList released;
|
||||
if (!mark.markedChunk())
|
||||
if (!mark.markedChunk()) {
|
||||
released = std::move(chunks_);
|
||||
else
|
||||
} else {
|
||||
released = chunks_.splitAfter(mark.markedChunk());
|
||||
}
|
||||
|
||||
// Release the content of all the blocks which are after the marks.
|
||||
for (detail::BumpChunk& bc : released)
|
||||
for (detail::BumpChunk& bc : released) {
|
||||
bc.release();
|
||||
}
|
||||
unused_.appendAll(std::move(released));
|
||||
|
||||
// Release everything which follows the mark in the last chunk.
|
||||
if (!chunks_.empty())
|
||||
if (!chunks_.empty()) {
|
||||
chunks_.last()->release(mark);
|
||||
}
|
||||
}
|
||||
|
||||
void releaseAll() {
|
||||
MOZ_ASSERT(!markCount);
|
||||
for (detail::BumpChunk& bc : chunks_)
|
||||
for (detail::BumpChunk& bc : chunks_) {
|
||||
bc.release();
|
||||
}
|
||||
unused_.appendAll(std::move(chunks_));
|
||||
}
|
||||
|
||||
// Protect the content of the LifoAlloc chunks.
|
||||
#ifdef LIFO_CHUNK_PROTECT
|
||||
void setReadOnly() {
|
||||
for (detail::BumpChunk& bc : chunks_)
|
||||
for (detail::BumpChunk& bc : chunks_) {
|
||||
bc.setReadOnly();
|
||||
for (detail::BumpChunk& bc : unused_)
|
||||
}
|
||||
for (detail::BumpChunk& bc : unused_) {
|
||||
bc.setReadOnly();
|
||||
}
|
||||
}
|
||||
void setReadWrite() {
|
||||
for (detail::BumpChunk& bc : chunks_)
|
||||
for (detail::BumpChunk& bc : chunks_) {
|
||||
bc.setReadWrite();
|
||||
for (detail::BumpChunk& bc : unused_)
|
||||
}
|
||||
for (detail::BumpChunk& bc : unused_) {
|
||||
bc.setReadWrite();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void setReadOnly() const {}
|
||||
|
@ -808,8 +836,9 @@ class LifoAlloc
|
|||
// Get the total "used" (occupied bytes) count for the arena chunks.
|
||||
size_t used() const {
|
||||
size_t accum = 0;
|
||||
for (const detail::BumpChunk& chunk : chunks_)
|
||||
for (const detail::BumpChunk& chunk : chunks_) {
|
||||
accum += chunk.used();
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
|
||||
|
@ -822,28 +851,33 @@ class LifoAlloc
|
|||
// Return the number of bytes remaining to allocate in the current chunk.
|
||||
// e.g. How many bytes we can allocate before needing a new block.
|
||||
size_t availableInCurrentChunk() const {
|
||||
if (chunks_.empty())
|
||||
if (chunks_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return chunks_.last()->unused();
|
||||
}
|
||||
|
||||
// Get the total size of the arena chunks (including unused space).
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
||||
size_t n = 0;
|
||||
for (const detail::BumpChunk& chunk : chunks_)
|
||||
for (const detail::BumpChunk& chunk : chunks_) {
|
||||
n += chunk.sizeOfIncludingThis(mallocSizeOf);
|
||||
for (const detail::BumpChunk& chunk : unused_)
|
||||
}
|
||||
for (const detail::BumpChunk& chunk : unused_) {
|
||||
n += chunk.sizeOfIncludingThis(mallocSizeOf);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Get the total size of the arena chunks (including unused space).
|
||||
size_t computedSizeOfExcludingThis() const {
|
||||
size_t n = 0;
|
||||
for (const detail::BumpChunk& chunk : chunks_)
|
||||
for (const detail::BumpChunk& chunk : chunks_) {
|
||||
n += chunk.computedSizeOfIncludingThis();
|
||||
for (const detail::BumpChunk& chunk : unused_)
|
||||
}
|
||||
for (const detail::BumpChunk& chunk : unused_) {
|
||||
n += chunk.computedSizeOfIncludingThis();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -869,8 +903,9 @@ class LifoAlloc
|
|||
#ifdef DEBUG
|
||||
bool contains(void* ptr) const {
|
||||
for (const detail::BumpChunk& chunk : chunks_) {
|
||||
if (chunk.contains(ptr))
|
||||
if (chunk.contains(ptr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -912,8 +947,9 @@ class LifoAlloc
|
|||
chunkEnd_(alloc.chunks_.end()),
|
||||
head_(nullptr)
|
||||
{
|
||||
if (chunkIt_ != chunkEnd_)
|
||||
if (chunkIt_ != chunkEnd_) {
|
||||
head_ = chunkIt_->begin();
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if there are no more bytes to enumerate.
|
||||
|
@ -957,8 +993,9 @@ class MOZ_NON_TEMPORARY_CLASS LifoAllocScope
|
|||
}
|
||||
|
||||
~LifoAllocScope() {
|
||||
if (shouldRelease)
|
||||
if (shouldRelease) {
|
||||
lifoAlloc->release(mark);
|
||||
}
|
||||
}
|
||||
|
||||
LifoAlloc& alloc() {
|
||||
|
@ -989,24 +1026,27 @@ class LifoAllocPolicy
|
|||
template <typename T>
|
||||
T* maybe_pod_malloc(size_t numElems) {
|
||||
size_t bytes;
|
||||
if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes)))
|
||||
if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
|
||||
return nullptr;
|
||||
}
|
||||
void* p = fb == Fallible ? alloc_.alloc(bytes) : alloc_.allocInfallible(bytes);
|
||||
return static_cast<T*>(p);
|
||||
}
|
||||
template <typename T>
|
||||
T* maybe_pod_calloc(size_t numElems) {
|
||||
T* p = maybe_pod_malloc<T>(numElems);
|
||||
if (MOZ_UNLIKELY(!p))
|
||||
if (MOZ_UNLIKELY(!p)) {
|
||||
return nullptr;
|
||||
}
|
||||
memset(p, 0, numElems * sizeof(T));
|
||||
return p;
|
||||
}
|
||||
template <typename T>
|
||||
T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
|
||||
T* n = maybe_pod_malloc<T>(newSize);
|
||||
if (MOZ_UNLIKELY(!n))
|
||||
if (MOZ_UNLIKELY(!n)) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
|
||||
memcpy(n, p, Min(oldSize * sizeof(T), newSize * sizeof(T)));
|
||||
return n;
|
||||
|
|
|
@ -56,10 +56,12 @@ class ProtectedRegionTree
|
|||
// included in a range, or if an address is already registered as a
|
||||
// protected region.
|
||||
static int compare(const Region& A, const Region& B) {
|
||||
if (A.last < B.first)
|
||||
if (A.last < B.first) {
|
||||
return -1;
|
||||
if (A.first > B.last)
|
||||
}
|
||||
if (A.first > B.last) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
@ -87,8 +89,9 @@ class ProtectedRegionTree
|
|||
MOZ_ASSERT(addr && size);
|
||||
LockGuard<Mutex> guard(lock);
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!tree.insert(Region(addr, size)))
|
||||
if (!tree.insert(Region(addr, size))) {
|
||||
oomUnsafe.crash("Failed to store allocation ID.");
|
||||
}
|
||||
}
|
||||
|
||||
void remove(uintptr_t addr) {
|
||||
|
@ -98,8 +101,9 @@ class ProtectedRegionTree
|
|||
}
|
||||
|
||||
bool isProtected(uintptr_t addr) {
|
||||
if (!addr)
|
||||
if (!addr) {
|
||||
return false;
|
||||
}
|
||||
LockGuard<Mutex> guard(lock);
|
||||
return tree.maybeLookup(Region(addr, 1));
|
||||
}
|
||||
|
@ -131,15 +135,17 @@ MemoryProtectionExceptionHandler::isDisabled()
|
|||
void
|
||||
MemoryProtectionExceptionHandler::addRegion(void* addr, size_t size)
|
||||
{
|
||||
if (sExceptionHandlerInstalled && sProtectedRegionsInit)
|
||||
if (sExceptionHandlerInstalled && sProtectedRegionsInit) {
|
||||
sProtectedRegions.insert(uintptr_t(addr), size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MemoryProtectionExceptionHandler::removeRegion(void* addr)
|
||||
{
|
||||
if (sExceptionHandlerInstalled && sProtectedRegionsInit)
|
||||
if (sExceptionHandlerInstalled && sProtectedRegionsInit) {
|
||||
sProtectedRegions.remove(uintptr_t(addr));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -216,8 +222,9 @@ MemoryProtectionExceptionHandler::install()
|
|||
MOZ_ASSERT(!sExceptionHandlerInstalled);
|
||||
|
||||
// If the exception handler is disabled, report success anyway.
|
||||
if (MemoryProtectionExceptionHandler::isDisabled())
|
||||
if (MemoryProtectionExceptionHandler::isDisabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Install our new exception handler.
|
||||
sVectoredExceptionHandler = AddVectoredExceptionHandler(/* FirstHandler = */ true,
|
||||
|
@ -277,12 +284,13 @@ UnixExceptionHandler(int signum, siginfo_t* info, void* context)
|
|||
|
||||
// Forward to the previous handler which may be a debugger,
|
||||
// the crash reporter or something else entirely.
|
||||
if (sPrevSEGVHandler.sa_flags & SA_SIGINFO)
|
||||
if (sPrevSEGVHandler.sa_flags & SA_SIGINFO) {
|
||||
sPrevSEGVHandler.sa_sigaction(signum, info, context);
|
||||
else if (sPrevSEGVHandler.sa_handler == SIG_DFL || sPrevSEGVHandler.sa_handler == SIG_IGN)
|
||||
} else if (sPrevSEGVHandler.sa_handler == SIG_DFL || sPrevSEGVHandler.sa_handler == SIG_IGN) {
|
||||
sigaction(SIGSEGV, &sPrevSEGVHandler, nullptr);
|
||||
else
|
||||
} else {
|
||||
sPrevSEGVHandler.sa_handler(signum);
|
||||
}
|
||||
|
||||
// If we reach here, we're returning to let the default signal handler deal
|
||||
// with the exception. This is technically undefined behavior, but
|
||||
|
@ -295,8 +303,9 @@ MemoryProtectionExceptionHandler::install()
|
|||
MOZ_ASSERT(!sExceptionHandlerInstalled);
|
||||
|
||||
// If the exception handler is disabled, report success anyway.
|
||||
if (MemoryProtectionExceptionHandler::isDisabled())
|
||||
if (MemoryProtectionExceptionHandler::isDisabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Install our new exception handler and save the previous one.
|
||||
struct sigaction faultHandler = {};
|
||||
|
@ -571,21 +580,25 @@ MachExceptionHandler()
|
|||
previous.behavior, previous.flavor);
|
||||
|
||||
// If we failed even receiving the message, just give up.
|
||||
if (ret != MACH_MSG_SUCCESS)
|
||||
if (ret != MACH_MSG_SUCCESS) {
|
||||
MOZ_CRASH("MachExceptionHandler: mach_msg failed to receive a message!");
|
||||
}
|
||||
|
||||
// Terminate the thread if we're shutting down.
|
||||
if (request.header.msgh_id == sIDQuit)
|
||||
if (request.header.msgh_id == sIDQuit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The only other valid message ID is the one associated with the
|
||||
// EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES behavior we chose.
|
||||
if (request.header.msgh_id != sIDRequest64)
|
||||
if (request.header.msgh_id != sIDRequest64) {
|
||||
MOZ_CRASH("MachExceptionHandler: Unexpected Message ID!");
|
||||
}
|
||||
|
||||
// Make sure we can understand the exception we received.
|
||||
if (request.exception != EXC_BAD_ACCESS || request.code_count != 2)
|
||||
if (request.exception != EXC_BAD_ACCESS || request.code_count != 2) {
|
||||
MOZ_CRASH("MachExceptionHandler: Unexpected exception type!");
|
||||
}
|
||||
|
||||
// Get the address that the offending code tried to access.
|
||||
uintptr_t address = uintptr_t(request.code[1]);
|
||||
|
@ -606,8 +619,9 @@ MachExceptionHandler()
|
|||
// If the previous handler requested thread state, get it here.
|
||||
stateCount = THREAD_STATE_MAX;
|
||||
ret = thread_get_state(request.thread.name, previous.flavor, state, &stateCount);
|
||||
if (ret != KERN_SUCCESS)
|
||||
if (ret != KERN_SUCCESS) {
|
||||
MOZ_CRASH("MachExceptionHandler: Could not get the thread state to forward!");
|
||||
}
|
||||
}
|
||||
|
||||
// Depending on the behavior of the previous handler, the forwarded
|
||||
|
@ -653,8 +667,9 @@ MachExceptionHandler()
|
|||
forward.header.msgh_remote_port = previous.port;
|
||||
ret = mach_msg(&forward.header, MACH_SEND_MSG, forward.header.msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (ret != MACH_MSG_SUCCESS)
|
||||
if (ret != MACH_MSG_SUCCESS) {
|
||||
MOZ_CRASH("MachExceptionHandler: Failed to forward to the previous handler!");
|
||||
}
|
||||
} else {
|
||||
// There was no previous task-level exception handler, so defer to the
|
||||
// host level one instead. We set the return code to KERN_FAILURE to
|
||||
|
@ -671,8 +686,9 @@ MachExceptionHandler()
|
|||
reply.RetCode = KERN_FAILURE;
|
||||
ret = mach_msg(&reply.header, MACH_SEND_MSG, reply.header.msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (ret != MACH_MSG_SUCCESS)
|
||||
if (ret != MACH_MSG_SUCCESS) {
|
||||
MOZ_CRASH("MachExceptionHandler: Failed to forward to the host level!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -690,10 +706,11 @@ TerminateMachExceptionHandlerThread()
|
|||
kern_return_t ret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
|
||||
if (ret == MACH_MSG_SUCCESS)
|
||||
if (ret == MACH_MSG_SUCCESS) {
|
||||
sMachExceptionState->handlerThread.join();
|
||||
else
|
||||
} else {
|
||||
MOZ_CRASH("MachExceptionHandler: Handler thread failed to terminate!");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -703,12 +720,14 @@ MemoryProtectionExceptionHandler::install()
|
|||
MOZ_ASSERT(!sMachExceptionState);
|
||||
|
||||
// If the exception handler is disabled, report success anyway.
|
||||
if (MemoryProtectionExceptionHandler::isDisabled())
|
||||
if (MemoryProtectionExceptionHandler::isDisabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sMachExceptionState = js_new<ExceptionHandlerState>();
|
||||
if (!sMachExceptionState)
|
||||
if (!sMachExceptionState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kern_return_t ret;
|
||||
mach_port_t task = mach_task_self();
|
||||
|
@ -717,8 +736,9 @@ MemoryProtectionExceptionHandler::install()
|
|||
sMachExceptionState->current = {};
|
||||
MachExceptionParameters& current = sMachExceptionState->current;
|
||||
ret = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, ¤t.port);
|
||||
if (ret != KERN_SUCCESS)
|
||||
if (ret != KERN_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Give the new port send rights as well.
|
||||
ret = mach_port_insert_right(task, current.port, current.port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
|
|
|
@ -35,22 +35,25 @@ class MOZ_STACK_CLASS Nestable
|
|||
|
||||
template <typename Predicate /* (Concrete*) -> bool */>
|
||||
static Concrete* findNearest(Concrete* it, Predicate predicate) {
|
||||
while (it && !predicate(it))
|
||||
while (it && !predicate(it)) {
|
||||
it = it->enclosing();
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T* findNearest(Concrete* it) {
|
||||
while (it && !it->template is<T>())
|
||||
while (it && !it->template is<T>()) {
|
||||
it = it->enclosing();
|
||||
}
|
||||
return it ? &it->template as<T>() : nullptr;
|
||||
}
|
||||
|
||||
template <typename T, typename Predicate /* (T*) -> bool */>
|
||||
static T* findNearest(Concrete* it, Predicate predicate) {
|
||||
while (it && (!it->template is<T>() || !predicate(&it->template as<T>())))
|
||||
while (it && (!it->template is<T>() || !predicate(&it->template as<T>()))) {
|
||||
it = it->enclosing();
|
||||
}
|
||||
return it ? &it->template as<T>() : nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,10 +118,12 @@ class OrderedHashTable
|
|||
|
||||
uint32_t buckets = initialBuckets();
|
||||
Data** tableAlloc = alloc.template pod_malloc<Data*>(buckets);
|
||||
if (!tableAlloc)
|
||||
if (!tableAlloc) {
|
||||
return false;
|
||||
for (uint32_t i = 0; i < buckets; i++)
|
||||
}
|
||||
for (uint32_t i = 0; i < buckets; i++) {
|
||||
tableAlloc[i] = nullptr;
|
||||
}
|
||||
|
||||
uint32_t capacity = uint32_t(buckets * fillFactor());
|
||||
Data* dataAlloc = alloc.template pod_malloc<Data>(capacity);
|
||||
|
@ -187,8 +189,9 @@ class OrderedHashTable
|
|||
// If the hashTable is more than 1/4 deleted data, simply rehash in
|
||||
// place to free up some space. Otherwise, grow the table.
|
||||
uint32_t newHashShift = liveCount >= dataCapacity * 0.75 ? hashShift - 1 : hashShift;
|
||||
if (!rehash(newHashShift))
|
||||
if (!rehash(newHashShift)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
h >>= hashShift;
|
||||
|
@ -230,8 +233,9 @@ class OrderedHashTable
|
|||
|
||||
// If many entries have been removed, try to shrink the table.
|
||||
if (hashBuckets() > initialBuckets() && liveCount < dataLength * minDataFill()) {
|
||||
if (!rehash(hashShift + 1))
|
||||
if (!rehash(hashShift + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -344,8 +348,9 @@ class OrderedHashTable
|
|||
: ht(ht), i(0), count(0), prevp(listp), next(*listp)
|
||||
{
|
||||
*prevp = this;
|
||||
if (next)
|
||||
if (next) {
|
||||
next->prevp = &next;
|
||||
}
|
||||
seek();
|
||||
}
|
||||
|
||||
|
@ -354,14 +359,16 @@ class OrderedHashTable
|
|||
: ht(other.ht), i(other.i), count(other.count), prevp(&ht->ranges), next(ht->ranges)
|
||||
{
|
||||
*prevp = this;
|
||||
if (next)
|
||||
if (next) {
|
||||
next->prevp = &next;
|
||||
}
|
||||
}
|
||||
|
||||
~Range() {
|
||||
*prevp = next;
|
||||
if (next)
|
||||
if (next) {
|
||||
next->prevp = prevp;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -369,8 +376,9 @@ class OrderedHashTable
|
|||
Range& operator=(const Range& other) = delete;
|
||||
|
||||
void seek() {
|
||||
while (i < ht->dataLength && Ops::isEmpty(Ops::getKey(ht->data[i].element)))
|
||||
while (i < ht->dataLength && Ops::isEmpty(Ops::getKey(ht->data[i].element))) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -379,10 +387,12 @@ class OrderedHashTable
|
|||
*/
|
||||
void onRemove(uint32_t j) {
|
||||
MOZ_ASSERT(valid());
|
||||
if (j < i)
|
||||
if (j < i) {
|
||||
count--;
|
||||
if (j == i)
|
||||
}
|
||||
if (j == i) {
|
||||
seek();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -470,8 +480,9 @@ class OrderedHashTable
|
|||
// key's hash code changed since it was inserted, breaking the
|
||||
// hash code invariant.)
|
||||
Data** ep = &ht->hashTable[oldHash];
|
||||
while (*ep != &entry)
|
||||
while (*ep != &entry) {
|
||||
ep = &(*ep)->chain;
|
||||
}
|
||||
*ep = entry.chain;
|
||||
|
||||
// Add it to the new hash chain. We could just insert it at the
|
||||
|
@ -481,8 +492,9 @@ class OrderedHashTable
|
|||
// depends on this invariant, so it's fine to kill it if
|
||||
// needed.
|
||||
ep = &ht->hashTable[newHash];
|
||||
while (*ep && *ep > &entry)
|
||||
while (*ep && *ep > &entry) {
|
||||
ep = &(*ep)->chain;
|
||||
}
|
||||
entry.chain = *ep;
|
||||
*ep = &entry;
|
||||
}
|
||||
|
@ -545,12 +557,14 @@ class OrderedHashTable
|
|||
* when the entry was added to the table.
|
||||
*/
|
||||
void rekeyOneEntry(const Key& current, const Key& newKey, const T& element) {
|
||||
if (current == newKey)
|
||||
if (current == newKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
Data* entry = lookup(current, prepareHash(current));
|
||||
if (!entry)
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashNumber oldHash = prepareHash(current) >> hashShift;
|
||||
HashNumber newHash = prepareHash(newKey) >> hashShift;
|
||||
|
@ -563,8 +577,9 @@ class OrderedHashTable
|
|||
// key's hash code changed since it was inserted, breaking the
|
||||
// hash code invariant.)
|
||||
Data** ep = &hashTable[oldHash];
|
||||
while (*ep != entry)
|
||||
while (*ep != entry) {
|
||||
ep = &(*ep)->chain;
|
||||
}
|
||||
*ep = entry->chain;
|
||||
|
||||
// Add it to the new hash chain. We could just insert it at the
|
||||
|
@ -574,8 +589,9 @@ class OrderedHashTable
|
|||
// depends on this invariant, so it's fine to kill it if
|
||||
// needed.
|
||||
ep = &hashTable[newHash];
|
||||
while (*ep && *ep > entry)
|
||||
while (*ep && *ep > entry) {
|
||||
ep = &(*ep)->chain;
|
||||
}
|
||||
entry->chain = *ep;
|
||||
*ep = entry;
|
||||
}
|
||||
|
@ -630,8 +646,9 @@ class OrderedHashTable
|
|||
}
|
||||
|
||||
static void destroyData(Data* data, uint32_t length) {
|
||||
for (Data* p = data + length; p != data; )
|
||||
for (Data* p = data + length; p != data; ) {
|
||||
(--p)->~Data();
|
||||
}
|
||||
}
|
||||
|
||||
void freeData(Data* data, uint32_t length, uint32_t capacity) {
|
||||
|
@ -641,8 +658,9 @@ class OrderedHashTable
|
|||
|
||||
Data* lookup(const Lookup& l, HashNumber h) {
|
||||
for (Data* e = hashTable[h >> hashShift]; e; e = e->chain) {
|
||||
if (Ops::match(Ops::getKey(e->element), l))
|
||||
if (Ops::match(Ops::getKey(e->element), l)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -660,15 +678,17 @@ class OrderedHashTable
|
|||
|
||||
/* Compact the entries in |data| and rehash them. */
|
||||
void rehashInPlace() {
|
||||
for (uint32_t i = 0, N = hashBuckets(); i < N; i++)
|
||||
for (uint32_t i = 0, N = hashBuckets(); i < N; i++) {
|
||||
hashTable[i] = nullptr;
|
||||
}
|
||||
Data* wp = data;
|
||||
Data* end = data + dataLength;
|
||||
for (Data* rp = data; rp != end; rp++) {
|
||||
if (!Ops::isEmpty(Ops::getKey(rp->element))) {
|
||||
HashNumber h = prepareHash(Ops::getKey(rp->element)) >> hashShift;
|
||||
if (rp != wp)
|
||||
if (rp != wp) {
|
||||
wp->element = std::move(rp->element);
|
||||
}
|
||||
wp->chain = hashTable[h];
|
||||
hashTable[h] = wp;
|
||||
wp++;
|
||||
|
@ -676,8 +696,9 @@ class OrderedHashTable
|
|||
}
|
||||
MOZ_ASSERT(wp == data + liveCount);
|
||||
|
||||
while (wp != end)
|
||||
while (wp != end) {
|
||||
(--end)->~Data();
|
||||
}
|
||||
dataLength = liveCount;
|
||||
compacted();
|
||||
}
|
||||
|
@ -700,10 +721,12 @@ class OrderedHashTable
|
|||
size_t newHashBuckets =
|
||||
size_t(1) << (js::kHashNumberBits - newHashShift);
|
||||
Data** newHashTable = alloc.template pod_malloc<Data*>(newHashBuckets);
|
||||
if (!newHashTable)
|
||||
if (!newHashTable) {
|
||||
return false;
|
||||
for (uint32_t i = 0; i < newHashBuckets; i++)
|
||||
}
|
||||
for (uint32_t i = 0; i < newHashBuckets; i++) {
|
||||
newHashTable[i] = nullptr;
|
||||
}
|
||||
|
||||
uint32_t newCapacity = uint32_t(newHashBuckets * fillFactor());
|
||||
Data* newData = alloc.template pod_malloc<Data>(newCapacity);
|
||||
|
@ -820,8 +843,9 @@ class OrderedHashMap
|
|||
|
||||
void rekeyOneEntry(const Key& current, const Key& newKey) {
|
||||
const Entry* e = get(current);
|
||||
if (!e)
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
return impl.rekeyOneEntry(current, newKey, Entry(newKey, e->value));
|
||||
}
|
||||
|
||||
|
|
|
@ -104,17 +104,19 @@ class PageProtectingVector final
|
|||
MOZ_ASSERT(protectUsedEnabled || protectUnusedEnabled);
|
||||
size_t nextPage = (pageSize - (uintptr_t(begin() + length()) & pageMask)) >> elemShift;
|
||||
size_t nextResize = capacity() - length();
|
||||
if (MOZ_LIKELY(nextPage <= nextResize))
|
||||
if (MOZ_LIKELY(nextPage <= nextResize)) {
|
||||
elemsUntilTest = intptr_t(nextPage);
|
||||
else
|
||||
} else {
|
||||
elemsUntilTest = intptr_t(nextResize);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void setTestInitial() {
|
||||
if (MOZ_LIKELY(!protectUsedEnabled && !protectUnusedEnabled))
|
||||
if (MOZ_LIKELY(!protectUsedEnabled && !protectUnusedEnabled)) {
|
||||
elemsUntilTest = intptr_t(capacity() - length());
|
||||
else
|
||||
} else {
|
||||
resetTest();
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void resetForNewBuffer() {
|
||||
|
@ -129,58 +131,69 @@ class PageProtectingVector final
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void poisonNewBuffer() {
|
||||
if (!PoisonUnused)
|
||||
if (!PoisonUnused) {
|
||||
return;
|
||||
}
|
||||
T* addr = begin() + length();
|
||||
size_t toPoison = (capacity() - length()) * sizeof(T);
|
||||
memset(addr, PoisonPattern, toPoison);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void addExceptionHandler() {
|
||||
if (MOZ_UNLIKELY(protectUsedEnabled || protectUnusedEnabled))
|
||||
if (MOZ_UNLIKELY(protectUsedEnabled || protectUnusedEnabled)) {
|
||||
MemoryProtectionExceptionHandler::addRegion(begin(), capacity() << elemShift);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void removeExceptionHandler() {
|
||||
if (MOZ_UNLIKELY(protectUsedEnabled || protectUnusedEnabled))
|
||||
if (MOZ_UNLIKELY(protectUsedEnabled || protectUnusedEnabled)) {
|
||||
MemoryProtectionExceptionHandler::removeRegion(begin());
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void protectUsed() {
|
||||
if (MOZ_LIKELY(!protectUsedEnabled))
|
||||
if (MOZ_LIKELY(!protectUsedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(currPage <= initPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(currPage <= initPage)) {
|
||||
return;
|
||||
}
|
||||
T* addr = reinterpret_cast<T*>(initPage << pageShift);
|
||||
size_t size = (currPage - initPage) << pageShift;
|
||||
gc::MakePagesReadOnly(addr, size);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void unprotectUsed() {
|
||||
if (MOZ_LIKELY(!protectUsedEnabled))
|
||||
if (MOZ_LIKELY(!protectUsedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(currPage <= initPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(currPage <= initPage)) {
|
||||
return;
|
||||
}
|
||||
T* addr = reinterpret_cast<T*>(initPage << pageShift);
|
||||
size_t size = (currPage - initPage) << pageShift;
|
||||
gc::UnprotectPages(addr, size);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void protectUnused() {
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled))
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(currPage >= lastPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(currPage >= lastPage)) {
|
||||
return;
|
||||
}
|
||||
T* addr = reinterpret_cast<T*>((currPage + 1) << pageShift);
|
||||
size_t size = (lastPage - currPage) << pageShift;
|
||||
gc::ProtectPages(addr, size);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void unprotectUnused() {
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled))
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(currPage >= lastPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(currPage >= lastPage)) {
|
||||
return;
|
||||
}
|
||||
T* addr = reinterpret_cast<T*>((currPage + 1) << pageShift);
|
||||
size_t size = (lastPage - currPage) << pageShift;
|
||||
gc::UnprotectPages(addr, size);
|
||||
|
@ -202,36 +215,45 @@ class PageProtectingVector final
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void protectUnusedPartial(size_t curr, size_t next) {
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled))
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(next > lastPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(next > lastPage)) {
|
||||
--next;
|
||||
if (MOZ_UNLIKELY(next == curr))
|
||||
}
|
||||
if (MOZ_UNLIKELY(next == curr)) {
|
||||
return;
|
||||
}
|
||||
void* addr = reinterpret_cast<T*>((curr + 1) << pageShift);
|
||||
size_t size = (next - curr) << pageShift;
|
||||
gc::ProtectPages(addr, size);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void unprotectUnusedPartial(size_t curr, size_t next) {
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled))
|
||||
if (MOZ_LIKELY(!protectUnusedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(next > lastPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(next > lastPage)) {
|
||||
--next;
|
||||
if (MOZ_UNLIKELY(next == curr))
|
||||
}
|
||||
if (MOZ_UNLIKELY(next == curr)) {
|
||||
return;
|
||||
}
|
||||
void* addr = reinterpret_cast<T*>((curr + 1) << pageShift);
|
||||
size_t size = (next - curr) << pageShift;
|
||||
gc::UnprotectPages(addr, size);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void protectUsedPartial(size_t curr, size_t next) {
|
||||
if (MOZ_LIKELY(!protectUsedEnabled))
|
||||
if (MOZ_LIKELY(!protectUsedEnabled)) {
|
||||
return;
|
||||
if (MOZ_UNLIKELY(curr < initPage))
|
||||
}
|
||||
if (MOZ_UNLIKELY(curr < initPage)) {
|
||||
++curr;
|
||||
if (MOZ_UNLIKELY(next == curr))
|
||||
}
|
||||
if (MOZ_UNLIKELY(next == curr)) {
|
||||
return;
|
||||
}
|
||||
void* addr = reinterpret_cast<T*>(curr << pageShift);
|
||||
size_t size = (next - curr) << pageShift;
|
||||
gc::MakePagesReadOnly(addr, size);
|
||||
|
@ -306,8 +328,9 @@ class PageProtectingVector final
|
|||
protectUsedEnabled(false),
|
||||
protectUnusedEnabled(false)
|
||||
{
|
||||
if (gc::SystemPageSize() != pageSize)
|
||||
if (gc::SystemPageSize() != pageSize) {
|
||||
usable = false;
|
||||
}
|
||||
protectNewBuffer();
|
||||
}
|
||||
|
||||
|
@ -346,8 +369,9 @@ class PageProtectingVector final
|
|||
if (MOZ_UNLIKELY(protectUsedEnabled)) {
|
||||
uintptr_t l = uintptr_t(first) >> pageShift;
|
||||
uintptr_t r = uintptr_t(first + size - 1) >> pageShift;
|
||||
if (r >= initPage && l < currPage)
|
||||
if (r >= initPage && l < currPage) {
|
||||
unprotectRegionSlow(l, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,8 +383,9 @@ class PageProtectingVector final
|
|||
if (MOZ_UNLIKELY(protectUsedEnabled)) {
|
||||
uintptr_t l = uintptr_t(first) >> pageShift;
|
||||
uintptr_t r = uintptr_t(first + size - 1) >> pageShift;
|
||||
if (r >= initPage && l < currPage)
|
||||
if (r >= initPage && l < currPage) {
|
||||
reprotectRegionSlow(l, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,24 +402,27 @@ class PageProtectingVector final
|
|||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE MOZ_MUST_USE bool reserve(size_t size) {
|
||||
if (MOZ_LIKELY(size <= capacity()))
|
||||
if (MOZ_LIKELY(size <= capacity())) {
|
||||
return vector.reserve(size);
|
||||
}
|
||||
return reserveSlow(size);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
MOZ_ALWAYS_INLINE void infallibleAppend(const U* values, size_t size) {
|
||||
elemsUntilTest -= size;
|
||||
if (MOZ_LIKELY(elemsUntilTest >= 0))
|
||||
if (MOZ_LIKELY(elemsUntilTest >= 0)) {
|
||||
return vector.infallibleAppend(values, size);
|
||||
}
|
||||
infallibleAppendSlow(values, size);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
MOZ_ALWAYS_INLINE MOZ_MUST_USE bool append(const U* values, size_t size) {
|
||||
elemsUntilTest -= size;
|
||||
if (MOZ_LIKELY(elemsUntilTest >= 0))
|
||||
if (MOZ_LIKELY(elemsUntilTest >= 0)) {
|
||||
return vector.append(values, size);
|
||||
}
|
||||
return appendSlow(values, size);
|
||||
}
|
||||
};
|
||||
|
@ -403,10 +431,12 @@ template<typename T, size_t A, class B, bool C, bool D, size_t E, bool F, uint8_
|
|||
MOZ_NEVER_INLINE void
|
||||
PageProtectingVector<T, A, B, C, D, E, F, G>::unprotectRegionSlow(uintptr_t l, uintptr_t r)
|
||||
{
|
||||
if (l < initPage)
|
||||
if (l < initPage) {
|
||||
l = initPage;
|
||||
if (r >= currPage)
|
||||
}
|
||||
if (r >= currPage) {
|
||||
r = currPage - 1;
|
||||
}
|
||||
T* addr = reinterpret_cast<T*>(l << pageShift);
|
||||
size_t size = (r - l + 1) << pageShift;
|
||||
gc::UnprotectPages(addr, size);
|
||||
|
@ -416,10 +446,12 @@ template<typename T, size_t A, class B, bool C, bool D, size_t E, bool F, uint8_
|
|||
MOZ_NEVER_INLINE void
|
||||
PageProtectingVector<T, A, B, C, D, E, F, G>::reprotectRegionSlow(uintptr_t l, uintptr_t r)
|
||||
{
|
||||
if (l < initPage)
|
||||
if (l < initPage) {
|
||||
l = initPage;
|
||||
if (r >= currPage)
|
||||
}
|
||||
if (r >= currPage) {
|
||||
r = currPage - 1;
|
||||
}
|
||||
T* addr = reinterpret_cast<T*>(l << pageShift);
|
||||
size_t size = (r - l + 1) << pageShift;
|
||||
gc::MakePagesReadOnly(addr, size);
|
||||
|
@ -449,8 +481,9 @@ template<typename U>
|
|||
MOZ_NEVER_INLINE MOZ_MUST_USE bool
|
||||
PageProtectingVector<T, A, B, C, D, E, F, G>::appendSlow(const U* values, size_t size)
|
||||
{
|
||||
if (MOZ_LIKELY(length() + size <= capacity()))
|
||||
if (MOZ_LIKELY(length() + size <= capacity())) {
|
||||
return appendNewPage(values, size);
|
||||
}
|
||||
return appendNewBuffer(values, size);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,9 @@ class PriorityQueue
|
|||
}
|
||||
|
||||
MOZ_MUST_USE bool insert(const T& v) {
|
||||
if (!heap.append(v))
|
||||
if (!heap.append(v)) {
|
||||
return false;
|
||||
}
|
||||
siftUp(heap.length() - 1);
|
||||
return true;
|
||||
}
|
||||
|
@ -115,8 +116,9 @@ class PriorityQueue
|
|||
while (n > 0) {
|
||||
size_t parent = (n - 1) / 2;
|
||||
|
||||
if (P::priority(heap[parent]) > P::priority(heap[n]))
|
||||
if (P::priority(heap[parent]) > P::priority(heap[n])) {
|
||||
break;
|
||||
}
|
||||
|
||||
swap(n, parent);
|
||||
n = parent;
|
||||
|
|
|
@ -35,14 +35,16 @@ MergeArrayRuns(T* dst, const T* src, size_t run1, size_t run2, Comparator c)
|
|||
/* Copy runs already in sorted order. */
|
||||
const T* b = src + run1;
|
||||
bool lessOrEqual;
|
||||
if (!c(b[-1], b[0], &lessOrEqual))
|
||||
if (!c(b[-1], b[0], &lessOrEqual)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lessOrEqual) {
|
||||
/* Runs are not already sorted, merge them. */
|
||||
for (const T* a = src;;) {
|
||||
if (!c(*a, *b, &lessOrEqual))
|
||||
if (!c(*a, *b, &lessOrEqual)) {
|
||||
return false;
|
||||
}
|
||||
if (lessOrEqual) {
|
||||
*dst++ = *a++;
|
||||
if (!--run1) {
|
||||
|
@ -83,8 +85,9 @@ MergeSort(T* array, size_t nelems, T* scratch, Comparator c)
|
|||
{
|
||||
const size_t INS_SORT_LIMIT = 3;
|
||||
|
||||
if (nelems <= 1)
|
||||
if (nelems <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply insertion sort to small chunks to reduce the number of merge
|
||||
|
@ -92,20 +95,24 @@ MergeSort(T* array, size_t nelems, T* scratch, Comparator c)
|
|||
*/
|
||||
for (size_t lo = 0; lo < nelems; lo += INS_SORT_LIMIT) {
|
||||
size_t hi = lo + INS_SORT_LIMIT;
|
||||
if (hi >= nelems)
|
||||
if (hi >= nelems) {
|
||||
hi = nelems;
|
||||
}
|
||||
for (size_t i = lo + 1; i != hi; i++) {
|
||||
for (size_t j = i; ;) {
|
||||
bool lessOrEqual;
|
||||
if (!c(array[j - 1], array[j], &lessOrEqual))
|
||||
if (!c(array[j - 1], array[j], &lessOrEqual)) {
|
||||
return false;
|
||||
if (lessOrEqual)
|
||||
}
|
||||
if (lessOrEqual) {
|
||||
break;
|
||||
}
|
||||
T tmp = array[j - 1];
|
||||
array[j - 1] = array[j];
|
||||
array[j] = tmp;
|
||||
if (--j == lo)
|
||||
if (--j == lo) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,15 +127,17 @@ MergeSort(T* array, size_t nelems, T* scratch, Comparator c)
|
|||
break;
|
||||
}
|
||||
size_t run2 = (run <= nelems - hi) ? run : nelems - hi;
|
||||
if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c))
|
||||
if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
T* swap = vec1;
|
||||
vec1 = vec2;
|
||||
vec2 = swap;
|
||||
}
|
||||
if (vec1 == scratch)
|
||||
if (vec1 == scratch) {
|
||||
detail::CopyNonEmptyArray(array, scratch, nelems);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,16 +70,18 @@ class SplayTree
|
|||
|
||||
T* maybeLookup(const T& v)
|
||||
{
|
||||
if (!root)
|
||||
if (!root) {
|
||||
return nullptr;
|
||||
}
|
||||
Node* last = lookup(v);
|
||||
return (C::compare(v, last->item) == 0) ? &(last->item) : nullptr;
|
||||
}
|
||||
|
||||
bool contains(const T& v, T* res)
|
||||
{
|
||||
if (!root)
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
Node* last = lookup(v);
|
||||
splay(last);
|
||||
checkCoherency();
|
||||
|
@ -93,8 +95,9 @@ class SplayTree
|
|||
MOZ_MUST_USE bool insert(const T& v)
|
||||
{
|
||||
Node* element = allocateNode(v);
|
||||
if (!element)
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
root = element;
|
||||
|
@ -131,13 +134,15 @@ class SplayTree
|
|||
Node* swapChild;
|
||||
if (root->left) {
|
||||
swap = root->left;
|
||||
while (swap->right)
|
||||
while (swap->right) {
|
||||
swap = swap->right;
|
||||
}
|
||||
swapChild = swap->left;
|
||||
} else if (root->right) {
|
||||
swap = root->right;
|
||||
while (swap->left)
|
||||
while (swap->left) {
|
||||
swap = swap->left;
|
||||
}
|
||||
swapChild = swap->right;
|
||||
} else {
|
||||
freeNode(root);
|
||||
|
@ -147,12 +152,14 @@ class SplayTree
|
|||
|
||||
// The selected node has at most one child, in swapChild. Detach it
|
||||
// from the subtree by replacing it with that child.
|
||||
if (swap == swap->parent->left)
|
||||
if (swap == swap->parent->left) {
|
||||
swap->parent->left = swapChild;
|
||||
else
|
||||
} else {
|
||||
swap->parent->right = swapChild;
|
||||
if (swapChild)
|
||||
}
|
||||
if (swapChild) {
|
||||
swapChild->parent = swap->parent;
|
||||
}
|
||||
|
||||
root->item = swap->item;
|
||||
freeNode(swap);
|
||||
|
@ -176,12 +183,13 @@ class SplayTree
|
|||
do {
|
||||
parent = node;
|
||||
int c = C::compare(v, node->item);
|
||||
if (c == 0)
|
||||
if (c == 0) {
|
||||
return node;
|
||||
else if (c < 0)
|
||||
} else if (c < 0) {
|
||||
node = node->left;
|
||||
else
|
||||
} else {
|
||||
node = node->right;
|
||||
}
|
||||
} while (node);
|
||||
return parent;
|
||||
}
|
||||
|
@ -240,8 +248,9 @@ class SplayTree
|
|||
// y c ==> a x
|
||||
// a b b c
|
||||
parent->left = node->right;
|
||||
if (node->right)
|
||||
if (node->right) {
|
||||
node->right->parent = parent;
|
||||
}
|
||||
node->right = parent;
|
||||
} else {
|
||||
MOZ_ASSERT(parent->right == node);
|
||||
|
@ -249,17 +258,19 @@ class SplayTree
|
|||
// a y ==> x c
|
||||
// b c a b
|
||||
parent->right = node->left;
|
||||
if (node->left)
|
||||
if (node->left) {
|
||||
node->left->parent = parent;
|
||||
}
|
||||
node->left = parent;
|
||||
}
|
||||
node->parent = parent->parent;
|
||||
parent->parent = node;
|
||||
if (Node* grandparent = node->parent) {
|
||||
if (grandparent->left == parent)
|
||||
if (grandparent->left == parent) {
|
||||
grandparent->left = node;
|
||||
else
|
||||
} else {
|
||||
grandparent->right = node;
|
||||
}
|
||||
} else {
|
||||
root = node;
|
||||
}
|
||||
|
@ -268,8 +279,9 @@ class SplayTree
|
|||
template <class Op>
|
||||
void forEachInner(Op op, Node* node)
|
||||
{
|
||||
if (!node)
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
forEachInner<Op>(op, node->left);
|
||||
op(node->item);
|
||||
|
@ -279,10 +291,12 @@ class SplayTree
|
|||
void checkCoherency() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (!enableCheckCoherency)
|
||||
if (!enableCheckCoherency) {
|
||||
return;
|
||||
if (!root)
|
||||
}
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(root->parent == nullptr);
|
||||
const Node* node = root;
|
||||
const Node* minimum = nullptr;
|
||||
|
@ -328,8 +342,9 @@ class SplayTree
|
|||
minimum = node;
|
||||
}
|
||||
|
||||
if (node->right != prev && node->right != nullptr)
|
||||
if (node->right != prev && node->right != nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!node->parent) {
|
||||
|
@ -337,8 +352,9 @@ class SplayTree
|
|||
// We reached the root node either because we came back from
|
||||
// the right hand side, or because the root node had a
|
||||
// single child.
|
||||
if (node->right == prev || node->right == nullptr)
|
||||
if (node->right == prev || node->right == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Go to the right node which we have not visited yet.
|
||||
|
|
|
@ -45,10 +45,12 @@ class TraceableFifo : public js::Fifo<T, MinInlineCapacity, AllocPolicy>
|
|||
TraceableFifo& operator=(const TraceableFifo&) = delete;
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
for (size_t i = 0; i < this->front_.length(); ++i)
|
||||
for (size_t i = 0; i < this->front_.length(); ++i) {
|
||||
JS::GCPolicy<T>::trace(trc, &this->front_[i], "fifo element");
|
||||
for (size_t i = 0; i < this->rear_.length(); ++i)
|
||||
}
|
||||
for (size_t i = 0; i < this->rear_.length(); ++i) {
|
||||
JS::GCPolicy<T>::trace(trc, &this->rear_[i], "fifo element");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "jit/JitFrames-inl.h"
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
#include "vm/BytecodeUtil-inl.h"
|
||||
#include "vm/GeckoProfiler-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/JSScript-inl.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
|
@ -252,6 +253,7 @@ jit::BaselineCompile(JSContext* cx, JSScript* script, bool forceDebugInstrumenta
|
|||
MOZ_ASSERT(!script->hasBaselineScript());
|
||||
MOZ_ASSERT(script->canBaselineCompile());
|
||||
MOZ_ASSERT(IsBaselineEnabled(cx));
|
||||
AutoGeckoProfilerEntry pseudoFrame(cx, "Baseline script compilation");
|
||||
|
||||
script->ensureNonLazyCanonicalFunction();
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
#include "jit/shared/Lowering-shared-inl.h"
|
||||
#include "vm/Debugger-inl.h"
|
||||
#include "vm/EnvironmentObject-inl.h"
|
||||
#include "vm/GeckoProfiler-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/JSScript-inl.h"
|
||||
#include "vm/Realm-inl.h"
|
||||
|
@ -2374,6 +2375,7 @@ Compile(JSContext* cx, HandleScript script, BaselineFrame* osrFrame, jsbytecode*
|
|||
MOZ_ASSERT(jit::IsIonEnabled(cx));
|
||||
MOZ_ASSERT(jit::IsBaselineEnabled(cx));
|
||||
MOZ_ASSERT_IF(osrPc != nullptr, LoopEntryCanIonOsr(osrPc));
|
||||
AutoGeckoProfilerEntry pseudoFrame(cx, "Ion script compilation");
|
||||
|
||||
if (!script->hasBaselineScript()) {
|
||||
return Method_Skipped;
|
||||
|
|
|
@ -378,9 +378,17 @@ var FormAssistant = {
|
|||
scrollY += rect.top + parseInt(top);
|
||||
}
|
||||
|
||||
// The rect computed above is relative to the origin of the viewport frame,
|
||||
// i.e. the layout viewport origin, but the consumer of the
|
||||
// FormAssist::AutoCompleteResult messaage expects a rect relative to
|
||||
// the visual viewport origin, so translate between the two.
|
||||
let offsetX = {}, offsetY = {};
|
||||
aElement.ownerGlobal.windowUtils
|
||||
.getVisualViewportOffsetRelativeToLayoutViewport(offsetX, offsetY);
|
||||
|
||||
return {
|
||||
x: r.left + scrollX,
|
||||
y: r.top + scrollY,
|
||||
x: r.left + scrollX - offsetX.value,
|
||||
y: r.top + scrollY - offsetY.value,
|
||||
w: r.width,
|
||||
h: r.height,
|
||||
};
|
||||
|
|
|
@ -152,8 +152,8 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
mPatchedFns.append(reinterpret_cast<void*>(readOnlyTargetFn.GetBaseAddress()));
|
||||
return true;
|
||||
return mPatchedFns.append(
|
||||
reinterpret_cast<void*>(readOnlyTargetFn.GetBaseAddress()));
|
||||
}
|
||||
|
||||
bool WriteHook(const ReadOnlyTargetFunction<MMPolicyT>& aFn,
|
||||
|
@ -169,7 +169,7 @@ public:
|
|||
|
||||
// Check that the 5 bytes before the function are NOP's or INT 3's,
|
||||
const uint8_t nopOrBp[] = { 0x90, 0xCC };
|
||||
if (!writableFn.VerifyValuesAreOneOf<uint8_t, 5>(nopOrBp)) {
|
||||
if (!writableFn.template VerifyValuesAreOneOf<uint8_t, 5>(nopOrBp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,8 @@ public:
|
|||
|
||||
// (These look backwards because little-endian)
|
||||
const uint16_t possibleEncodings[] = { 0xFF8B, 0xFF89 };
|
||||
if (!writableFn.VerifyValuesAreOneOf<uint16_t, 1>(possibleEncodings, 5)) {
|
||||
if (!writableFn.template VerifyValuesAreOneOf<uint16_t, 1>(
|
||||
possibleEncodings, 5)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,27 @@
|
|||
namespace mozilla {
|
||||
namespace interceptor {
|
||||
|
||||
#if defined(_M_IX86)
|
||||
|
||||
template <typename T> bool
|
||||
CommitAndWriteShortInternal(const T& aMMPolicy, void* aDest, uint16_t aValue);
|
||||
|
||||
template <> inline bool
|
||||
CommitAndWriteShortInternal<MMPolicyInProcess>(
|
||||
const MMPolicyInProcess& aMMPolicy, void* aDest, uint16_t aValue)
|
||||
{
|
||||
return aMMPolicy.WriteAtomic(aDest, aValue);
|
||||
}
|
||||
|
||||
template <> inline bool
|
||||
CommitAndWriteShortInternal<MMPolicyOutOfProcess>(
|
||||
const MMPolicyOutOfProcess& aMMPolicy, void* aDest, uint16_t aValue)
|
||||
{
|
||||
return aMMPolicy.Write(aDest, &aValue, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
#endif // defined(_M_IX86)
|
||||
|
||||
template <typename MMPolicy>
|
||||
class MOZ_STACK_CLASS WritableTargetFunction final
|
||||
{
|
||||
|
@ -233,24 +254,6 @@ public:
|
|||
}
|
||||
|
||||
#if defined(_M_IX86)
|
||||
private:
|
||||
template <typename T>
|
||||
bool CommitAndWriteShortInternal(const T& aMMPolicy, void* aDest, uint16_t aValue);
|
||||
|
||||
template <>
|
||||
bool CommitAndWriteShortInternal<MMPolicyInProcess>(const MMPolicyInProcess& aMMPolicy,
|
||||
void* aDest, uint16_t aValue)
|
||||
{
|
||||
return aMMPolicy.WriteAtomic(aDest, aValue);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool CommitAndWriteShortInternal<MMPolicyOutOfProcess>(const MMPolicyOutOfProcess& aMMPolicy,
|
||||
void* aDest, uint16_t aValue)
|
||||
{
|
||||
return aMMPolicy.Write(aDest, &aValue, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Commits any dirty writes, and then writes a short, atomically if possible.
|
||||
|
@ -595,82 +598,81 @@ private:
|
|||
uint8_t const * const mBase;
|
||||
};
|
||||
|
||||
template <typename TargetMMPolicy> class TargetBytesPtr;
|
||||
|
||||
template<>
|
||||
class TargetBytesPtr<MMPolicyInProcess>
|
||||
{
|
||||
public:
|
||||
typedef TargetBytesPtr<MMPolicyInProcess> Type;
|
||||
|
||||
static Type Make(const MMPolicyInProcess& aMMPolicy, const void* aFunc)
|
||||
{
|
||||
return TargetBytesPtr(aMMPolicy, aFunc);
|
||||
}
|
||||
|
||||
static Type CopyFromOffset(const TargetBytesPtr& aOther,
|
||||
const uint32_t aOffsetFromOther)
|
||||
{
|
||||
return TargetBytesPtr(aOther, aOffsetFromOther);
|
||||
}
|
||||
|
||||
ReadOnlyTargetBytes<MMPolicyInProcess>* operator->()
|
||||
{
|
||||
return &mTargetBytes;
|
||||
}
|
||||
|
||||
TargetBytesPtr(TargetBytesPtr&& aOther)
|
||||
: mTargetBytes(std::move(aOther.mTargetBytes))
|
||||
{
|
||||
}
|
||||
|
||||
TargetBytesPtr(const TargetBytesPtr& aOther)
|
||||
: mTargetBytes(aOther.mTargetBytes)
|
||||
{
|
||||
}
|
||||
|
||||
TargetBytesPtr& operator=(const TargetBytesPtr&) = delete;
|
||||
TargetBytesPtr& operator=(TargetBytesPtr&&) = delete;
|
||||
|
||||
private:
|
||||
TargetBytesPtr(const MMPolicyInProcess& aMMPolicy, const void* aFunc)
|
||||
: mTargetBytes(aMMPolicy, aFunc)
|
||||
{
|
||||
}
|
||||
|
||||
TargetBytesPtr(const TargetBytesPtr& aOther,
|
||||
const uint32_t aOffsetFromOther)
|
||||
: mTargetBytes(aOther.mTargetBytes, aOffsetFromOther)
|
||||
{
|
||||
}
|
||||
|
||||
ReadOnlyTargetBytes<MMPolicyInProcess> mTargetBytes;
|
||||
};
|
||||
|
||||
template <>
|
||||
class TargetBytesPtr<MMPolicyOutOfProcess>
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<ReadOnlyTargetBytes<MMPolicyOutOfProcess>> Type;
|
||||
|
||||
static Type Make(const MMPolicyOutOfProcess& aMMPolicy, const void* aFunc)
|
||||
{
|
||||
return std::make_shared<ReadOnlyTargetBytes<MMPolicyOutOfProcess>>(
|
||||
aMMPolicy, aFunc);
|
||||
}
|
||||
|
||||
static Type CopyFromOffset(const Type& aOther,
|
||||
const uint32_t aOffsetFromOther)
|
||||
{
|
||||
return std::make_shared<ReadOnlyTargetBytes<MMPolicyOutOfProcess>>(
|
||||
*aOther, aOffsetFromOther);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MMPolicy>
|
||||
class MOZ_STACK_CLASS ReadOnlyTargetFunction final
|
||||
{
|
||||
template <typename TargetMMPolicy>
|
||||
class TargetBytesPtr;
|
||||
|
||||
template<>
|
||||
class TargetBytesPtr<MMPolicyInProcess>
|
||||
{
|
||||
public:
|
||||
typedef TargetBytesPtr<MMPolicyInProcess> Type;
|
||||
|
||||
static Type Make(const MMPolicyInProcess& aMMPolicy, const void* aFunc)
|
||||
{
|
||||
return std::move(TargetBytesPtr(aMMPolicy, aFunc));
|
||||
}
|
||||
|
||||
static Type CopyFromOffset(const TargetBytesPtr& aOther,
|
||||
const uint32_t aOffsetFromOther)
|
||||
{
|
||||
return std::move(TargetBytesPtr(aOther, aOffsetFromOther));
|
||||
}
|
||||
|
||||
ReadOnlyTargetBytes<MMPolicyInProcess>* operator->()
|
||||
{
|
||||
return &mTargetBytes;
|
||||
}
|
||||
|
||||
TargetBytesPtr(TargetBytesPtr&& aOther)
|
||||
: mTargetBytes(std::move(aOther.mTargetBytes))
|
||||
{
|
||||
}
|
||||
|
||||
TargetBytesPtr(const TargetBytesPtr& aOther)
|
||||
: mTargetBytes(aOther.mTargetBytes)
|
||||
{
|
||||
}
|
||||
|
||||
TargetBytesPtr& operator=(const TargetBytesPtr&) = delete;
|
||||
TargetBytesPtr& operator=(TargetBytesPtr&&) = delete;
|
||||
|
||||
private:
|
||||
TargetBytesPtr(const MMPolicyInProcess& aMMPolicy, const void* aFunc)
|
||||
: mTargetBytes(aMMPolicy, aFunc)
|
||||
{
|
||||
}
|
||||
|
||||
TargetBytesPtr(const TargetBytesPtr& aOther,
|
||||
const uint32_t aOffsetFromOther)
|
||||
: mTargetBytes(aOther.mTargetBytes, aOffsetFromOther)
|
||||
{
|
||||
}
|
||||
|
||||
ReadOnlyTargetBytes<MMPolicyInProcess> mTargetBytes;
|
||||
};
|
||||
|
||||
template <>
|
||||
class TargetBytesPtr<MMPolicyOutOfProcess>
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<ReadOnlyTargetBytes<MMPolicyOutOfProcess>> Type;
|
||||
|
||||
static Type Make(const MMPolicyOutOfProcess& aMMPolicy, const void* aFunc)
|
||||
{
|
||||
return std::move(std::make_shared<ReadOnlyTargetBytes<MMPolicyOutOfProcess>>(
|
||||
aMMPolicy, aFunc));
|
||||
}
|
||||
|
||||
static Type CopyFromOffset(const Type& aOther,
|
||||
const uint32_t aOffsetFromOther)
|
||||
{
|
||||
return std::move(std::make_shared<ReadOnlyTargetBytes<MMPolicyOutOfProcess>>(
|
||||
*aOther, aOffsetFromOther));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
ReadOnlyTargetFunction(const MMPolicy& aMMPolicy, const void* aFunc)
|
||||
: mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, aFunc))
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface mozIDOMWindowProxy;
|
||||
interface nsIDocShell;
|
||||
interface nsITransportSecurityInfo;
|
||||
|
||||
[scriptable, uuid(718c662a-f810-4a80-a6c9-0b1810ecade2)]
|
||||
interface nsISecureBrowserUI : nsISupports
|
||||
{
|
||||
void init(in mozIDOMWindowProxy window);
|
||||
void setDocShell(in nsIDocShell docShell);
|
||||
void init(in nsIDocShell docShell);
|
||||
|
||||
readonly attribute unsigned long state;
|
||||
readonly attribute nsITransportSecurityInfo secInfo;
|
||||
|
|
|
@ -391,6 +391,11 @@ CookieServiceChild::GetCookieStringFromCookieHashTable(nsIURI *a
|
|||
if (!nsCookieService::DomainMatches(cookie, hostFromURI))
|
||||
continue;
|
||||
|
||||
// We don't show HttpOnly cookies in content processes.
|
||||
if (cookie->IsHttpOnly()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the cookie is secure and the host scheme isn't, we can't send it
|
||||
if (cookie->IsSecure() && !isSecure)
|
||||
continue;
|
||||
|
@ -521,9 +526,7 @@ CookieServiceChild::RecordDocumentCookie(nsCookie *aCookie,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!aCookie->IsHttpOnly()) {
|
||||
cookiesList->AppendElement(aCookie);
|
||||
}
|
||||
cookiesList->AppendElement(aCookie);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -677,19 +680,38 @@ CookieServiceChild::SetCookieStringInternal(nsIURI *aHostURI,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCookieKey key(baseDomain, attrs);
|
||||
CookiesList *cookies = mCookiesMap.Get(key);
|
||||
|
||||
nsCString serverTimeString(aServerTime);
|
||||
int64_t serverTime = nsCookieService::ParseServerTime(serverTimeString);
|
||||
bool moreCookies;
|
||||
do {
|
||||
nsCookieAttributes cookieAttributes;
|
||||
bool canSetCookie = false;
|
||||
nsCookieKey key(baseDomain, attrs);
|
||||
moreCookies = nsCookieService::CanSetCookie(aHostURI, key, cookieAttributes,
|
||||
requireHostMatch, cookieStatus,
|
||||
cookieString, serverTime, aFromHttp,
|
||||
aChannel, mLeaveSecureAlone,
|
||||
canSetCookie, mThirdPartyUtil);
|
||||
|
||||
// We need to see if the cookie we're setting would overwrite an httponly
|
||||
// one. This would not affect anything we send over the net (those come from
|
||||
// the parent, which already checks this), but script could see an
|
||||
// inconsistent view of things.
|
||||
if (cookies && canSetCookie && !aFromHttp) {
|
||||
for (uint32_t i = 0; i < cookies->Length(); ++i) {
|
||||
RefPtr<nsCookie> cookie = cookies->ElementAt(i);
|
||||
if (cookie->Name().Equals(cookieAttributes.name) &&
|
||||
cookie->Host().Equals(cookieAttributes.host) &&
|
||||
cookie->Path().Equals(cookieAttributes.path) &&
|
||||
cookie->IsHttpOnly()) {
|
||||
// Can't overwrite an httponly cookie from a script context.
|
||||
canSetCookie = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canSetCookie) {
|
||||
SetCookieInternal(cookieAttributes, attrs, aChannel,
|
||||
aFromHttp, permissionService);
|
||||
|
|
|
@ -100,10 +100,12 @@ CookieServiceParent::RemoveBatchDeletedCookies(nsIArray *aCookieList) {
|
|||
auto cookie = static_cast<nsCookie*>(xpcCookie.get());
|
||||
attrs = cookie->OriginAttributesRef();
|
||||
GetInfoFromCookie(cookie, cookieStruct);
|
||||
if (!cookie->IsHttpOnly()) {
|
||||
cookieStructList.AppendElement(cookieStruct);
|
||||
attrsList.AppendElement(attrs);
|
||||
if (cookie->IsHttpOnly()) {
|
||||
// Child only needs to exist if an HttpOnly cookie exists, not its value
|
||||
cookieStruct.value() = "";
|
||||
}
|
||||
cookieStructList.AppendElement(cookieStruct);
|
||||
attrsList.AppendElement(attrs);
|
||||
}
|
||||
Unused << SendRemoveBatchDeletedCookies(cookieStructList, attrsList);
|
||||
}
|
||||
|
@ -121,9 +123,10 @@ CookieServiceParent::RemoveCookie(nsICookie *aCookie)
|
|||
OriginAttributes attrs = cookie->OriginAttributesRef();
|
||||
CookieStruct cookieStruct;
|
||||
GetInfoFromCookie(cookie, cookieStruct);
|
||||
if (!cookie->IsHttpOnly()) {
|
||||
Unused << SendRemoveCookie(cookieStruct, attrs);
|
||||
if (cookie->IsHttpOnly()) {
|
||||
cookieStruct.value() = "";
|
||||
}
|
||||
Unused << SendRemoveCookie(cookieStruct, attrs);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -133,6 +136,9 @@ CookieServiceParent::AddCookie(nsICookie *aCookie)
|
|||
OriginAttributes attrs = cookie->OriginAttributesRef();
|
||||
CookieStruct cookieStruct;
|
||||
GetInfoFromCookie(cookie, cookieStruct);
|
||||
if (cookie->IsHttpOnly()) {
|
||||
cookieStruct.value() = "";
|
||||
}
|
||||
Unused << SendAddCookie(cookieStruct, attrs);
|
||||
}
|
||||
|
||||
|
@ -191,7 +197,9 @@ CookieServiceParent::SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCoo
|
|||
nsCookie *cookie = aFoundCookieList.ElementAt(i);
|
||||
CookieStruct* cookieStruct = aCookiesList.AppendElement();
|
||||
cookieStruct->name() = cookie->Name();
|
||||
cookieStruct->value() = cookie->Value();
|
||||
if (!cookie->IsHttpOnly()) {
|
||||
cookieStruct->value() = cookie->Value();
|
||||
}
|
||||
cookieStruct->host() = cookie->Host();
|
||||
cookieStruct->path() = cookie->Path();
|
||||
cookieStruct->expiry() = cookie->Expiry();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "nsIAuthPromptProvider.h"
|
||||
#include "nsIEncodedChannel.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsIForcePendingChannel.h"
|
||||
#include "mozilla/ipc/IPCStreamUtils.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "mozilla/net/RedirectChannelRegistrar.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsIStorageStream.h"
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
# coding=utf8
|
||||
|
||||
# Any copyright is dedicated to the Public Domain.
|
||||
# http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
from __future__ import absolute_import
|
||||
import fluent.syntax.ast as FTL
|
||||
from fluent.migrate.helpers import transforms_from, VARIABLE_REFERENCE
|
||||
from fluent.migrate import REPLACE, COPY
|
||||
|
||||
def migrate(ctx):
|
||||
"""Bug 1486936 - Convert about:config to using Fluent for localization, part {index}."""
|
||||
|
||||
ctx.add_transforms(
|
||||
"toolkit/toolkit/about/aboutConfig.ftl",
|
||||
"toolkit/toolkit/about/aboutConfig.ftl",
|
||||
transforms_from(
|
||||
"""
|
||||
config-window =
|
||||
.title = { COPY(from_path, "window.title") }
|
||||
config-about-warning-title =
|
||||
.value = { COPY(from_path, "aboutWarningTitle.label") }
|
||||
config-about-warning-text = { COPY(from_path, "aboutWarningText.label") }
|
||||
config-about-warning-button =
|
||||
.label = { COPY(from_path, "aboutWarningButton2.label") }
|
||||
config-about-warning-checkbox =
|
||||
.label = { COPY(from_path, "aboutWarningCheckbox.label") }
|
||||
config-search-prefs =
|
||||
.value = { COPY(from_path, "searchPrefs.label") }
|
||||
.accesskey = { COPY(from_path, "searchPrefs.accesskey") }
|
||||
config-focus-search =
|
||||
.key = { COPY(from_path, "focusSearch.key") }
|
||||
config-focus-search-2 =
|
||||
.key = { COPY(from_path, "focusSearch2.key") }
|
||||
config-pref-column =
|
||||
.label = { COPY(from_path, "prefColumn.label") }
|
||||
config-lock-column =
|
||||
.label = { COPY(from_path, "lockColumn.label") }
|
||||
config-type-column =
|
||||
.label = { COPY(from_path, "typeColumn.label") }
|
||||
config-value-column =
|
||||
.label = { COPY(from_path, "valueColumn.label") }
|
||||
config-pref-column-header =
|
||||
.tooltip = { COPY(from_path, "prefColumnHeader.tooltip") }
|
||||
config-column-chooser =
|
||||
.tooltip = { COPY(from_path, "columnChooser.tooltip") }
|
||||
config-copy-pref =
|
||||
.key = { COPY(from_path, "copyPref.key") }
|
||||
.label = { COPY(from_path, "copyPref.label") }
|
||||
.accesskey = { COPY(from_path, "copyPref.accesskey") }
|
||||
config-copy-name =
|
||||
.label = { COPY(from_path, "copyName.label") }
|
||||
.accesskey = { COPY(from_path, "copyName.accesskey") }
|
||||
config-copy-value =
|
||||
.label = { COPY(from_path, "copyValue.label") }
|
||||
.accesskey = { COPY(from_path, "copyValue.accesskey") }
|
||||
config-modify =
|
||||
.label = { COPY(from_path, "modify.label") }
|
||||
.accesskey = { COPY(from_path, "modify.accesskey") }
|
||||
config-toggle =
|
||||
.label = { COPY(from_path, "toggle.label") }
|
||||
.accesskey = { COPY(from_path, "toggle.accesskey") }
|
||||
config-reset =
|
||||
.label = { COPY(from_path, "reset.label") }
|
||||
.accesskey = { COPY(from_path, "reset.accesskey") }
|
||||
config-new =
|
||||
.label = { COPY(from_path, "new.label") }
|
||||
.accesskey = { COPY(from_path, "new.accesskey") }
|
||||
config-string =
|
||||
.label = { COPY(from_path, "string.label") }
|
||||
.accesskey = { COPY(from_path, "string.accesskey") }
|
||||
config-integer =
|
||||
.label = { COPY(from_path, "integer.label") }
|
||||
.accesskey = { COPY(from_path, "integer.accesskey") }
|
||||
config-boolean =
|
||||
.label = { COPY(from_path, "boolean.label") }
|
||||
.accesskey = { COPY(from_path, "boolean.accesskey") }
|
||||
""", from_path="toolkit/chrome/global/config.dtd"))
|
||||
|
||||
ctx.add_transforms(
|
||||
"toolkit/toolkit/about/aboutConfig.ftl",
|
||||
"toolkit/toolkit/about/aboutConfig.ftl",
|
||||
transforms_from(
|
||||
"""
|
||||
config-default = { COPY(from_path, "default") }
|
||||
config-modified = { COPY(from_path, "modified") }
|
||||
config-locked = { COPY(from_path, "locked") }
|
||||
config-property-string = { COPY(from_path, "string") }
|
||||
config-property-int = { COPY(from_path, "int") }
|
||||
config-property-bool = { COPY(from_path, "bool") }
|
||||
config-new-prompt = { COPY(from_path, "new_prompt") }
|
||||
config-nan-title = { COPY(from_path, "nan_title") }
|
||||
config-nan-text = { COPY(from_path, "nan_text") }
|
||||
""", from_path="toolkit/chrome/global/config.properties"))
|
||||
|
||||
ctx.add_transforms(
|
||||
"toolkit/toolkit/about/aboutConfig.ftl",
|
||||
"toolkit/toolkit/about/aboutConfig.ftl",
|
||||
[
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("config-new-title"),
|
||||
value=REPLACE(
|
||||
"toolkit/chrome/global/config.properties",
|
||||
"new_title",
|
||||
{
|
||||
"%S": VARIABLE_REFERENCE(
|
||||
"type"
|
||||
),
|
||||
}
|
||||
)
|
||||
),
|
||||
FTL.Message(
|
||||
id=FTL.Identifier("config-modify-title"),
|
||||
value=REPLACE(
|
||||
"toolkit/chrome/global/config.properties",
|
||||
"modify_title",
|
||||
{
|
||||
"%S": VARIABLE_REFERENCE(
|
||||
"type"
|
||||
),
|
||||
}
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
|
@ -206,16 +206,20 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
// certificates.
|
||||
if (mCertDBTrustType == trustSSL) {
|
||||
bool isCertRevoked;
|
||||
nsresult nsrv = mCertBlocklist->IsCertRevoked(
|
||||
candidateCert->derIssuer.data,
|
||||
candidateCert->derIssuer.len,
|
||||
candidateCert->serialNumber.data,
|
||||
candidateCert->serialNumber.len,
|
||||
candidateCert->derSubject.data,
|
||||
candidateCert->derSubject.len,
|
||||
candidateCert->derPublicKey.data,
|
||||
candidateCert->derPublicKey.len,
|
||||
&isCertRevoked);
|
||||
|
||||
nsAutoCString encIssuer;
|
||||
nsAutoCString encSerial;
|
||||
nsAutoCString encSubject;
|
||||
nsAutoCString encPubKey;
|
||||
|
||||
nsresult nsrv = BuildRevocationCheckStrings(candidateCert.get(), encIssuer, encSerial, encSubject, encPubKey);
|
||||
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
nsrv = mCertBlocklist->IsCertRevoked(
|
||||
encIssuer, encSerial, encSubject, encPubKey, &isCertRevoked);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
@ -1285,6 +1289,47 @@ DefaultServerNicknameForCert(const CERTCertificate* cert,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BuildRevocationCheckStrings(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& encIssuer,
|
||||
/*out*/ nsCString& encSerial,
|
||||
/*out*/ nsCString& encSubject,
|
||||
/*out*/ nsCString& encPubKey)
|
||||
{
|
||||
// Convert issuer, serial, subject and pubKey data to Base64 encoded DER
|
||||
nsDependentCSubstring issuerString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->derIssuer.data),
|
||||
cert->derIssuer.len);
|
||||
nsDependentCSubstring serialString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->serialNumber.data),
|
||||
cert->serialNumber.len);
|
||||
nsDependentCSubstring subjectString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->derSubject.data),
|
||||
cert->derSubject.len);
|
||||
nsDependentCSubstring pubKeyString(
|
||||
BitwiseCast<char*, uint8_t*>(cert->derPublicKey.data),
|
||||
cert->derPublicKey.len);
|
||||
|
||||
nsresult rv = Base64Encode(issuerString, encIssuer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(serialString, encSerial);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(subjectString, encSubject);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Encode(pubKeyString, encPubKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of certificates representing a verified certificate path from an
|
||||
* end-entity certificate to a trust anchor, imports the intermediate
|
||||
|
|
|
@ -58,6 +58,30 @@ void UnloadLoadableRoots();
|
|||
nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& nickname);
|
||||
|
||||
/**
|
||||
* Build strings of base64 encoded issuer, serial, subject and public key data
|
||||
* from the supplied certificate for use in revocation checks.
|
||||
*
|
||||
* @param cert
|
||||
* The CERTCertificate* from which to extract the data.
|
||||
* @param out encIssuer
|
||||
* The string to populate with base64 encoded issuer data.
|
||||
* @param out encSerial
|
||||
* The string to populate with base64 encoded serial number data.
|
||||
* @param out encSubject
|
||||
* The string to populate with base64 encoded subject data.
|
||||
* @param out encPubKey
|
||||
* The string to populate with base64 encoded public key data.
|
||||
* @return
|
||||
* NS_OK, unless there's a Base64 encoding problem, in which case
|
||||
* NS_ERROR_FAILURE.
|
||||
*/
|
||||
nsresult BuildRevocationCheckStrings(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& encIssuer,
|
||||
/*out*/ nsCString& encSerial,
|
||||
/*out*/ nsCString& encSubject,
|
||||
/*out*/ nsCString& encPubKey);
|
||||
|
||||
void SaveIntermediateCerts(const UniqueCERTCertList& certList);
|
||||
|
||||
class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "pkix/pkixnss.h"
|
||||
|
@ -44,17 +45,19 @@ CSTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
|
|||
return MapPRErrorCodeToResult(PR_GetError());
|
||||
}
|
||||
|
||||
nsAutoCString encIssuer;
|
||||
nsAutoCString encSerial;
|
||||
nsAutoCString encSubject;
|
||||
nsAutoCString encPubKey;
|
||||
|
||||
nsresult nsrv = BuildRevocationCheckStrings(candidateCert.get(), encIssuer, encSerial, encSubject, encPubKey);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
||||
bool isCertRevoked;
|
||||
nsresult nsrv = mCertBlocklist->IsCertRevoked(
|
||||
candidateCert->derIssuer.data,
|
||||
candidateCert->derIssuer.len,
|
||||
candidateCert->serialNumber.data,
|
||||
candidateCert->serialNumber.len,
|
||||
candidateCert->derSubject.data,
|
||||
candidateCert->derSubject.len,
|
||||
candidateCert->derPublicKey.data,
|
||||
candidateCert->derPublicKey.len,
|
||||
&isCertRevoked);
|
||||
nsrv = mCertBlocklist->IsCertRevoked(
|
||||
encIssuer, encSerial, encSubject, encPubKey, &isCertRevoked);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
|
|
|
@ -508,48 +508,54 @@ CertBlocklist::SaveEntries()
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
|
||||
uint32_t aIssuerLength,
|
||||
const uint8_t* aSerial,
|
||||
uint32_t aSerialLength,
|
||||
const uint8_t* aSubject,
|
||||
uint32_t aSubjectLength,
|
||||
const uint8_t* aPubKey,
|
||||
uint32_t aPubKeyLength,
|
||||
CertBlocklist::IsCertRevoked(const nsACString& aIssuerString,
|
||||
const nsACString& aSerialNumberString,
|
||||
const nsACString& aSubjectString,
|
||||
const nsACString& aPubKeyString,
|
||||
bool* _retval)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, ("CertBlocklist::IsCertRevoked"));
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked?"));
|
||||
nsresult rv = EnsureBackingFileInitialized(lock);
|
||||
nsCString decodedIssuer;
|
||||
nsCString decodedSerial;
|
||||
nsCString decodedSubject;
|
||||
nsCString decodedPubKey;
|
||||
|
||||
nsresult rv = Base64Decode(aIssuerString, decodedIssuer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Decode(aSerialNumberString, decodedSerial);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Decode(aSubjectString, decodedSubject);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = Base64Decode(aPubKeyString, decodedPubKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Input issuer;
|
||||
Input serial;
|
||||
if (issuer.Init(aIssuer, aIssuerLength) != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (serial.Init(aSerial, aSerialLength) != Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CertBlocklistItem issuerSerial(aIssuer, aIssuerLength, aSerial, aSerialLength,
|
||||
BlockByIssuerAndSerial);
|
||||
|
||||
nsAutoCString encDN;
|
||||
nsAutoCString encOther;
|
||||
|
||||
issuerSerial.ToBase64(encDN, encOther);
|
||||
rv = EnsureBackingFileInitialized(lock);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
CertBlocklistItem issuerSerial(
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedIssuer.get()),
|
||||
decodedIssuer.Length(),
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedSerial.get()),
|
||||
decodedSerial.Length(),
|
||||
BlockByIssuerAndSerial);
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog,
|
||||
LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked issuer %s - serial %s",
|
||||
encDN.get(), encOther.get()));
|
||||
PromiseFlatCString(aIssuerString).get(),
|
||||
PromiseFlatCString(aSerialNumberString).get()));
|
||||
|
||||
*_retval = mBlocklist.Contains(issuerSerial);
|
||||
|
||||
|
@ -567,7 +573,9 @@ CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = crypto->Update(aPubKey, aPubKeyLength);
|
||||
rv = crypto->Update(
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedPubKey.get()),
|
||||
decodedPubKey.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -579,20 +587,24 @@ CertBlocklist::IsCertRevoked(const uint8_t* aIssuer,
|
|||
}
|
||||
|
||||
CertBlocklistItem subjectPubKey(
|
||||
aSubject,
|
||||
static_cast<size_t>(aSubjectLength),
|
||||
BitwiseCast<const uint8_t*, const char*>(decodedSubject.get()),
|
||||
decodedSubject.Length(),
|
||||
BitwiseCast<const uint8_t*, const char*>(hashString.get()),
|
||||
hashString.Length(),
|
||||
BlockBySubjectAndPubKey);
|
||||
|
||||
rv = subjectPubKey.ToBase64(encDN, encOther);
|
||||
nsCString encodedHash;
|
||||
rv = Base64Encode(hashString, encodedHash);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked subject %s - pubKey hash %s",
|
||||
encDN.get(), encOther.get()));
|
||||
MOZ_LOG(gCertBlockPRLog,
|
||||
LogLevel::Warning,
|
||||
("CertBlocklist::IsCertRevoked subject %s - pubKeyHash %s (pubKey %s)",
|
||||
PromiseFlatCString(aSubjectString).get(),
|
||||
PromiseFlatCString(encodedHash).get(),
|
||||
PromiseFlatCString(aPubKeyString).get()));
|
||||
*_retval = mBlocklist.Contains(subjectPubKey);
|
||||
|
||||
MOZ_LOG(gCertBlockPRLog, LogLevel::Warning,
|
||||
|
|
|
@ -42,20 +42,16 @@ interface nsICertBlocklist : nsISupports {
|
|||
|
||||
/**
|
||||
* Check if a certificate is blocked.
|
||||
* issuer - issuer name, DER encoded
|
||||
* serial - serial number, DER encoded
|
||||
* subject - subject name, DER encoded
|
||||
* pubkey - public key, DER encoded
|
||||
* issuer - issuer name, DER, Base64 encoded
|
||||
* serial - serial number, DER, BAse64 encoded
|
||||
* subject - subject name, DER, Base64 encoded
|
||||
* pubkey - public key, DER, Base64 encoded
|
||||
*/
|
||||
[must_use]
|
||||
boolean isCertRevoked([const, array, size_is(issuer_length)] in octet issuer,
|
||||
in unsigned long issuer_length,
|
||||
[const, array, size_is(serial_length)] in octet serial,
|
||||
in unsigned long serial_length,
|
||||
[const, array, size_is(subject_length)] in octet subject,
|
||||
in unsigned long subject_length,
|
||||
[const, array, size_is(pubkey_length)] in octet pubkey,
|
||||
in unsigned long pubkey_length);
|
||||
boolean isCertRevoked(in ACString issuer,
|
||||
in ACString serial,
|
||||
in ACString subject,
|
||||
in ACString pubkey);
|
||||
|
||||
/**
|
||||
* Check that the blocklist data is current. Specifically, that the current
|
||||
|
|
|
@ -32,29 +32,27 @@ NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
|
|||
nsISupportsWeakReference)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSecureBrowserUIImpl::Init(mozIDOMWindowProxy* aWindow)
|
||||
nsSecureBrowserUIImpl::Init(nsIDocShell* aDocShell)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_ARG(aWindow);
|
||||
NS_ENSURE_ARG(aDocShell);
|
||||
|
||||
auto* piwindow = nsPIDOMWindowOuter::From(aWindow);
|
||||
nsIDocShell* docShell = piwindow->GetDocShell();
|
||||
aDocShell->SetSecurityUI(this);
|
||||
|
||||
// The Docshell will own the SecureBrowserUI object
|
||||
if (!docShell) {
|
||||
return NS_ERROR_FAILURE;
|
||||
// The Docshell will own the SecureBrowserUI object, we keep a weak ref.
|
||||
nsresult rv;
|
||||
mDocShell = do_GetWeakReference(aDocShell, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
docShell->SetSecurityUI(this);
|
||||
|
||||
// hook up to the webprogress notifications.
|
||||
nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
|
||||
nsCOMPtr<nsIWebProgress> wp(do_GetInterface(aDocShell));
|
||||
if (!wp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Save this so we can compare it to the web progress in OnLocationChange.
|
||||
nsresult rv;
|
||||
mWebProgress = do_GetWeakReference(wp, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
|
@ -92,16 +90,6 @@ nsSecureBrowserUIImpl::GetSecInfo(nsITransportSecurityInfo** result)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSecureBrowserUIImpl::SetDocShell(nsIDocShell* aDocShell)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_ARG(aDocShell);
|
||||
nsresult rv;
|
||||
mDocShell = do_GetWeakReference(aDocShell, &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Ask the docShell if we've blocked or loaded any mixed or tracking content.
|
||||
void
|
||||
nsSecureBrowserUIImpl::CheckForBlockedContent()
|
||||
|
|
|
@ -162,10 +162,6 @@ var addonManager = Cc["@mozilla.org/addons/integration;1"]
|
|||
.QueryInterface(Ci.nsITimerCallback);
|
||||
addonManager.observe(null, "addons-startup", null);
|
||||
|
||||
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
function verify_cert(file, expectedError) {
|
||||
let ee = constructCertFromFile(file);
|
||||
return checkCertErrorGeneric(certDB, ee, expectedError,
|
||||
|
@ -190,19 +186,8 @@ function load_cert(cert, trust) {
|
|||
|
||||
function test_is_revoked(certList, issuerString, serialString, subjectString,
|
||||
pubKeyString) {
|
||||
let issuer = converter.convertToByteArray(issuerString || "", {});
|
||||
let serial = converter.convertToByteArray(serialString || "", {});
|
||||
let subject = converter.convertToByteArray(subjectString || "", {});
|
||||
let pubKey = converter.convertToByteArray(pubKeyString || "", {});
|
||||
|
||||
return certList.isCertRevoked(issuer,
|
||||
issuerString ? issuerString.length : 0,
|
||||
serial,
|
||||
serialString ? serialString.length : 0,
|
||||
subject,
|
||||
subjectString ? subjectString.length : 0,
|
||||
pubKey,
|
||||
pubKeyString ? pubKeyString.length : 0);
|
||||
return certList.isCertRevoked(btoa(issuerString), btoa(serialString),
|
||||
btoa(subjectString), btoa(pubKeyString));
|
||||
}
|
||||
|
||||
function fetch_blocklist() {
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче