merge mozilla-central to mozilla-inbound. r=merge a=merge on a CLOSED TREE

This commit is contained in:
Sebastian Hengst 2017-10-14 00:11:06 +02:00
Родитель 89ddab8643 373bdd8602
Коммит 4bf9d6e663
164 изменённых файлов: 3344 добавлений и 1939 удалений

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

@ -102,3 +102,47 @@ RootAccessibleWrap::DocumentActivated(DocAccessible* aDocument)
}
}
}
STDMETHODIMP
RootAccessibleWrap::accNavigate(
/* [in] */ long navDir,
/* [optional][in] */ VARIANT varStart,
/* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
{
// Special handling for NAVRELATION_EMBEDS.
// When we only have a single process, this can be handled the same way as
// any other relation.
// However, for multi process, the normal relation mechanism doesn't work
// because it can't handle remote objects.
if (navDir != NAVRELATION_EMBEDS ||
varStart.vt != VT_I4 || varStart.lVal != CHILDID_SELF) {
// We only handle EMBEDS on the root here.
// Forward to the base implementation.
return DocAccessibleWrap::accNavigate(navDir, varStart, pvarEndUpAt);
}
if (!pvarEndUpAt) {
return E_INVALIDARG;
}
Accessible* target = nullptr;
// Get the document in the active tab.
ProxyAccessible* docProxy = GetPrimaryRemoteTopLevelContentDoc();
if (docProxy) {
target = WrapperFor(docProxy);
} else {
// The base implementation could handle this, but we may as well
// just handle it here.
Relation rel = RelationByType(RelationType::EMBEDS);
target = rel.Next();
}
if (!target) {
return E_FAIL;
}
VariantInit(pvarEndUpAt);
pvarEndUpAt->pdispVal = NativeAccessible(target);
pvarEndUpAt->vt = VT_DISPATCH;
return S_OK;
}

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

@ -38,6 +38,11 @@ public:
*/
already_AddRefed<IUnknown> GetInternalUnknown();
virtual /* [id] */ HRESULT STDMETHODCALLTYPE accNavigate(
/* [in] */ long navDir,
/* [optional][in] */ VARIANT varStart,
/* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt) override;
private:
// DECLARE_AGGREGATABLE declares the internal IUnknown methods as well as
// mInternalUnknown.

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

@ -1464,6 +1464,7 @@ var gBrowserInit = {
BrowserOffline.init();
IndexedDBPromptHelper.init();
CanvasPermissionPromptHelper.init();
if (AppConstants.E10S_TESTING_ONLY)
gRemoteTabsUI.init();
@ -1897,6 +1898,7 @@ var gBrowserInit = {
}
BrowserOffline.uninit();
IndexedDBPromptHelper.uninit();
CanvasPermissionPromptHelper.uninit();
PanelUI.uninit();
AutoShowBookmarksToolbar.uninit();
}
@ -5717,17 +5719,29 @@ var gUIDensity = {
mode = this.getCurrentDensity().mode;
}
let doc = document.documentElement;
switch (mode) {
case this.MODE_COMPACT:
doc.setAttribute("uidensity", "compact");
break;
case this.MODE_TOUCH:
doc.setAttribute("uidensity", "touch");
break;
default:
doc.removeAttribute("uidensity");
break;
let docs = [
document.documentElement,
SidebarUI.browser.contentDocument.documentElement,
];
for (let doc of docs) {
switch (mode) {
case this.MODE_COMPACT:
doc.setAttribute("uidensity", "compact");
break;
case this.MODE_TOUCH:
doc.setAttribute("uidensity", "touch");
break;
default:
doc.removeAttribute("uidensity");
break;
}
}
let tree = SidebarUI.browser.contentDocument.querySelector(".sidebar-placesTree");
if (tree) {
// Tree items don't update their styles without changing some property on the
// parent tree element, like background-color or border. See bug 1407399.
tree.style.border = "1px";
tree.style.border = "";
}
TabsInTitlebar.updateAppearance(true);
@ -6669,6 +6683,82 @@ var IndexedDBPromptHelper = {
}
};
var CanvasPermissionPromptHelper = {
_permissionsPrompt: "canvas-permissions-prompt",
_notificationIcon: "canvas-notification-icon",
init() {
Services.obs.addObserver(this, this._permissionsPrompt);
},
uninit() {
Services.obs.removeObserver(this, this._permissionsPrompt);
},
// aSubject is an nsIBrowser (e10s) or an nsIDOMWindow (non-e10s).
// aData is an URL string.
observe(aSubject, aTopic, aData) {
if (aTopic != this._permissionsPrompt) {
return;
}
let browser;
if (aSubject instanceof Ci.nsIDOMWindow) {
let contentWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
browser = gBrowser.getBrowserForContentWindow(contentWindow);
} else {
browser = aSubject.QueryInterface(Ci.nsIBrowser);
}
let uri = Services.io.newURI(aData);
if (gBrowser.selectedBrowser !== browser) {
// Must belong to some other window.
return;
}
let message = gNavigatorBundle.getFormattedString("canvas.siteprompt", [ uri.asciiHost ]);
function setCanvasPermission(aURI, aPerm, aPersistent) {
Services.perms.add(aURI, "canvas/extractData", aPerm,
aPersistent ? Ci.nsIPermissionManager.EXPIRE_NEVER
: Ci.nsIPermissionManager.EXPIRE_SESSION);
}
let mainAction = {
label: gNavigatorBundle.getString("canvas.allow"),
accessKey: gNavigatorBundle.getString("canvas.allow.accesskey"),
callback(state) {
setCanvasPermission(uri, Ci.nsIPermissionManager.ALLOW_ACTION,
state && state.checkboxChecked);
}
};
let secondaryActions = [{
label: gNavigatorBundle.getString("canvas.notAllow"),
accessKey: gNavigatorBundle.getString("canvas.notAllow.accesskey"),
callback(state) {
setCanvasPermission(uri, Ci.nsIPermissionManager.DENY_ACTION,
state && state.checkboxChecked);
}
}];
let checkbox = {
// In PB mode, we don't want the "always remember" checkbox
show: !PrivateBrowsingUtils.isWindowPrivate(window)
};
if (checkbox.show) {
checkbox.checked = true;
checkbox.label = gBrowserBundle.GetStringFromName("canvas.remember");
}
let options = {
checkbox
};
PopupNotifications.show(browser, aTopic, message, this._notificationIcon,
mainAction, secondaryActions, options);
}
};
function CanCloseWindow() {
// Avoid redundant calls to canClose from showing multiple
// PermitUnload dialogs.

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

@ -823,6 +823,8 @@
tooltiptext="&urlbar.geolocationNotificationAnchor.tooltip;"/>
<image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
tooltiptext="&urlbar.addonsNotificationAnchor.tooltip;"/>
<image id="canvas-notification-icon" class="notification-anchor-icon" role="button"
tooltiptext="&urlbar.canvasNotificationAnchor.tooltip;"/>
<image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
tooltiptext="&urlbar.indexedDBNotificationAnchor.tooltip;"/>
<image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"

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

@ -3,6 +3,7 @@ support-files=
head.js
permissions.html
[browser_canvas_fingerprinting_resistance.js]
[browser_permissions.js]
[browser_temporary_permissions.js]
support-files =

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

@ -0,0 +1,122 @@
/**
* When "privacy.resistFingerprinting" is set to true, user permission is
* required for canvas data extraction.
* This tests whether the site permission prompt for canvas data extraction
* works properly.
*/
"use strict";
const kUrl = "https://example.com/";
const kPrincipal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.createCodebasePrincipal(Services.io.newURI(kUrl), {});
const kPermission = "canvas/extractData";
function initTab() {
let contentWindow = content.wrappedJSObject;
let drawCanvas = (fillStyle, id) => {
let contentDocument = contentWindow.document;
let width = 64, height = 64;
let canvas = contentDocument.createElement("canvas");
if (id) {
canvas.setAttribute("id", id);
}
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
contentDocument.body.appendChild(canvas);
let context = canvas.getContext("2d");
context.fillStyle = fillStyle;
context.fillRect(0, 0, width, height);
return canvas;
};
let placeholder = drawCanvas("white");
contentWindow.kPlaceholderData = placeholder.toDataURL();
let canvas = drawCanvas("cyan", "canvas-id-canvas");
isnot(canvas.toDataURL(), contentWindow.kPlaceholderData,
"privacy.resistFingerprinting = false, canvas data != placeholder data");
}
function enableResistFingerprinting() {
return SpecialPowers.pushPrefEnv({
set: [
["privacy.resistFingerprinting", true]
]
});
}
function promisePopupShown() {
return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown")
}
function promisePopupHidden() {
return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden")
}
function extractCanvasData(grantPermission) {
let contentWindow = content.wrappedJSObject;
let canvas = contentWindow.document.getElementById("canvas-id-canvas");
let canvasData = canvas.toDataURL();
if (grantPermission) {
isnot(canvasData, contentWindow.kPlaceholderData,
"privacy.resistFingerprinting = true, permission granted, canvas data != placeholderdata");
} else if (grantPermission === false) {
is(canvasData, contentWindow.kPlaceholderData,
"privacy.resistFingerprinting = true, permission denied, canvas data == placeholderdata");
} else {
is(canvasData, contentWindow.kPlaceholderData,
"privacy.resistFingerprinting = true, requesting permission, canvas data == placeholderdata");
}
}
function triggerCommand(button) {
let notifications = PopupNotifications.panel.childNodes;
let notification = notifications[0];
EventUtils.synthesizeMouseAtCenter(notification[button], {});
}
function triggerMainCommand() {
triggerCommand("button");
}
function triggerSecondaryCommand() {
triggerCommand("secondaryButton");
}
function testPermission() {
return Services.perms.testPermissionFromPrincipal(kPrincipal, kPermission);
}
async function withNewTab(grantPermission, browser) {
await ContentTask.spawn(browser, null, initTab);
await enableResistFingerprinting();
let popupShown = promisePopupShown();
await ContentTask.spawn(browser, null, extractCanvasData);
await popupShown;
let popupHidden = promisePopupHidden();
if (grantPermission) {
triggerMainCommand();
await popupHidden;
is(testPermission(), Services.perms.ALLOW_ACTION, "permission granted");
} else {
triggerSecondaryCommand();
await popupHidden;
is(testPermission(), Services.perms.DENY_ACTION, "permission denied");
}
await ContentTask.spawn(browser, grantPermission, extractCanvasData);
await SpecialPowers.popPrefEnv();
}
async function doTest(grantPermission) {
Services.perms.removeFromPrincipal(kPrincipal, kPermission);
await BrowserTestUtils.withNewTab(kUrl, withNewTab.bind(null, grantPermission));
}
// Tests clicking "Don't Allow" button of the permission prompt.
add_task(doTest.bind(null, false));
// Tests clicking "Allow" button of the permission prompt.
add_task(doTest.bind(null, true));

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

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/AppConstants.jsm");
window.top.gUIDensity.update();
var SidebarUtils = {
handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) {

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

@ -4,59 +4,8 @@
* maximum values.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
let gMaxAvailWidth;
let gMaxAvailHeight;
// We need the chrome UI size of popup windows for testing outerWidth/Height.
let gPopupChromeUIWidth;
let gPopupChromeUIHeight;
const TESTCASES = [
{ settingWidth: 1025, settingHeight: 1050, targetWidth: 1000, targetHeight: 1000 },
{ settingWidth: 9999, settingHeight: 9999, targetWidth: 1000, targetHeight: 1000 },
{ settingWidth: 999, settingHeight: 999, targetWidth: 1000, targetHeight: 1000 },
];
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
gPopupChromeUIWidth = popUpChromeUISize.chromeWidth;
gPopupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(gPopupChromeUIWidth,
gPopupChromeUIHeight);
gMaxAvailWidth = maxAvailSize.maxAvailWidth;
gMaxAvailHeight = maxAvailSize.maxAvailHeight;
});
add_task(async function test_window_open() {
// Open a tab to test window.open().
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
for (let test of TESTCASES) {
// Test 'width' and 'height' of window features.
await testWindowOpen(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, false, gMaxAvailWidth,
gMaxAvailHeight, gPopupChromeUIWidth, gPopupChromeUIHeight);
// test 'outerWidth' and 'outerHeight' of window features.
await testWindowOpen(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, true, gMaxAvailWidth,
gMaxAvailHeight, gPopupChromeUIWidth, gPopupChromeUIHeight);
}
await BrowserTestUtils.removeTab(tab);
});
OpenTest.run([
{settingWidth: 1025, settingHeight: 1050, targetWidth: 1000, targetHeight: 1000},
{settingWidth: 9999, settingHeight: 9999, targetWidth: 1000, targetHeight: 1000},
{settingWidth: 999, settingHeight: 999, targetWidth: 1000, targetHeight: 1000}
]);

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

@ -4,59 +4,8 @@
* middle values.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
let gMaxAvailWidth;
let gMaxAvailHeight;
// We need the chrome UI size of popup windows for testing outerWidth/Height.
let gPopupChromeUIWidth;
let gPopupChromeUIHeight;
const TESTCASES = [
{ settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600 },
{ settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600 },
{ settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600 },
];
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
gPopupChromeUIWidth = popUpChromeUISize.chromeWidth;
gPopupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(gPopupChromeUIWidth,
gPopupChromeUIHeight);
gMaxAvailWidth = maxAvailSize.maxAvailWidth;
gMaxAvailHeight = maxAvailSize.maxAvailHeight;
});
add_task(async function test_window_open() {
// Open a tab to test window.open().
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
for (let test of TESTCASES) {
// Test 'width' and 'height' of window features.
await testWindowOpen(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, false, gMaxAvailWidth,
gMaxAvailHeight, gPopupChromeUIWidth, gPopupChromeUIHeight);
// test 'outerWidth' and 'outerHeight' of window features.
await testWindowOpen(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, true, gMaxAvailWidth,
gMaxAvailHeight, gPopupChromeUIWidth, gPopupChromeUIHeight);
}
await BrowserTestUtils.removeTab(tab);
});
OpenTest.run([
{settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600},
{settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600},
{settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600}
]);

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

@ -4,58 +4,7 @@
* minimum values.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
let gMaxAvailWidth;
let gMaxAvailHeight;
// We need the chrome UI size of popup windows for testing outerWidth/Height.
let gPopupChromeUIWidth;
let gPopupChromeUIHeight;
const TESTCASES = [
{ settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100 },
{ settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100 },
];
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
gPopupChromeUIWidth = popUpChromeUISize.chromeWidth;
gPopupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(gPopupChromeUIWidth,
gPopupChromeUIHeight);
gMaxAvailWidth = maxAvailSize.maxAvailWidth;
gMaxAvailHeight = maxAvailSize.maxAvailHeight;
});
add_task(async function test_window_open() {
// Open a tab to test window.open().
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
for (let test of TESTCASES) {
// Test 'width' and 'height' of window features.
await testWindowOpen(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, false, gMaxAvailWidth,
gMaxAvailHeight, gPopupChromeUIWidth, gPopupChromeUIHeight);
// test 'outerWidth' and 'outerHeight' of window features.
await testWindowOpen(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, true, gMaxAvailWidth,
gMaxAvailHeight, gPopupChromeUIWidth, gPopupChromeUIHeight);
}
await BrowserTestUtils.removeTab(tab);
});
OpenTest.run([
{settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
{settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
]);

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

@ -4,64 +4,8 @@
* test is for maximum values.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
let gMaxAvailWidth;
let gMaxAvailHeight;
// We need the chrome UI size of popup windows for testing outerWidth/Height.
let gPopupChromeUIWidth;
let gPopupChromeUIHeight;
const TESTCASES = [
{ settingWidth: 1025, settingHeight: 1050, targetWidth: 1000, targetHeight: 1000,
initWidth: 200, initHeight: 100 },
{ settingWidth: 9999, settingHeight: 9999, targetWidth: 1000, targetHeight: 1000,
initWidth: 200, initHeight: 100 },
{ settingWidth: 999, settingHeight: 999, targetWidth: 1000, targetHeight: 1000,
initWidth: 200, initHeight: 100 },
];
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
gPopupChromeUIWidth = popUpChromeUISize.chromeWidth;
gPopupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(gPopupChromeUIWidth,
gPopupChromeUIHeight);
gMaxAvailWidth = maxAvailSize.maxAvailWidth;
gMaxAvailHeight = maxAvailSize.maxAvailHeight;
});
add_task(async function test_window_size_setting() {
// Open a tab to test.
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
for (let test of TESTCASES) {
// Test window.innerWidth and window.innerHeight.
await testWindowSizeSetting(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, test.initWidth,
test.initHeight, false, gMaxAvailWidth, gMaxAvailHeight,
gPopupChromeUIWidth, gPopupChromeUIHeight);
// test window.outerWidth and window.outerHeight.
await testWindowSizeSetting(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, test.initWidth,
test.initHeight, true, gMaxAvailWidth, gMaxAvailHeight,
gPopupChromeUIWidth, gPopupChromeUIHeight);
}
await BrowserTestUtils.removeTab(tab);
});
WindowSettingTest.run([
{settingWidth: 1025, settingHeight: 1050, targetWidth: 1000, targetHeight: 1000, initWidth: 200, initHeight: 100},
{settingWidth: 9999, settingHeight: 9999, targetWidth: 1000, targetHeight: 1000, initWidth: 200, initHeight: 100},
{settingWidth: 999, settingHeight: 999, targetWidth: 1000, targetHeight: 1000, initWidth: 200, initHeight: 100}
]);

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

@ -4,64 +4,8 @@
* test is for middle values.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
let gMaxAvailWidth;
let gMaxAvailHeight;
// We need the chrome UI size of popup windows for testing outerWidth/Height.
let gPopupChromeUIWidth;
let gPopupChromeUIHeight;
const TESTCASES = [
{ settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600,
initWidth: 200, initHeight: 100 },
{ settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600,
initWidth: 200, initHeight: 100 },
{ settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600,
initWidth: 200, initHeight: 100 },
];
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
gPopupChromeUIWidth = popUpChromeUISize.chromeWidth;
gPopupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(gPopupChromeUIWidth,
gPopupChromeUIHeight);
gMaxAvailWidth = maxAvailSize.maxAvailWidth;
gMaxAvailHeight = maxAvailSize.maxAvailHeight;
});
add_task(async function test_window_size_setting() {
// Open a tab to test.
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
for (let test of TESTCASES) {
// Test window.innerWidth and window.innerHeight.
await testWindowSizeSetting(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, test.initWidth,
test.initHeight, false, gMaxAvailWidth, gMaxAvailHeight,
gPopupChromeUIWidth, gPopupChromeUIHeight);
// test window.outerWidth and window.outerHeight.
await testWindowSizeSetting(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, test.initWidth,
test.initHeight, true, gMaxAvailWidth, gMaxAvailHeight,
gPopupChromeUIWidth, gPopupChromeUIHeight);
}
await BrowserTestUtils.removeTab(tab);
});
WindowSettingTest.run([
{settingWidth: 600, settingHeight: 600, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100},
{settingWidth: 599, settingHeight: 599, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100},
{settingWidth: 401, settingHeight: 501, targetWidth: 600, targetHeight: 600, initWidth: 200, initHeight: 100}
]);

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

@ -4,62 +4,7 @@
* test is for minimum values.
*/
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
const TEST_DOMAIN = "http://example.net/";
const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
let gMaxAvailWidth;
let gMaxAvailHeight;
// We need the chrome UI size of popup windows for testing outerWidth/Height.
let gPopupChromeUIWidth;
let gPopupChromeUIHeight;
const TESTCASES = [
{ settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100,
initWidth: 1000, initHeight: 1000 },
{ settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100,
initWidth: 1000, initHeight: 1000 },
];
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
gPopupChromeUIWidth = popUpChromeUISize.chromeWidth;
gPopupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(gPopupChromeUIWidth,
gPopupChromeUIHeight);
gMaxAvailWidth = maxAvailSize.maxAvailWidth;
gMaxAvailHeight = maxAvailSize.maxAvailHeight;
});
add_task(async function test_window_size_setting() {
// Open a tab to test.
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, TEST_PATH + "file_dummy.html");
for (let test of TESTCASES) {
// Test window.innerWidth and window.innerHeight.
await testWindowSizeSetting(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, test.initWidth,
test.initHeight, false, gMaxAvailWidth, gMaxAvailHeight,
gPopupChromeUIWidth, gPopupChromeUIHeight);
// test window.outerWidth and window.outerHeight.
await testWindowSizeSetting(tab.linkedBrowser, test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight, test.initWidth,
test.initHeight, true, gMaxAvailWidth, gMaxAvailHeight,
gPopupChromeUIWidth, gPopupChromeUIHeight);
}
await BrowserTestUtils.removeTab(tab);
});
WindowSettingTest.run([
{settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100, initWidth: 1000, initHeight: 1000},
{settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100, initWidth: 1000, initHeight: 1000}
]);

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

@ -228,3 +228,88 @@ async function testWindowSizeSetting(aBrowser, aSettingWidth, aSettingHeight,
}
);
}
class RoundedWindowTest {
// testOuter is optional. run() can be invoked with only 1 parameter.
static run(testCases, testOuter) {
// "this" is the calling class itself.
// e.g. when invoked by RoundedWindowTest.run(), "this" is "class RoundedWindowTest".
let test = new this(testCases);
add_task(async () => test.setup());
add_task(async () => {
if (testOuter == undefined) {
// If testOuter is not given, do tests for both inner and outer.
await test.doTests(false);
await test.doTests(true);
} else {
await test.doTests(testOuter);
}
});
}
get TEST_PATH() {
return "http://example.net/browser/browser/components/resistfingerprinting/test/browser/";
}
constructor(testCases) {
this.testCases = testCases;
}
async setup() {
await SpecialPowers.pushPrefEnv({"set":
[["privacy.resistFingerprinting", true]]
});
// Calculate the popup window's chrome UI size for tests of outerWidth/Height.
let popUpChromeUISize = await calcPopUpWindowChromeUISize();
this.popupChromeUIWidth = popUpChromeUISize.chromeWidth;
this.popupChromeUIHeight = popUpChromeUISize.chromeHeight;
// Calculate the maximum available size.
let maxAvailSize = await calcMaximumAvailSize(this.popupChromeUIWidth,
this.popupChromeUIHeight);
this.maxAvailWidth = maxAvailSize.maxAvailWidth;
this.maxAvailHeight = maxAvailSize.maxAvailHeight;
}
async doTests(testOuter) {
// Open a tab to test.
this.tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser, this.TEST_PATH + "file_dummy.html");
for (let test of this.testCases) {
await this.doTest(test, testOuter);
}
await BrowserTestUtils.removeTab(this.tab);
}
async doTest() {
throw new Error("RoundedWindowTest.doTest must be overridden.");
}
}
class WindowSettingTest extends RoundedWindowTest {
async doTest(test, testOuter) {
await testWindowSizeSetting(this.tab.linkedBrowser,
test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight,
test.initWidth, test.initHeight,
testOuter,
this.maxAvailWidth, this.maxAvailHeight,
this.popupChromeUIWidth, this.popupChromeUIHeight);
}
}
class OpenTest extends RoundedWindowTest {
async doTest(test, testOuter) {
await testWindowOpen(this.tab.linkedBrowser,
test.settingWidth, test.settingHeight,
test.targetWidth, test.targetHeight,
testOuter,
this.maxAvailWidth, this.maxAvailHeight,
this.popupChromeUIWidth, this.popupChromeUIHeight);
}
}

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

@ -205,6 +205,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY urlbar.defaultNotificationAnchor.tooltip "Open message panel">
<!ENTITY urlbar.geolocationNotificationAnchor.tooltip "Open location request panel">
<!ENTITY urlbar.addonsNotificationAnchor.tooltip "Open add-on installation message panel">
<!ENTITY urlbar.canvasNotificationAnchor.tooltip "Manage canvas extraction permission">
<!ENTITY urlbar.indexedDBNotificationAnchor.tooltip "Open offline storage message panel">
<!ENTITY urlbar.passwordNotificationAnchor.tooltip "Open save password message panel">
<!ENTITY urlbar.pluginsNotificationAnchor.tooltip "Manage plug-in use">

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

@ -467,6 +467,15 @@ offlineApps.usage=This website (%S) is now storing more than %SMB of data on you
offlineApps.manageUsage=Show settings
offlineApps.manageUsageAccessKey=S
# Canvas permission prompt
# LOCALIZATION NOTE (canvas.siteprompt): %S is hostname
canvas.siteprompt=Will you allow %S to use your HTML5 canvas image data? This may be used to uniquely identify your computer.
canvas.notAllow=Dont Allow
canvas.notAllow.accesskey=n
canvas.allow=Allow Data Access
canvas.allow.accesskey=A
canvas.remember=Always remember my decision
identity.identified.verifier=Verified by: %S
identity.identified.verified_by_you=You have added a security exception for this site.
identity.identified.state_and_country=%S, %S

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

@ -51,6 +51,11 @@
min-height: 24px;
}
:root[uidensity=touch] #search-box,
:root[uidensity=touch] .sidebar-placesTreechildren::-moz-tree-row {
min-height: 32px;
}
.sidebar-placesTreechildren::-moz-tree-cell(leaf) ,
.sidebar-placesTreechildren::-moz-tree-image(leaf) {
cursor: pointer;

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

@ -19,6 +19,10 @@
font-size: 1.0909rem;
}
:root[uidensity=touch] .sidebar-placesTreechildren::-moz-tree-row {
min-height: 32px;
}
.sidebar-placesTree {
-moz-appearance: -moz-mac-source-list;
-moz-font-smoothing-background-color: -moz-mac-source-list;

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

@ -61,6 +61,7 @@
skin/classic/browser/notification-icons/camera-blocked.svg (../shared/notification-icons/camera-blocked.svg)
skin/classic/browser/notification-icons/camera.svg (../shared/notification-icons/camera.svg)
skin/classic/browser/notification-icons/canvas.svg (../shared/notification-icons/canvas.svg)
skin/classic/browser/notification-icons/default-info.svg (../shared/notification-icons/default-info.svg)
skin/classic/browser/notification-icons/desktop-notification-blocked.svg (../shared/notification-icons/desktop-notification-blocked.svg)
skin/classic/browser/notification-icons/desktop-notification.svg (../shared/notification-icons/desktop-notification.svg)

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

@ -117,6 +117,11 @@
list-style-image: url(chrome://browser/skin/notification-icons/screen-blocked.svg);
}
#canvas-notification-icon,
.popup-notification-icon[popupid="canvas-permissions-prompt"] {
list-style-image: url(chrome://browser/skin/notification-icons/canvas.svg);
}
#webRTC-preview:not([hidden]) {
display: -moz-stack;
border-radius: 4px;

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

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 481.156 508.687" fill="context-fill" fill-opacity="context-fill-opacity">
<path d="M477.656 289.656c-36.133 224.615-220.16 222.188-283 218C22.22 496.163-68.584 254.586 61.98 128.066l105.857 105.872c-7.183 24.6 2.7 54.1 42.418 70.59 42.865 17.8 87.4 9.747 87.4 9.747s-14.08-22.03-15.565-68.266c-1.1-34.2-40.15-55.996-72.513-50.483L108.757 94.69c30.6-12.34 59.033-1.8 69.9 6.966 26.87 21.688 16.616 68.436 53 54 70.87-28.12-40.744-132.32 53-154 79.026-18.278 220.516 116.945 193 288zm-371-14a41 41 0 1 0 41 41 41 41 0 0 0-41-41zm149.5 92a43.5 43.5 0 1 0 43.5 43.5 43.5 43.5 0 0 0-43.5-43.5zm97.5-273a40 40 0 1 0 40 40 40 40 0 0 0-40-40zm24.5 141a45.5 45.5 0 1 0 45.5 45.5 45.5 45.5 0 0 0-45.5-45.5z"/>
<path d="M213.656 296.656c-36.083-15.022-42.678-42.92-33.52-64.423L35.847 87.925a17.732 17.732 0 0 1 25.076-25.078L205.45 207.393c28.076-10.037 67.206 8.853 68.206 40.263 1.24 38.716 13 58 13 58s-37.2 5.905-73-9z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

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

@ -21,6 +21,11 @@
margin: 0;
}
:root[uidensity=touch] #search-box,
:root[uidensity=touch] .sidebar-placesTreechildren::-moz-tree-row {
min-height: 32px;
}
.sidebar-placesTreechildren::-moz-tree-cell,
.sidebar-placesTreechildren::-moz-tree-twisty {
padding: 0 4px;

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

@ -76,7 +76,7 @@ function createToolMenuElements(toolDefinition, doc) {
let oncommand = function (id, event) {
let window = event.target.ownerDocument.defaultView;
gDevToolsBrowser.selectToolCommand(window.gBrowser, id);
gDevToolsBrowser.selectToolCommand(window.gBrowser, id, window.performance.now());
}.bind(null, id);
let menuitem = createMenuItem({

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

@ -73,7 +73,7 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
* of there
*/
// used by browser-sets.inc, command
toggleToolboxCommand(gBrowser) {
toggleToolboxCommand(gBrowser, startTime) {
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
@ -81,7 +81,11 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
// - should close a docked toolbox
// - should focus a windowed toolbox
let isDocked = toolbox && toolbox.hostType != Toolbox.HostType.WINDOW;
isDocked ? gDevTools.closeToolbox(target) : gDevTools.showToolbox(target);
if (isDocked) {
gDevTools.closeToolbox(target);
} else {
gDevTools.showToolbox(target, null, null, null, startTime);
}
},
/**
@ -216,7 +220,7 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
*/
// Used when: - registering a new tool
// - new xul window, to add menu items
selectToolCommand(gBrowser, toolId) {
selectToolCommand(gBrowser, toolId, startTime) {
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
let toolDefinition = gDevTools.getToolDefinition(toolId);
@ -234,7 +238,7 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
}
gDevTools.emit("select-tool-command", toolId);
} else {
gDevTools.showToolbox(target, toolId).then(newToolbox => {
gDevTools.showToolbox(target, toolId, null, null, startTime).then(newToolbox => {
newToolbox.fireCustomKey(toolId);
gDevTools.emit("select-tool-command", toolId);
});
@ -253,18 +257,21 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
* - `toolId` used to identify a toolbox's panel like inspector or webconsole,
* - `id` used to identify any other key shortcuts like scratchpad or
* about:debugging
* @param {Number} startTime
* Optional, indicates the time at which the key event fired. This is a
* `performance.now()` timing.
*/
onKeyShortcut(window, key) {
onKeyShortcut(window, key, startTime) {
// If this is a toolbox's panel key shortcut, delegate to selectToolCommand
if (key.toolId) {
gDevToolsBrowser.selectToolCommand(window.gBrowser, key.toolId);
gDevToolsBrowser.selectToolCommand(window.gBrowser, key.toolId, startTime);
return;
}
// Otherwise implement all other key shortcuts individually here
switch (key.id) {
case "toggleToolbox":
case "toggleToolboxF12":
gDevToolsBrowser.toggleToolboxCommand(window.gBrowser);
gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, startTime);
break;
case "toggleToolbar":
gDevToolsBrowser.getDeveloperToolbar(window).focusToggle();

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

@ -421,6 +421,12 @@ DevTools.prototype = {
}
},
/**
* Boolean, true, if we never opened a toolbox.
* Used to implement the telemetry tracking toolbox opening.
*/
_firstShowToolbox: true,
/**
* Show a Toolbox for a target (either by creating a new one, or if a toolbox
* already exists for the target, by bring to the front the existing one)
@ -437,11 +443,14 @@ DevTools.prototype = {
* The type of host (bottom, window, side)
* @param {object} hostOptions
* Options for host specifically
* @param {Number} startTime
* Optional, indicates the time at which the user event related to this toolbox
* opening started. This is a `performance.now()` timing.
*
* @return {Toolbox} toolbox
* The toolbox that was opened
*/
showToolbox: Task.async(function* (target, toolId, hostType, hostOptions) {
showToolbox: Task.async(function* (target, toolId, hostType, hostOptions, startTime) {
let toolbox = this._toolboxes.get(target);
if (toolbox) {
if (hostType != null && toolbox.hostType != hostType) {
@ -465,10 +474,37 @@ DevTools.prototype = {
this._creatingToolboxes.set(target, toolboxPromise);
toolbox = yield toolboxPromise;
this._creatingToolboxes.delete(target);
if (startTime) {
this.logToolboxOpenTime(toolbox.currentToolId, startTime);
}
this._firstShowToolbox = false;
}
return toolbox;
}),
/**
* Log telemetry related to toolbox opening.
* Two distinct probes are logged. One for cold startup, when we open the very first
* toolbox. This one includes devtools framework loading. And a second one for all
* subsequent toolbox opening, which should all be faster.
* These two probes are indexed by Tool ID.
*
* @param {String} toolId
* The id of the opened tool.
* @param {Number} startTime
* Indicates the time at which the user event related to the toolbox
* opening started. This is a `performance.now()` timing.
*/
logToolboxOpenTime(toolId, startTime) {
let { performance } = Services.appShell.hiddenDOMWindow;
let delay = performance.now() - startTime;
let telemetryKey = this._firstShowToolbox ?
"DEVTOOLS_COLD_TOOLBOX_OPEN_DELAY_MS" : "DEVTOOLS_WARM_TOOLBOX_OPEN_DELAY_MS";
let histogram = Services.telemetry.getKeyedHistogramById(telemetryKey);
histogram.add(toolId, delay);
},
createToolbox: Task.async(function* (target, toolId, hostType, hostOptions) {
let manager = new ToolboxHostManager(target, hostType, hostOptions);
@ -575,13 +611,16 @@ DevTools.prototype = {
* An array of CSS selectors to find the target node. Several selectors can be
* needed if the element is nested in frames and not directly in the root
* document.
* @param {Number} startTime
* Optional, indicates the time at which the user event related to this node
* inspection started. This is a `performance.now()` timing.
* @return {Promise} a promise that resolves when the node is selected in the inspector
* markup view.
*/
async inspectNode(tab, nodeSelectors) {
async inspectNode(tab, nodeSelectors, startTime) {
let target = TargetFactory.forTab(tab);
let toolbox = await gDevTools.showToolbox(target, "inspector");
let toolbox = await gDevTools.showToolbox(target, "inspector", null, null, startTime);
let inspector = toolbox.getCurrentPanel();
// new-node-front tells us when the node has been selected, whether the

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

@ -41,7 +41,7 @@ exports.menuitems = [
l10nKey: "devToolboxMenuItem",
oncommand(event) {
let window = event.target.ownerDocument.defaultView;
gDevToolsBrowser.toggleToolboxCommand(window.gBrowser);
gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, window.performance.now());
},
keyId: "toggleToolbox",
checkbox: true

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

@ -995,6 +995,7 @@ JSTerm.prototype = {
this.focus();
this.emit("messages-cleared");
},
/**
* Remove all of the private messages from the Web Console output.
*
@ -1773,7 +1774,13 @@ JSTerm.prototype = {
this._sidebarDestroy();
this.clearCompletion();
this.clearOutput();
if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
this.webConsoleClient.clearNetworkRequests();
this.hud.outputNode.innerHTML = "";
} else {
this.clearOutput();
}
this.autocompletePopup.destroy();
this.autocompletePopup = null;

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

@ -206,11 +206,10 @@ skip-if = true # Bug 1401881
[browser_jsterm_inspect.js]
[browser_jsterm_no_autocompletion_on_defined_variables.js]
skip-if = true # Bug 1401881
[browser_jsterm_no_input_and_tab_key_pressed.js]
[browser_netmonitor_shows_reqs_in_webconsole.js]
[browser_webconsole.js]
skip-if = true # Bug 1404829
[browser_webconsole_No_input_and_Tab_key_pressed.js]
skip-if = true # Bug 1403910
[browser_webconsole_No_input_change_and_Tab_key_pressed.js]
skip-if = true # Bug 1404882
[browser_webconsole_add_edited_input_to_history.js]

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

@ -7,13 +7,10 @@
// See Bug 583816.
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/browser/test-console.html";
const TEST_URI = "data:text/html,Testing jsterm with no input";
add_task(function* () {
yield loadTab(TEST_URI);
let hud = yield openConsole();
let hud = yield openNewTabAndConsole(TEST_URI);
testCompletion(hud);
});
@ -25,7 +22,7 @@ function testCompletion(hud) {
EventUtils.synthesizeKey("VK_TAB", {});
is(jsterm.completeNode.value, "<- no result", "<- no result - matched");
is(input.value, "", "inputnode is empty - matched");
is(input.getAttribute("focused"), "true", "input is still focused");
ok(hasFocus(input), "input is still focused");
// Any thing which is not in property autocompleter
jsterm.setInputValue("window.Bug583816");
@ -33,5 +30,5 @@ function testCompletion(hud) {
is(jsterm.completeNode.value, " <- no result",
"completenode content - matched");
is(input.value, "window.Bug583816", "inputnode content - matched");
is(input.getAttribute("focused"), "true", "input is still focused");
ok(hasFocus(input), "input is still focused");
}

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

@ -160,12 +160,18 @@ this.DevToolsShim = {
* markup view or that resolves immediately if DevTools are not installed.
*/
inspectNode: function (tab, selectors) {
// Record the timing at which this event started in order to compute later in
// gDevTools.showToolbox, the complete time it takes to open the toolbox.
// i.e. especially take `DevtoolsStartup.initDevTools` into account.
let { performance } = Services.appShell.hiddenDOMWindow;
let startTime = performance.now();
let devtoolsReady = this._maybeInitializeDevTools("ContextMenu");
if (!devtoolsReady) {
return Promise.resolve();
}
return this._gDevTools.inspectNode(tab, selectors);
return this._gDevTools.inspectNode(tab, selectors, startTime);
},
_onDevToolsRegistered: function () {

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

@ -5,7 +5,7 @@
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd"> %globalDTD;
<!ENTITY % aboutdevtoolsDTD SYSTEM "chrome://devtools-shim/locale/aboutdevtools.dtd"> %aboutdevtoolsDTD;
<!ENTITY % aboutdevtoolsDTD SYSTEM "chrome://devtools-shim/content/aboutdevtools/tmp-locale/aboutdevtools.dtd"> %aboutdevtoolsDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml" dir="&locale.dir;">

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

@ -1,3 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY aboutDevtools.headTitle "About Developer Tools">
<!ENTITY aboutDevtools.enable.title "Enable Firefox Developer Tools">
<!ENTITY aboutDevtools.enable.inspectElementTitle "Enable Firefox Developer Tools to use Inspect Element">

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

@ -449,11 +449,15 @@ DevToolsStartup.prototype = {
},
onKey(window, key) {
// Record the timing at which this event started in order to compute later in
// gDevTools.showToolbox, the complete time it takes to open the toolbox.
// i.e. especially take `initDevTools` into account.
let startTime = window.performance.now();
let require = this.initDevTools("KeyShortcut");
if (require) {
// require might be null if initDevTools was called while DevTools are disabled.
let { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
gDevToolsBrowser.onKeyShortcut(window, key);
gDevToolsBrowser.onKeyShortcut(window, key, startTime);
}
},

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

@ -10,6 +10,10 @@ devtools-shim.jar:
content/aboutdevtools/images/otter.png (aboutdevtools/images/otter.png)
# Temporary localisation file, move back to devtools/shim/locales/en-US when ready for localization
# See https://bugzilla.mozilla.org/show_bug.cgi?id=1408369
content/aboutdevtools/tmp-locale/aboutdevtools.dtd (aboutdevtools/tmp-locale/aboutdevtools.dtd)
content/aboutdevtools/images/feature-inspector.svg (aboutdevtools/images/feature-inspector.svg)
content/aboutdevtools/images/feature-console.svg (aboutdevtools/images/feature-console.svg)
content/aboutdevtools/images/feature-debugger.svg (aboutdevtools/images/feature-debugger.svg)

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

@ -200,20 +200,18 @@ AnimationEffectReadOnly::GetComputedTimingAt(
? fmod(overallProgress, 1.0)
: fmod(result.mIterationStart, 1.0);
// When we finish exactly at the end of an iteration we need to report
// the end of the final iteration and not the start of the next iteration.
// We *don't* want to do this when we have a zero-iteration animation or
// when the animation has been effectively made into a zero-duration animation
// using a negative end-delay, however.
if (result.mPhase == ComputedTiming::AnimationPhase::After &&
progress == 0.0 &&
result.mIterations != 0.0 &&
(result.mActiveTime != zeroDuration ||
result.mDuration == zeroDuration)) {
// The only way we can be in the after phase with a progress of zero and
// a current iteration of zero, is if we have a zero iteration count or
// were clipped using a negative end delay--both of which we should have
// detected above.
// When we are at the end of the active interval and the end of an iteration
// we need to report the end of the final iteration and not the start of the
// next iteration. We *don't* want to do this, however, when we have
// a zero-iteration animation.
if (progress == 0.0 &&
(result.mPhase == ComputedTiming::AnimationPhase::After ||
result.mPhase == ComputedTiming::AnimationPhase::Active) &&
result.mActiveTime == result.mActiveDuration &&
result.mIterations != 0.0) {
// The only way we can reach the end of the active interval and have
// a progress of zero and a current iteration of zero, is if we have a zero
// iteration count -- something we should have detected above.
MOZ_ASSERT(result.mCurrentIteration != 0,
"Should not have zero current iteration");
progress = 1.0;

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

@ -28,8 +28,14 @@ public:
void AddPlayPending(dom::Animation& aAnimation)
{
MOZ_ASSERT(!IsWaitingToPause(aAnimation),
"Animation is already waiting to pause");
// We'd like to assert here that IsWaitingToPause(aAnimation) is false but
// if |aAnimation| was tracked here as a pause-pending animation when it was
// removed from |mDocument|, then re-attached to |mDocument|, and then
// played again, we could end up here with IsWaitingToPause returning true.
//
// However, that should be harmless since all it means is that we'll call
// Animation::TriggerOnNextTick or Animation::TriggerNow twice, both of
// which will handle the redundant call gracefully.
AddPending(aAnimation, mPlayPendingSet);
mHasPlayPendingGeometricAnimations = CheckState::Indeterminate;
}
@ -45,8 +51,10 @@ public:
void AddPausePending(dom::Animation& aAnimation)
{
MOZ_ASSERT(!IsWaitingToPlay(aAnimation),
"Animation is already waiting to play");
// As with AddPausePending, we'd like to assert that
// IsWaitingToPlay(aAnimation) is false but there are some circumstances
// where this can be true. Fortunately adding the animation to both pending
// sets should be harmless.
AddPending(aAnimation, mPausePendingSet);
}
void RemovePausePending(dom::Animation& aAnimation)

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

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html class=reftest-wait>
<meta charset=utf-8>
<script>
function boom() {
const div = document.createElement('div');
const anim = div.animate([{}], {});
document.documentElement.appendChild(div);
anim.pause();
document.documentElement.removeChild(div);
requestAnimationFrame(() => {
document.documentElement.appendChild(div);
anim.play();
document.documentElement.className = '';
});
}
</script>
</head>
<body onload="boom();"></body>
</html>

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

@ -10,6 +10,7 @@ pref(dom.animations-api.core.enabled,true) load 1272475-1.html
pref(dom.animations-api.core.enabled,true) load 1272475-2.html
pref(dom.animations-api.core.enabled,true) load 1278485-1.html
pref(dom.animations-api.core.enabled,true) load 1277272-1.html
pref(dom.animations-api.core.enabled,true) load 1282691-1.html
pref(dom.animations-api.core.enabled,true) load 1291413-1.html
pref(dom.animations-api.core.enabled,true) load 1291413-2.html
pref(dom.animations-api.core.enabled,true) load 1304886-1.html

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

@ -3467,27 +3467,38 @@ Element::GetTokenList(nsAtom* aAtom,
Element*
Element::Closest(const nsAString& aSelector, ErrorResult& aResult)
{
nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aResult);
if (!selectorList) {
// Either we failed (and aResult already has the exception), or this
// is a pseudo-element-only selector that matches nothing.
return nullptr;
}
TreeMatchContext matchingContext(false,
nsRuleWalker::eRelevantLinkUnvisited,
OwnerDoc(),
TreeMatchContext::eNeverMatchVisited);
matchingContext.SetHasSpecifiedScope();
matchingContext.AddScopeElement(this);
for (nsINode* node = this; node; node = node->GetParentNode()) {
if (node->IsElement() &&
nsCSSRuleProcessor::SelectorListMatches(node->AsElement(),
matchingContext,
selectorList)) {
return node->AsElement();
return WithSelectorList<Element*>(
aSelector,
aResult,
[&](const RawServoSelectorList* aList) -> Element* {
if (!aList) {
return nullptr;
}
return const_cast<Element*>(Servo_SelectorList_Closest(this, aList));
},
[&](nsCSSSelectorList* aList) -> Element* {
if (!aList) {
// Either we failed (and aError already has the exception), or this
// is a pseudo-element-only selector that matches nothing.
return nullptr;
}
TreeMatchContext matchingContext(false,
nsRuleWalker::eRelevantLinkUnvisited,
OwnerDoc(),
TreeMatchContext::eNeverMatchVisited);
matchingContext.SetHasSpecifiedScope();
matchingContext.AddScopeElement(this);
for (nsINode* node = this; node; node = node->GetParentNode()) {
if (node->IsElement() &&
nsCSSRuleProcessor::SelectorListMatches(node->AsElement(),
matchingContext,
aList)) {
return node->AsElement();
}
}
return nullptr;
}
}
return nullptr;
);
}
bool

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

@ -155,6 +155,7 @@ public:
EncodingCompleteEvent* aEncodingCompleteEvent,
int32_t aFormat,
const nsIntSize aSize,
bool aUsePlaceholder,
bool aUsingCustomOptions)
: Runnable("EncodingRunnable")
, mType(aType)
@ -165,6 +166,7 @@ public:
, mEncodingCompleteEvent(aEncodingCompleteEvent)
, mFormat(aFormat)
, mSize(aSize)
, mUsePlaceholder(aUsePlaceholder)
, mUsingCustomOptions(aUsingCustomOptions)
{}
@ -176,6 +178,7 @@ public:
mImageBuffer.get(),
mFormat,
mSize,
mUsePlaceholder,
mImage,
nullptr,
nullptr,
@ -190,6 +193,7 @@ public:
mImageBuffer.get(),
mFormat,
mSize,
mUsePlaceholder,
mImage,
nullptr,
nullptr,
@ -239,6 +243,7 @@ private:
RefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
int32_t mFormat;
const nsIntSize mSize;
bool mUsePlaceholder;
bool mUsingCustomOptions;
};
@ -251,6 +256,7 @@ nsresult
ImageEncoder::ExtractData(nsAString& aType,
const nsAString& aOptions,
const nsIntSize aSize,
bool aUsePlaceholder,
nsICanvasRenderingContextInternal* aContext,
layers::AsyncCanvasRenderer* aRenderer,
nsIInputStream** aStream)
@ -260,7 +266,8 @@ ImageEncoder::ExtractData(nsAString& aType,
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, nullptr,
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize,
aUsePlaceholder, nullptr,
aContext, aRenderer, aStream, encoder);
}
@ -270,6 +277,7 @@ ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
const nsAString& aOptions,
bool aUsingCustomOptions,
layers::Image* aImage,
bool aUsePlaceholder,
EncodeCompleteCallback* aEncodeCallback)
{
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
@ -294,6 +302,7 @@ ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
completeEvent,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
size,
aUsePlaceholder,
aUsingCustomOptions);
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
}
@ -306,6 +315,7 @@ ImageEncoder::ExtractDataAsync(nsAString& aType,
UniquePtr<uint8_t[]> aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
bool aUsePlaceholder,
EncodeCompleteCallback* aEncodeCallback)
{
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
@ -329,6 +339,7 @@ ImageEncoder::ExtractDataAsync(nsAString& aType,
completeEvent,
aFormat,
aSize,
aUsePlaceholder,
aUsingCustomOptions);
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
}
@ -359,6 +370,7 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
bool aUsePlaceholder,
layers::Image* aImage,
nsICanvasRenderingContextInternal* aContext,
layers::AsyncCanvasRenderer* aRenderer,
@ -373,7 +385,7 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
// get image bytes
nsresult rv;
if (aImageBuffer) {
if (aImageBuffer && !aUsePlaceholder) {
if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
return NS_ERROR_INVALID_ARG;
}
@ -386,17 +398,17 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
aEncoder,
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else if (aContext) {
} else if (aContext && !aUsePlaceholder) {
NS_ConvertUTF16toUTF8 encoderType(aType);
rv = aContext->GetInputStream(encoderType.get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else if (aRenderer) {
} else if (aRenderer && !aUsePlaceholder) {
NS_ConvertUTF16toUTF8 encoderType(aType);
rv = aRenderer->GetInputStream(encoderType.get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else if (aImage) {
} else if (aImage && !aUsePlaceholder) {
// It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread.
// Other image formats could have problem to convert format off-main-thread.
// So here it uses a help function GetBRGADataSourceSurfaceSync() to convert
@ -472,6 +484,10 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
if (!emptyCanvas->Map(DataSourceSurface::MapType::WRITE, &map)) {
return NS_ERROR_INVALID_ARG;
}
if (aUsePlaceholder) {
// If placeholder data was requested, return all-white, opaque image data.
memset(map.mData, 0xFF, 4 * aSize.width * aSize.height);
}
rv = aEncoder->InitFromData(map.mData,
aSize.width * aSize.height * 4,
aSize.width,

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

@ -42,6 +42,7 @@ public:
static nsresult ExtractData(nsAString& aType,
const nsAString& aOptions,
const nsIntSize aSize,
bool aUsePlaceholder,
nsICanvasRenderingContextInternal* aContext,
layers::AsyncCanvasRenderer* aRenderer,
nsIInputStream** aStream);
@ -63,6 +64,7 @@ public:
UniquePtr<uint8_t[]> aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
bool aUsePlaceholder,
EncodeCompleteCallback* aEncodeCallback);
// Extract an Image asynchronously. Its function is same as ExtractDataAsync
@ -74,6 +76,7 @@ public:
const nsAString& aOptions,
bool aUsingCustomOptions,
layers::Image* aImage,
bool aUsePlaceholder,
EncodeCompleteCallback* aEncodeCallback);
// Gives you a stream containing the image represented by aImageBuffer.
@ -95,6 +98,7 @@ private:
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
bool aUsePlaceholder,
layers::Image* aImage,
nsICanvasRenderingContextInternal* aContext,
layers::AsyncCanvasRenderer* aRenderer,

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

@ -124,7 +124,7 @@ DOMInterfaces = {
'CanvasRenderingContext2D': {
'implicitJSContext': [
'createImageData', 'getImageData'
'createImageData', 'getImageData', 'isPointInPath', 'isPointInStroke'
],
'binaryNames': {
'mozImageSmoothingEnabled': 'imageSmoothingEnabled'

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

@ -4905,12 +4905,19 @@ CanvasRenderingContext2D::LineDashOffset() const {
}
bool
CanvasRenderingContext2D::IsPointInPath(double aX, double aY, const CanvasWindingRule& aWinding)
CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, double aX, double aY, const CanvasWindingRule& aWinding)
{
if (!FloatValidate(aX, aY)) {
return false;
}
// Check for site-specific permission and return false if no permission.
if (mCanvasElement) {
nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
return false;
}
EnsureUserSpacePath(aWinding);
if (!mPath) {
return false;
@ -4923,7 +4930,7 @@ CanvasRenderingContext2D::IsPointInPath(double aX, double aY, const CanvasWindin
return mPath->ContainsPoint(Point(aX, aY), mTarget->GetTransform());
}
bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding)
bool CanvasRenderingContext2D::IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding)
{
if (!FloatValidate(aX, aY)) {
return false;
@ -4940,12 +4947,19 @@ bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath& aPath, double aX,
}
bool
CanvasRenderingContext2D::IsPointInStroke(double aX, double aY)
CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, double aX, double aY)
{
if (!FloatValidate(aX, aY)) {
return false;
}
// Check for site-specific permission and return false if no permission.
if (mCanvasElement) {
nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
if (!ownerDoc || !CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx))
return false;
}
EnsureUserSpacePath();
if (!mPath) {
return false;
@ -4967,7 +4981,7 @@ CanvasRenderingContext2D::IsPointInStroke(double aX, double aY)
return mPath->StrokeContainsPoint(strokeOptions, Point(aX, aY), mTarget->GetTransform());
}
bool CanvasRenderingContext2D::IsPointInStroke(const CanvasPath& aPath, double aX, double aY)
bool CanvasRenderingContext2D::IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX, double aY)
{
if (!FloatValidate(aX, aY)) {
return false;
@ -5808,7 +5822,17 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
IntRect dstWriteRect = srcReadRect;
dstWriteRect.MoveBy(-aX, -aY);
{
// Check for site-specific permission. This check is not needed if the
// canvas was created with a docshell (that is only done for special
// internal uses).
bool usePlaceholder = false;
if (mCanvasElement) {
nsCOMPtr<nsIDocument> ownerDoc = mCanvasElement->OwnerDoc();
usePlaceholder = !ownerDoc ||
!CanvasUtils::IsImageExtractionAllowed(ownerDoc, aCx);
}
do {
JS::AutoCheckCannotGC nogc;
bool isShared;
uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
@ -5816,6 +5840,13 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
uint32_t srcStride = rawData.mStride;
uint8_t* src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
// Return all-white, opaque pixel data if no permission.
if (usePlaceholder) {
memset(data, 0xFF, len.value());
break;
}
uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
if (mOpaque) {
@ -5827,7 +5858,7 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
dst, aWidth * 4, SurfaceFormat::R8G8B8A8,
dstWriteRect.Size());
}
}
} while (false);
readback->Unmap();
*aRetval = darray;

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

@ -204,10 +204,10 @@ public:
bool DrawCustomFocusRing(mozilla::dom::Element& aElement);
void Clip(const CanvasWindingRule& aWinding);
void Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
bool IsPointInPath(double aX, double aY, const CanvasWindingRule& aWinding);
bool IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding);
bool IsPointInStroke(double aX, double aY);
bool IsPointInStroke(const CanvasPath& aPath, double aX, double aY);
bool IsPointInPath(JSContext* aCx, double aX, double aY, const CanvasWindingRule& aWinding);
bool IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding);
bool IsPointInStroke(JSContext* aCx, double aX, double aY);
bool IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX, double aY);
void FillText(const nsAString& aText, double aX, double aY,
const Optional<double>& aMaxWidth,
mozilla::ErrorResult& aError);

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

@ -25,6 +25,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
BlobCallback& aCallback,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
bool aUsePlaceholder,
ErrorResult& aRv)
{
// Encoder callback when encoding is complete.
@ -58,7 +59,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
RefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(aGlobal, &aCallback);
ToBlob(aCx, aGlobal, callback, aType, aParams, aRv);
ToBlob(aCx, aGlobal, callback, aType, aParams, aUsePlaceholder, aRv);
}
void
@ -67,6 +68,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
EncodeCompleteCallback* aCallback,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
bool aUsePlaceholder,
ErrorResult& aRv)
{
nsAutoString type;
@ -107,6 +109,7 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
Move(imageBuffer),
format,
GetWidthHeight(),
aUsePlaceholder,
callback);
}

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

@ -58,11 +58,11 @@ protected:
void ToBlob(JSContext* aCx, nsIGlobalObject* global, BlobCallback& aCallback,
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
bool aUsePlaceholder, ErrorResult& aRv);
void ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, EncodeCompleteCallback* aCallback,
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
bool aUsePlaceholder, ErrorResult& aRv);
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType);

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

@ -13,6 +13,7 @@
#include "nsICanvasRenderingContextInternal.h"
#include "nsIHTMLCollection.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/TabChild.h"
#include "nsIPrincipal.h"
#include "nsGfxCIID.h"
@ -23,11 +24,150 @@
#include "mozilla/gfx/Matrix.h"
#include "WebGL2Context.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIPermissionManager.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "mozIThirdPartyUtil.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "nsPrintfCString.h"
#include "nsIConsoleService.h"
#include "jsapi.h"
#define TOPIC_CANVAS_PERMISSIONS_PROMPT "canvas-permissions-prompt"
#define PERMISSION_CANVAS_EXTRACT_DATA "canvas/extractData"
using namespace mozilla::gfx;
namespace mozilla {
namespace CanvasUtils {
bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx)
{
// Do the rest of the checks only if privacy.resistFingerprinting is on.
if (!nsContentUtils::ShouldResistFingerprinting()) {
return true;
}
// Don't proceed if we don't have a document or JavaScript context.
if (!aDocument || !aCx) {
return false;
}
// Documents with system principal can always extract canvas data.
nsPIDOMWindowOuter *win = aDocument->GetWindow();
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
if (sop && nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
return true;
}
// Always give permission to chrome scripts (e.g. Page Inspector).
if (nsContentUtils::ThreadsafeIsCallerChrome()) {
return true;
}
// Get the document URI and its spec.
nsIURI *docURI = aDocument->GetDocumentURI();
nsCString docURISpec;
docURI->GetSpec(docURISpec);
// Allow local files to extract canvas data.
bool isFileURL;
(void) docURI->SchemeIs("file", &isFileURL);
if (isFileURL) {
return true;
}
// Get calling script file and line for logging.
JS::AutoFilename scriptFile;
unsigned scriptLine = 0;
bool isScriptKnown = false;
if (JS::DescribeScriptedCaller(aCx, &scriptFile, &scriptLine)) {
isScriptKnown = true;
// Don't show canvas prompt for PDF.js
if (scriptFile.get() &&
strcmp(scriptFile.get(), "resource://pdf.js/build/pdf.js") == 0) {
return true;
}
}
nsIDocument* topLevelDocument = aDocument->GetTopLevelContentDocument();
nsIURI *topLevelDocURI = topLevelDocument ? topLevelDocument->GetDocumentURI() : nullptr;
nsCString topLevelDocURISpec;
if (topLevelDocURI) {
topLevelDocURI->GetSpec(topLevelDocURISpec);
}
// Load Third Party Util service.
nsresult rv;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, false);
// Block all third-party attempts to extract canvas.
bool isThirdParty = true;
rv = thirdPartyUtil->IsThirdPartyURI(topLevelDocURI, docURI, &isThirdParty);
NS_ENSURE_SUCCESS(rv, false);
if (isThirdParty) {
nsAutoCString message;
message.AppendPrintf("Blocked third party %s in page %s from extracting canvas data.",
docURISpec.get(), topLevelDocURISpec.get());
if (isScriptKnown) {
message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine);
}
nsContentUtils::LogMessageToConsole(message.get());
return false;
}
// Load Permission Manager service.
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, false);
// Check if the site has permission to extract canvas data.
// Either permit or block extraction if a stored permission setting exists.
uint32_t permission;
rv = permissionManager->TestPermission(topLevelDocURI,
PERMISSION_CANVAS_EXTRACT_DATA,
&permission);
NS_ENSURE_SUCCESS(rv, false);
switch (permission) {
case nsIPermissionManager::ALLOW_ACTION:
return true;
case nsIPermissionManager::DENY_ACTION:
return false;
default:
break;
}
// At this point, permission is unknown (nsIPermissionManager::UNKNOWN_ACTION).
nsAutoCString message;
message.AppendPrintf("Blocked %s in page %s from extracting canvas data.",
docURISpec.get(), topLevelDocURISpec.get());
if (isScriptKnown) {
message.AppendPrintf(" %s:%u.", scriptFile.get(), scriptLine);
}
nsContentUtils::LogMessageToConsole(message.get());
// Prompt the user (asynchronous).
if (XRE_IsContentProcess()) {
TabChild* tabChild = TabChild::GetFrom(win);
if (tabChild) {
tabChild->SendShowCanvasPermissionPrompt(topLevelDocURISpec);
}
} else {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(win, TOPIC_CANVAS_PERMISSIONS_PROMPT,
NS_ConvertUTF8toUTF16(topLevelDocURISpec).get());
}
}
// We don't extract the image for now -- user may override at prompt.
return false;
}
bool
GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
{

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

@ -49,6 +49,9 @@ void DoDrawImageSecurityCheck(dom::HTMLCanvasElement *aCanvasElement,
// Check if the context is chrome or has the permission to drawWindow
bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* aObj);
// Check site-specific permission and display prompt if appropriate.
bool IsImageExtractionAllowed(nsIDocument *aDocument, JSContext *aCx);
// Make a double out of |v|, treating undefined values as 0.0 (for
// the sake of sparse arrays). Return true iff coercion
// succeeded.

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

@ -286,8 +286,13 @@ OffscreenCanvas::ToBlob(JSContext* aCx,
RefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(global, promise);
CanvasRenderingContextHelper::ToBlob(aCx, global,
callback, aType, aParams, aRv);
// TODO: Can we obtain the context and document here somehow
// so that we can decide when usePlaceholder should be true/false?
// See https://trac.torproject.org/18599
// For now, we always return a placeholder if fingerprinting resistance is on.
bool usePlaceholder = nsContentUtils::ShouldResistFingerprinting();
CanvasRenderingContextHelper::ToBlob(aCx, global, callback, aType, aParams,
usePlaceholder, aRv);
return promise.forget();
}

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

@ -44,6 +44,7 @@
#include "nsRefreshDriver.h"
#include "nsStreamUtils.h"
#include "ActiveLayerTracker.h"
#include "CanvasUtils.h"
#include "VRManagerChild.h"
#include "WebGL1Context.h"
#include "WebGL2Context.h"
@ -62,8 +63,10 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver
public:
RequestedFrameRefreshObserver(HTMLCanvasElement* const aOwningElement,
nsRefreshDriver* aRefreshDriver)
nsRefreshDriver* aRefreshDriver,
bool aReturnPlaceholderData)
: mRegistered(false),
mReturnPlaceholderData(aReturnPlaceholderData),
mOwningElement(aOwningElement),
mRefreshDriver(aRefreshDriver)
{
@ -71,7 +74,8 @@ public:
}
static already_AddRefed<DataSourceSurface>
CopySurface(const RefPtr<SourceSurface>& aSurface)
CopySurface(const RefPtr<SourceSurface>& aSurface,
bool aReturnPlaceholderData)
{
RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
if (!data) {
@ -100,12 +104,23 @@ public:
MOZ_ASSERT(data->GetSize() == copy->GetSize());
MOZ_ASSERT(data->GetFormat() == copy->GetFormat());
memcpy(write.GetData(), read.GetData(),
write.GetStride() * copy->GetSize().height);
if (aReturnPlaceholderData) {
// If returning placeholder data, fill the frame copy with white pixels.
memset(write.GetData(), 0xFF,
write.GetStride() * copy->GetSize().height);
} else {
memcpy(write.GetData(), read.GetData(),
write.GetStride() * copy->GetSize().height);
}
return copy.forget();
}
void SetReturnPlaceholderData(bool aReturnPlaceholderData)
{
mReturnPlaceholderData = aReturnPlaceholderData;
}
void WillRefresh(TimeStamp aTime) override
{
MOZ_ASSERT(NS_IsMainThread());
@ -144,7 +159,7 @@ public:
{
AUTO_PROFILER_LABEL(
"RequestedFrameRefreshObserver::WillRefresh:CopySurface", OTHER);
copy = CopySurface(snapshot);
copy = CopySurface(snapshot, mReturnPlaceholderData);
if (!copy) {
return;
}
@ -201,6 +216,7 @@ private:
}
bool mRegistered;
bool mReturnPlaceholderData;
HTMLCanvasElement* const mOwningElement;
RefPtr<nsRefreshDriver> mRefreshDriver;
};
@ -754,7 +770,14 @@ HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
new CanvasCaptureTrackSource(principal, stream));
stream->AddTrackInternal(track);
rv = RegisterFrameCaptureListener(stream->FrameCaptureListener());
// Check site-specific permission and display prompt if appropriate.
// If no permission, arrange for the frame capture listener to return
// all-white, opaque image data.
bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
OwnerDoc(),
nsContentUtils::GetCurrentJSContext());
rv = RegisterFrameCaptureListener(stream->FrameCaptureListener(), usePlaceholder);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
@ -764,13 +787,18 @@ HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
}
nsresult
HTMLCanvasElement::ExtractData(nsAString& aType,
HTMLCanvasElement::ExtractData(JSContext* aCx,
nsAString& aType,
const nsAString& aOptions,
nsIInputStream** aStream)
{
// Check site-specific permission and display prompt if appropriate.
// If no permission, return all-white, opaque image data.
bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(OwnerDoc(), aCx);
return ImageEncoder::ExtractData(aType,
aOptions,
GetSize(),
usePlaceholder,
mCurrentContext,
mAsyncCanvasRenderer,
aStream);
@ -800,12 +828,12 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
}
nsCOMPtr<nsIInputStream> stream;
rv = ExtractData(type, params, getter_AddRefs(stream));
rv = ExtractData(aCx, type, params, getter_AddRefs(stream));
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
rv = ExtractData(aCx, type, EmptyString(), getter_AddRefs(stream));
}
NS_ENSURE_SUCCESS(rv, rv);
@ -855,8 +883,11 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
return;
}
// Check site-specific permission and display prompt if appropriate.
// If no permission, return all-white, opaque image data.
bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(OwnerDoc(), aCx);
CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
aParams, aRv);
aParams, usePlaceholder, aRv);
}
@ -925,7 +956,8 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
{
nsCOMPtr<nsIInputStream> stream;
nsAutoString type(aType);
nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
nsresult rv = ExtractData(nsContentUtils::GetCurrentJSContext(),
type, EmptyString(), getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, rv);
uint64_t imgSize;
@ -1246,7 +1278,8 @@ HTMLCanvasElement::IsContextCleanForFrameCapture()
}
nsresult
HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener)
HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener,
bool aReturnPlaceholderData)
{
WeakPtr<FrameCaptureListener> listener = aListener;
@ -1285,7 +1318,9 @@ HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener)
}
mRequestedFrameRefreshObserver =
new RequestedFrameRefreshObserver(this, driver);
new RequestedFrameRefreshObserver(this, driver, aReturnPlaceholderData);
} else {
mRequestedFrameRefreshObserver->SetReturnPlaceholderData(aReturnPlaceholderData);
}
mRequestedFrameListeners.AppendElement(listener);

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

@ -262,7 +262,8 @@ public:
* caller's responsibility to keep them alive. Once a registered
* FrameCaptureListener is destroyed it will be automatically deregistered.
*/
nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener);
nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener,
bool aReturnPlaceholderData);
/*
* Returns true when there is at least one registered FrameCaptureListener
@ -348,7 +349,8 @@ protected:
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType) override;
nsresult ExtractData(nsAString& aType,
nsresult ExtractData(JSContext* aCx,
nsAString& aType,
const nsAString& aOptions,
nsIInputStream** aStream);
nsresult ToDataURLImpl(JSContext* aCx,

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

@ -1860,6 +1860,8 @@ void HTMLMediaElement::AbortExistingLoads()
mEventDeliveryPaused = false;
mPendingEvents.Clear();
AssertReadyStateIsNothing();
}
void HTMLMediaElement::NoSupportedMediaSourceError(const nsACString& aErrorDetails)
@ -1921,6 +1923,8 @@ void HTMLMediaElement::QueueLoadFromSourceTask()
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
}
AssertReadyStateIsNothing();
ChangeDelayLoadStatus(true);
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
RefPtr<Runnable> r = NewRunnableMethod("HTMLMediaElement::LoadFromSourceChildren",
@ -2579,6 +2583,8 @@ nsresult HTMLMediaElement::LoadResource()
return FinishDecoderSetup(decoder);
}
AssertReadyStateIsNothing();
RefPtr<ChannelLoader> loader = new ChannelLoader;
nsresult rv = loader->Load(this);
if (NS_SUCCEEDED(rv)) {
@ -4868,12 +4874,36 @@ HTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
return NS_OK;
}
void
HTMLMediaElement::AssertReadyStateIsNothing()
{
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
if (mReadyState != nsIDOMHTMLMediaElement::HAVE_NOTHING) {
char buf[1024];
SprintfLiteral(buf,
"readyState=%d networkState=%d mLoadWaitStatus=%d "
"mSourceLoadCandidate=%d "
"mIsLoadingFromSourceChildren=%d mPreloadAction=%d "
"mSuspendedForPreloadNone=%d error=%d",
int(mReadyState.Ref()),
int(mNetworkState),
int(mLoadWaitStatus),
!!mSourceLoadCandidate,
mIsLoadingFromSourceChildren,
int(mPreloadAction),
mSuspendedForPreloadNone,
GetError() ? GetError()->Code() : 0);
MOZ_CRASH_UNSAFE_PRINTF("ReadyState should be HAVE_NOTHING! %s", buf);
}
#endif
}
nsresult
HTMLMediaElement::InitializeDecoderAsClone(ChannelMediaDecoder* aOriginal)
{
NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
MOZ_DIAGNOSTIC_ASSERT(mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING);
AssertReadyStateIsNothing();
MediaDecoderInit decoderInit(this,
mMuted ? 0.0 : mVolume,
@ -4927,7 +4957,7 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
nsIStreamListener** aListener)
{
NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
MOZ_DIAGNOSTIC_ASSERT(mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING);
AssertReadyStateIsNothing();
DecoderDoctorDiagnostics diagnostics;
@ -5041,6 +5071,10 @@ HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder)
// This will also do an AddRemoveSelfReference.
NotifyOwnerDocumentActivityChanged();
if (mPausedForInactiveDocumentOrChannel) {
mDecoder->Suspend();
}
nsresult rv = NS_OK;
if (!mPaused) {
SetPlayedOrSeeked(true);
@ -6586,6 +6620,7 @@ void HTMLMediaElement::NotifyAddedSource()
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
{
AssertReadyStateIsNothing();
QueueSelectResourceTask();
}

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

@ -1820,6 +1820,9 @@ private:
// resolved/rejected at AsyncResolveSeekDOMPromiseIfExists()/
// AsyncRejectSeekDOMPromiseIfExists() methods.
RefPtr<dom::Promise> mSeekDOMPromise;
// For debugging bug 1407148.
void AssertReadyStateIsNothing();
};
// Check if the context is chrome or has the debugger or tabs permission

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

@ -85,6 +85,7 @@ function* testSteps()
ok(!("exception" in info), "shouldn't throw" + test);
is(JSON.stringify(objectStore.keyPath), JSON.stringify(info.keyPath),
"correct keyPath property" + test);
// eslint-disable-next-line no-self-compare
ok(objectStore.keyPath === objectStore.keyPath,
"object identity should be preserved");
stores[indexName] = objectStore;
@ -174,6 +175,7 @@ function* testSteps()
ok(!("exception" in info), "shouldn't throw" + test);
is(JSON.stringify(index.keyPath), JSON.stringify(info.keyPath),
"index has correct keyPath property" + test);
// eslint-disable-next-line no-self-compare
ok(index.keyPath === index.keyPath,
"object identity should be preserved");
indexes[indexName] = index;

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

@ -594,6 +594,14 @@ parent:
*/
async RequestCrossBrowserNavigation(uint32_t aGlobalIndex);
/**
* This function is used to notify the parent that it should display a
* canvas permission prompt.
*
* @param aFirstPartyURI first party of the tab that is requesting access.
*/
async ShowCanvasPermissionPrompt(nsCString aFirstPartyURI);
child:
/**
* Notify the remote browser that it has been Show()n on this

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

@ -3604,6 +3604,27 @@ TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex)
return IPC_OK();
}
mozilla::ipc::IPCResult
TabParent::RecvShowCanvasPermissionPrompt(const nsCString& aFirstPartyURI)
{
nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mFrameElement);
if (!browser) {
// If the tab is being closed, the browser may not be available.
// In this case we can ignore the request.
return IPC_OK();
}
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (!os) {
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = os->NotifyObservers(browser, "canvas-permissions-prompt",
NS_ConvertUTF8toUTF16(aFirstPartyURI).get());
if (NS_FAILED(rv)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
void
TabParent::LiveResizeStarted()
{

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

@ -648,6 +648,7 @@ protected:
const bool& aTruncate) override;
virtual mozilla::ipc::IPCResult RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) override;
virtual mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt(const nsCString& aFirstPartyURI) override;
ContentCacheInParent mContentCache;

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

@ -289,11 +289,18 @@ public:
!mImage.height) {
return ImageRect();
}
gfx::IntRect imageRect = ImageRect();
int64_t w = (aWidth * imageRect.Width()) / mImage.width;
int64_t h = (aHeight * imageRect.Height()) / mImage.height;
if (!w || !h) {
return imageRect;
}
imageRect.x = (imageRect.x * aWidth) / mImage.width;
imageRect.y = (imageRect.y * aHeight) / mImage.height;
imageRect.SetWidth((aWidth * imageRect.Width()) / mImage.width);
imageRect.SetHeight((aHeight * imageRect.Height()) / mImage.height);
imageRect.SetWidth(w);
imageRect.SetHeight(h);
return imageRect;
}

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

@ -157,6 +157,7 @@ CaptureTask::SetCurrentFrames(const VideoSegment& aSegment)
options,
false,
image,
false,
new EncodeComplete(this));
if (NS_FAILED(rv)) {
PostTrackEndEvent();

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

@ -96,18 +96,21 @@ TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
, mType(aType)
, mParser(ContainerParser::CreateForMIMEType(aType))
, mProcessedInput(0)
, mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
, mParentDecoder(
new nsMainThreadPtrHolder<MediaSourceDecoder>(
"TrackBuffersManager::mParentDecoder", aParentDecoder, false /* strict */))
, mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(
"TrackBuffersManager::mParentDecoder",
aParentDecoder,
false /* strict */))
, mAbstractMainThread(aParentDecoder->AbstractMainThread())
, mEnded(false)
, mVideoEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.video",
100 * 1024 * 1024))
, mAudioEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold.audio",
20 * 1024 * 1024))
, mVideoEvictionThreshold(
Preferences::GetUint("media.mediasource.eviction_threshold.video",
100 * 1024 * 1024))
, mAudioEvictionThreshold(
Preferences::GetUint("media.mediasource.eviction_threshold.audio",
20 * 1024 * 1024))
, mEvictionState(EvictionState::NO_EVICTION_NEEDED)
, mMonitor("TrackBuffersManager")
, mMutex("TrackBuffersManager")
, mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
{
MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
}
@ -127,8 +130,12 @@ TrackBuffersManager::AppendData(already_AddRefed<MediaByteBuffer> aData,
mEnded = false;
return InvokeAsync(GetTaskQueue(), this, __func__,
&TrackBuffersManager::DoAppendData, data.forget(), aAttributes);
return InvokeAsync(static_cast<AbstractThread*>(GetTaskQueueSafe().get()),
this,
__func__,
&TrackBuffersManager::DoAppendData,
data.forget(),
aAttributes);
}
RefPtr<TrackBuffersManager::AppendPromise>
@ -148,7 +155,8 @@ TrackBuffersManager::QueueTask(SourceBufferTask* aTask)
// The source buffer is a wrapped native, it would be unlinked twice and so
// the TrackBuffersManager::Detach() would also be called twice. Since the
// detach task has been done before, we could ignore this task.
if (!GetTaskQueue()) {
RefPtr<AutoTaskQueue> taskQueue = GetTaskQueueSafe();
if (!taskQueue) {
MOZ_ASSERT(aTask->GetType() == SourceBufferTask::Type::Detach,
"only detach task could happen here!");
MSE_DEBUG("Could not queue the task '%s' without task queue",
@ -156,15 +164,14 @@ TrackBuffersManager::QueueTask(SourceBufferTask* aTask)
return;
}
if (!OnTaskQueue()) {
GetTaskQueue()->Dispatch(NewRunnableMethod<RefPtr<SourceBufferTask>>(
if (!taskQueue->IsCurrentThreadIn()) {
taskQueue->Dispatch(NewRunnableMethod<RefPtr<SourceBufferTask>>(
"TrackBuffersManager::QueueTask",
this,
&TrackBuffersManager::QueueTask,
aTask));
return;
}
MOZ_ASSERT(OnTaskQueue());
mQueue.Push(aTask);
ProcessTasks();
}
@ -172,8 +179,16 @@ TrackBuffersManager::QueueTask(SourceBufferTask* aTask)
void
TrackBuffersManager::ProcessTasks()
{
// ProcessTask is always called OnTaskQueue, however it is possible that it is
// called once again after a first Detach task has run, in which case
// mTaskQueue would be null.
// This can happen under two conditions:
// 1- Two Detach tasks were queued in a row due to a double cycle collection.
// 2- An call to ProcessTasks() had queued another run of ProcessTasks while
// a Detach task is pending.
// We handle these two cases by aborting early.
// A second Detach task was queued, prior the first one running, ignore it.
if (!GetTaskQueue()) {
if (!mTaskQueue) {
RefPtr<SourceBufferTask> task = mQueue.Pop();
if (!task) {
return;
@ -234,17 +249,17 @@ TrackBuffersManager::ProcessTasks()
break;
case Type::Detach:
mCurrentInputBuffer = nullptr;
mTaskQueue = nullptr;
MOZ_DIAGNOSTIC_ASSERT(mQueue.Length() == 0,
"Detach task must be the last");
mVideoTracks.Reset();
mAudioTracks.Reset();
ShutdownDemuxers();
ResetTaskQueue();
return;
default:
NS_WARNING("Invalid Task");
}
GetTaskQueue()->Dispatch(
TaskQueueFromTaskQueue()->Dispatch(
NewRunnableMethod("TrackBuffersManager::ProcessTasks",
this,
&TrackBuffersManager::ProcessTasks));
@ -295,7 +310,9 @@ TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
mEnded = false;
return InvokeAsync(GetTaskQueue(), this, __func__,
return InvokeAsync(static_cast<AbstractThread*>(GetTaskQueueSafe().get()),
this,
__func__,
&TrackBuffersManager::CodedFrameRemovalWithPromise,
TimeInterval(aStart, aEnd));
}
@ -351,7 +368,7 @@ TrackBuffersManager::Buffered() const
// http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
nsTArray<const TimeIntervals*> tracks;
if (HasVideo()) {
tracks.AppendElement(&mVideoBufferedRanges);
@ -635,7 +652,7 @@ TrackBuffersManager::CodedFrameRemoval(TimeInterval aInterval)
void
TrackBuffersManager::UpdateBufferedRanges()
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
mVideoBufferedRanges = mVideoTracks.mSanitizedBufferedRanges;
mAudioBufferedRanges = mAudioTracks.mSanitizedBufferedRanges;
@ -775,8 +792,9 @@ TrackBuffersManager::SegmentParserLoop()
// 3. If the input buffer contains one or more complete coded frames, then run the coded frame processing algorithm.
RefPtr<TrackBuffersManager> self = this;
CodedFrameProcessing()
->Then(GetTaskQueue(), __func__,
[self] (bool aNeedMoreData) {
->Then(TaskQueueFromTaskQueue(),
__func__,
[self](bool aNeedMoreData) {
self->mProcessingRequest.Complete();
if (aNeedMoreData) {
self->NeedMoreData();
@ -784,7 +802,7 @@ TrackBuffersManager::SegmentParserLoop()
self->ScheduleSegmentParserLoop();
}
},
[self] (const MediaResult& aRejectValue) {
[self](const MediaResult& aRejectValue) {
self->mProcessingRequest.Complete();
self->RejectAppend(aRejectValue, __func__);
})
@ -825,7 +843,8 @@ TrackBuffersManager::RejectAppend(const MediaResult& aRejectValue, const char* a
void
TrackBuffersManager::ScheduleSegmentParserLoop()
{
GetTaskQueue()->Dispatch(
MOZ_ASSERT(OnTaskQueue());
TaskQueueFromTaskQueue()->Dispatch(
NewRunnableMethod("TrackBuffersManager::SegmentParserLoop",
this,
&TrackBuffersManager::SegmentParserLoop));
@ -874,6 +893,7 @@ TrackBuffersManager::CreateDemuxerforMIMEType()
void
TrackBuffersManager::ResetDemuxingState()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mParser && mParser->HasInitData());
RecreateParser(true);
mCurrentInputBuffer = new SourceBufferResource();
@ -887,7 +907,8 @@ TrackBuffersManager::ResetDemuxingState()
return;
}
mInputDemuxer->Init()
->Then(GetTaskQueue(), __func__,
->Then(TaskQueueFromTaskQueue(),
__func__,
this,
&TrackBuffersManager::OnDemuxerResetDone,
&TrackBuffersManager::OnDemuxerInitFailed)
@ -958,6 +979,7 @@ TrackBuffersManager::AppendDataToCurrentInputBuffer(MediaByteBuffer* aData)
void
TrackBuffersManager::InitializationSegmentReceived()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mParser->HasCompleteInitData());
int64_t endInit = mParser->InitSegmentRange().mEnd;
@ -989,7 +1011,8 @@ TrackBuffersManager::InitializationSegmentReceived()
return;
}
mInputDemuxer->Init()
->Then(GetTaskQueue(), __func__,
->Then(TaskQueueFromTaskQueue(),
__func__,
this,
&TrackBuffersManager::OnDemuxerInitDone,
&TrackBuffersManager::OnDemuxerInitFailed)
@ -1177,7 +1200,7 @@ TrackBuffersManager::OnDemuxerInitDone(const MediaResult& aResult)
}
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
mInfo = info;
}
@ -1292,7 +1315,9 @@ TrackBuffersManager::DoDemuxVideo()
return;
}
mVideoTracks.mDemuxer->GetSamples(-1)
->Then(GetTaskQueue(), __func__, this,
->Then(TaskQueueFromTaskQueue(),
__func__,
this,
&TrackBuffersManager::OnVideoDemuxCompleted,
&TrackBuffersManager::OnVideoDemuxFailed)
->Track(mVideoTracks.mDemuxRequest);
@ -1334,7 +1359,9 @@ TrackBuffersManager::DoDemuxAudio()
return;
}
mAudioTracks.mDemuxer->GetSamples(-1)
->Then(GetTaskQueue(), __func__, this,
->Then(TaskQueueFromTaskQueue(),
__func__,
this,
&TrackBuffersManager::OnAudioDemuxCompleted,
&TrackBuffersManager::OnAudioDemuxFailed)
->Track(mAudioTracks.mDemuxRequest);
@ -1871,7 +1898,7 @@ TrackBuffersManager::UpdateHighestTimestamp(TrackData& aTrackData,
const media::TimeUnit& aHighestTime)
{
if (aHighestTime > aTrackData.mHighestStartTimestamp) {
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
aTrackData.mHighestStartTimestamp = aHighestTime;
}
}
@ -1965,7 +1992,7 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
aTrackData.mEvictionIndex.mLastIndex >= samplesRemoved &&
aTrackData.mEvictionIndex.mEvictable >= sizeRemoved,
"Invalid eviction index");
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
aTrackData.mEvictionIndex.mLastIndex -= samplesRemoved;
aTrackData.mEvictionIndex.mEvictable -= sizeRemoved;
} else {
@ -2004,7 +2031,7 @@ TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
highestStartTime = sample->mTime;
}
}
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
aTrackData.mHighestStartTimestamp = highestStartTime;
}
@ -2066,7 +2093,7 @@ TrackBuffersManager::SetAppendState(AppendState aAppendState)
MediaInfo
TrackBuffersManager::GetMetadata() const
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
return mInfo;
}
@ -2087,7 +2114,7 @@ TrackBuffersManager::HighestStartTime(TrackInfo::TrackType aTrack) const
TimeIntervals
TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
return aTrack == TrackInfo::kVideoTrack
? mVideoBufferedRanges
: mAudioBufferedRanges;
@ -2096,7 +2123,7 @@ TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const
TimeUnit
TrackBuffersManager::HighestStartTime() const
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
TimeUnit highestStartTime;
for (auto& track : GetTracksList()) {
highestStartTime =
@ -2108,7 +2135,7 @@ TrackBuffersManager::HighestStartTime() const
TimeUnit
TrackBuffersManager::HighestEndTime() const
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
nsTArray<const TimeIntervals*> tracks;
if (HasVideo()) {
@ -2124,7 +2151,7 @@ TimeUnit
TrackBuffersManager::HighestEndTime(
nsTArray<const TimeIntervals*>& aTracks) const
{
mMonitor.AssertCurrentThreadOwns();
mMutex.AssertCurrentThreadOwns();
TimeUnit highestEndTime;
@ -2137,7 +2164,7 @@ TrackBuffersManager::HighestEndTime(
void
TrackBuffersManager::ResetEvictionIndex(TrackData& aTrackData)
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
aTrackData.mEvictionIndex.Reset();
}
@ -2157,7 +2184,7 @@ TrackBuffersManager::UpdateEvictionIndex(TrackData& aTrackData,
evictable += data[i]->ComputedSizeOfIncludingThis();
}
aTrackData.mEvictionIndex.mLastIndex = currentIndex;
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
aTrackData.mEvictionIndex.mEvictable += evictable;
}
@ -2535,7 +2562,7 @@ TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack,
uint32_t
TrackBuffersManager::Evictable(TrackInfo::TrackType aTrack) const
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
return GetTracksData(aTrack).mEvictionIndex.mEvictable;
}

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

@ -9,7 +9,8 @@
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozilla/NotNull.h"
#include "AutoTaskQueue.h"
#include "MediaContainerType.h"
@ -35,7 +36,7 @@ class SourceBufferTaskQueue
{
public:
SourceBufferTaskQueue()
: mMonitor("SourceBufferTaskQueue")
: mMutex("SourceBufferTaskQueue")
{}
~SourceBufferTaskQueue()
{
@ -44,13 +45,13 @@ public:
void Push(SourceBufferTask* aTask)
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
mQueue.AppendElement(aTask);
}
already_AddRefed<SourceBufferTask> Pop()
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
if (!mQueue.Length()) {
return nullptr;
}
@ -61,12 +62,12 @@ public:
nsTArray<SourceBufferTask>::size_type Length() const
{
MonitorAutoLock mon(mMonitor);
MutexAutoLock mut(mMutex);
return mQueue.Length();
}
private:
mutable Monitor mMonitor;
mutable Mutex mMutex;
nsTArray<RefPtr<SourceBufferTask>> mQueue;
};
@ -455,16 +456,29 @@ private:
TrackData mAudioTracks;
// TaskQueue methods and objects.
AbstractThread* GetTaskQueue() const
RefPtr<AutoTaskQueue> GetTaskQueueSafe() const
{
MutexAutoLock mut(mMutex);
return mTaskQueue;
}
NotNull<AbstractThread*> TaskQueueFromTaskQueue() const
{
#ifdef DEBUG
RefPtr<AutoTaskQueue> taskQueue = GetTaskQueueSafe();
MOZ_ASSERT(taskQueue && taskQueue->IsCurrentThreadIn());
#endif
return WrapNotNull(mTaskQueue.get());
}
bool OnTaskQueue() const
{
MOZ_RELEASE_ASSERT(GetTaskQueue());
return GetTaskQueue()->IsCurrentThreadIn();
auto taskQueue = TaskQueueFromTaskQueue();
return taskQueue->IsCurrentThreadIn();
}
void ResetTaskQueue()
{
MutexAutoLock mut(mMutex);
mTaskQueue = nullptr;
}
RefPtr<AutoTaskQueue> mTaskQueue;
// SourceBuffer Queues and running context.
SourceBufferTaskQueue mQueue;
@ -506,7 +520,11 @@ private:
Atomic<EvictionState> mEvictionState;
// Monitor to protect following objects accessed across multiple threads.
mutable Monitor mMonitor;
mutable Mutex mMutex;
// mTaskQueue is only ever written after construction on the task queue.
// As such, it can be accessed while on task queue without the need for the
// mutex.
RefPtr<AutoTaskQueue> mTaskQueue;
// Stable audio and video track time ranges.
media::TimeIntervals mVideoBufferedRanges;
media::TimeIntervals mAudioBufferedRanges;

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

@ -326,13 +326,16 @@ CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
Float S = sqrt(B * B - A * C);
Float n1 = (- B + S) / A;
Float n2 = (- B - S) / A;
Float n1 = - B + S;
Float n2 = - B - S;
MOZ_ASSERT(n1 >= 0);
MOZ_ASSERT(n2 >= 0);
#ifdef DEBUG
Float epsilon = (Float) 0.001;
MOZ_ASSERT(n1 >= -epsilon);
MOZ_ASSERT(n2 >= -epsilon);
#endif
return n1 < n2 ? n1 : n2;
return std::max((n1 < n2 ? n1 : n2) / A, (Float) 0.0);
}
} // namespace gfx

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

@ -15,6 +15,13 @@
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "mozilla/gfx/Logging.h"
#include "nsReadableUtils.h"
#include "nsString.h"
#include "nsUTF8Utils.h"
// IPP spec disallow the job-name which is over 255 characters.
// RFC: https://tools.ietf.org/html/rfc2911#section-4.1.2
#define IPP_JOB_NAME_LIMIT_LENGTH 255
namespace mozilla {
namespace gfx {
@ -160,6 +167,33 @@ PrintTarget::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
return do_AddRef(mRefDT);
}
/* static */
void
PrintTarget::AdjustPrintJobNameForIPP(const nsAString& aJobName,
nsCString& aAdjustedJobName)
{
CopyUTF16toUTF8(aJobName, aAdjustedJobName);
if (aAdjustedJobName.Length() > IPP_JOB_NAME_LIMIT_LENGTH) {
uint32_t length =
RewindToPriorUTF8Codepoint(aAdjustedJobName.get(),
(IPP_JOB_NAME_LIMIT_LENGTH - 3U));
aAdjustedJobName.SetLength(length);
aAdjustedJobName.AppendLiteral("...");
}
}
/* static */
void
PrintTarget::AdjustPrintJobNameForIPP(const nsAString& aJobName,
nsString& aAdjustedJobName)
{
nsAutoCString jobName;
AdjustPrintJobNameForIPP(aJobName, jobName);
CopyUTF8toUTF16(jobName, aAdjustedJobName);
}
/* static */ already_AddRefed<DrawTarget>
PrintTarget::CreateWrapAndRecordDrawTarget(DrawEventRecorder* aRecorder,
DrawTarget* aDrawTarget)

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

@ -136,6 +136,11 @@ public:
*/
virtual already_AddRefed<DrawTarget> GetReferenceDrawTarget(DrawEventRecorder* aRecorder);
static void AdjustPrintJobNameForIPP(const nsAString& aJobName,
nsCString& aAdjustedJobName);
static void AdjustPrintJobNameForIPP(const nsAString& aJobName,
nsString& aAdjustedJobName);
protected:
// Only created via subclass's constructors

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

@ -11,6 +11,7 @@
#include "mozilla/ServoStyleRule.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/ServoImportRule.h"
#include "mozilla/StyleSheetInlines.h"
#include "nsDocument.h"
#include "nsStyleSheetService.h"
@ -175,7 +176,10 @@ void
ServoStyleRuleMap::FillTableFromStyleSheet(ServoStyleSheet* aSheet)
{
if (aSheet->IsComplete()) {
FillTableFromRuleList(aSheet->GetCssRulesInternal());
// XBL stylesheets are not expected to ever change, so it's a waste
// to make its inner unique.
FillTableFromRuleList(aSheet->GetCssRulesInternal(
/* aRequireUniqueInner = */ !mStyleSet->IsForXBL()));
}
}

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

@ -284,7 +284,8 @@ inDOMUtils::GetCSSStyleRules(nsIDOMElement *aElement,
// Collect style rule maps for bindings.
for (nsIContent* bindingContent = element; bindingContent;
bindingContent = bindingContent->GetBindingParent()) {
if (nsXBLBinding* binding = bindingContent->GetXBLBinding()) {
for (nsXBLBinding* binding = bindingContent->GetXBLBinding();
binding; binding = binding->GetBaseBinding()) {
if (ServoStyleSet* styleSet = binding->GetServoStyleSet()) {
ServoStyleRuleMap* map = styleSet->StyleRuleMap();
map->EnsureTable();

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

@ -136,6 +136,8 @@ SERVO_BINDING_FUNC(Servo_SelectorList_Parse,
const nsACString* selector_list)
SERVO_BINDING_FUNC(Servo_SelectorList_Matches, bool,
RawGeckoElementBorrowed, RawServoSelectorListBorrowed)
SERVO_BINDING_FUNC(Servo_SelectorList_Closest, RawGeckoElementBorrowedOrNull,
RawGeckoElementBorrowed, RawServoSelectorListBorrowed)
SERVO_BINDING_FUNC(Servo_StyleSet_AddSizeOfExcludingThis, void,
mozilla::MallocSizeOf malloc_size_of,
mozilla::MallocSizeOf malloc_enclosing_size_of,

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

@ -478,6 +478,9 @@ public:
ServoStyleContext* aNewLayoutParent,
Element* aElement);
bool IsMaster() const { return mKind == Kind::Master; }
bool IsForXBL() const { return mKind == Kind::ForXBL; }
private:
friend class AutoSetInServoTraversal;
friend class AutoPrepareTraversal;
@ -489,9 +492,6 @@ private:
*/
const SnapshotTable& Snapshots();
bool IsMaster() const { return mKind == Kind::Master; }
bool IsForXBL() const { return mKind == Kind::ForXBL; }
/**
* Resolve all ServoDeclarationBlocks attached to mapped
* presentation attributes cached on the document.

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

@ -393,10 +393,15 @@ ServoStyleSheet::Clone(StyleSheet* aCloneParent,
}
ServoCSSRuleList*
ServoStyleSheet::GetCssRulesInternal()
ServoStyleSheet::GetCssRulesInternal(bool aRequireUniqueInner)
{
if (!mRuleList) {
EnsureUniqueInner();
MOZ_ASSERT(aRequireUniqueInner || !mDocument,
"Not requiring unique inner for stylesheet associated "
"with document may have undesired behavior");
if (aRequireUniqueInner) {
EnsureUniqueInner();
}
RefPtr<ServoCssRules> rawRules =
Servo_StyleSheet_GetRules(Inner()->mContents).Consume();

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

@ -120,7 +120,7 @@ public:
// Internal GetCssRules method which do not have security check and
// completelness check.
ServoCSSRuleList* GetCssRulesInternal();
ServoCSSRuleList* GetCssRulesInternal(bool aRequireUniqueInner = true);
// Returns the stylesheet's Servo origin as an OriginFlags value.
OriginFlags GetOrigin();

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

@ -450,7 +450,7 @@ StyleSheet::EnsureUniqueInner()
"unexpected number of outers");
mDirty = true;
if (mInner->mSheets.Length() == 1) {
if (HasUniqueInner()) {
// already unique
return;
}

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

@ -138,6 +138,7 @@ public:
bool IsModified() const { return mDirty; }
inline bool HasUniqueInner() const;
void EnsureUniqueInner();
// Append all of this sheet's child sheets to aArray.

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

@ -129,6 +129,12 @@ StyleSheet::GetIntegrity(dom::SRIMetadata& aResult) const
aResult = SheetInfo().mIntegrity;
}
bool
StyleSheet::HasUniqueInner() const
{
return mInner->mSheets.Length() == 1;
}
}
#endif // mozilla_StyleSheetInlines_h

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

@ -341,6 +341,7 @@ skip-if = toolkit == 'android' && debug # bug 1397615
[test_variable_serialization_specified.html]
[test_variables.html]
support-files = support/external-variable-url.css
[test_variables_loop.html]
[test_variables_order.html]
support-files = support/external-variable-url.css
[test_video_object_fit.html]

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

@ -0,0 +1,31 @@
<!DOCTYPE html>
<title>CSS variables loop resolving</title>
<script src="/MochiKit/MochiKit.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" type="text/css">
<style id="test">
#outer {
--a: a;
--b: b;
--c: c;
--d: d;
--e: e;
}
#inner {
--a: var(--d, ad);
--b: var(--d, ad);
--c: var(--d, ad);
--d: var(--e, de);
--e: var(--a, ea) var(--b, eb) var(--c, ec);
}
</style>
<div id="outer">
<div id="inner"></div>
</div>
<script>
let inner_cs = getComputedStyle(document.getElementById("inner"));
for (let v of ['a', 'b', 'c', 'd', 'e']) {
is(inner_cs.getPropertyValue(`--${v}`), "",
`Variable --${v} should be eliminated`);
}
</script>

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

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ParseFTPList.h"
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
@ -25,8 +26,22 @@ static inline int ParsingFailed(struct list_state *state)
return '"'; /* its part of a comment or error message */
}
void
FixupYear(PRExplodedTime* aTime)
{
/* if year has only two digits then assume that
00-79 is 2000-2079
80-99 is 1980-1999 */
if (aTime->tm_year < 80) {
aTime->tm_year += 2000;
} else if (aTime->tm_year < 100) {
aTime->tm_year += 1900;
}
}
int ParseFTPList(const char *line, struct list_state *state,
struct list_result *result )
struct list_result *result, PRTimeParamFn timeParam,
NowTimeFn nowTimeFn)
{
unsigned int carry_buf_len; /* copy of state->carry_buf_len */
unsigned int pos;
@ -136,7 +151,7 @@ int ParseFTPList(const char *line, struct list_state *state,
PRTime seconds;
PR_sscanf(p+1, "%llu", &seconds);
t = seconds * PR_USEC_PER_SEC;
PR_ExplodeTime(t, PR_LocalTimeParameters, &(result->fe_time) );
PR_ExplodeTime(t, timeParam, &(result->fe_time) );
}
}
}
@ -453,46 +468,17 @@ int ParseFTPList(const char *line, struct list_state *state,
while (pos < toklen[1] && (tokens[1][pos]) != '/')
pos++;
/*
* I've never seen size come back in bytes, its always in blocks, and
* the following test fails. So, always perform the "size in blocks".
* I'm leaving the "size in bytes" code if'd out in case we ever need
* to re-instate it.
*/
#if 0
if (pos < toklen[1] && ( (pos<<1) > (toklen[1]-1) ||
(strtoul(tokens[1], (char **)0, 10) >
strtoul(tokens[1]+pos+1, (char **)0, 10)) ))
{ /* size is in bytes */
if (pos > (sizeof(result->fe_size)-1))
pos = sizeof(result->fe_size)-1;
memcpy( result->fe_size, tokens[1], pos );
result->fe_size[pos] = '\0';
}
else /* size is in blocks */
#endif
{
/* size requires multiplication by blocksize.
*
* We could assume blocksize is 512 (like Lynx does) and
* shift by 9, but that might not be right. Even if it
* were, doing that wouldn't reflect what the file's
* real size was. The sanest thing to do is not use the
* LISTing's filesize, so we won't (like ftpmirror).
*
* ulltoa(((unsigned long long)fsz)<<9, result->fe_size, 10);
*
* A block is always 512 bytes on OpenVMS, compute size.
* So its rounded up to the next block, so what, its better
* than not showing the size at all.
* A block is always 512 bytes on OpenVMS, compute size.
* So its rounded up to the next block, so what, its better
* than not showing the size at all.
*/
uint64_t fsz = uint64_t(strtoul(tokens[1], (char **)0, 10) * 512);
SprintfLiteral(result->fe_size, "%" PRId64, fsz);
}
/*
* On OpenVMS, the size is given in blocks. A block is 512
* bytes. This can only approximate the size of the file,
* but that's better than not showing a size at all.
* numBlocks is clamped to UINT32_MAX to make 32-bit and
* 64-bit builds return consistent results.
*/
uint64_t numBlocks = strtoul(tokens[1], nullptr, 10);
numBlocks = std::min(numBlocks, (uint64_t)UINT32_MAX);
uint64_t fileSize = numBlocks * 512;
SprintfLiteral(result->fe_size, "%" PRIu64, fileSize);
} /* if (result->fe_type != 'd') */
p = tokens[2] + 2;
@ -655,7 +641,7 @@ int ParseFTPList(const char *line, struct list_state *state,
p = tokens[tokmarker+4];
if (toklen[tokmarker+4] == 10) /* newstyle: YYYY-MM-DD format */
{
result->fe_time.tm_year = atoi(p+0) - 1900;
result->fe_time.tm_year = atoi(p+0);
result->fe_time.tm_month = atoi(p+5) - 1;
result->fe_time.tm_mday = atoi(p+8);
}
@ -665,8 +651,7 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_time.tm_month = atoi(p) - 1;
result->fe_time.tm_mday = atoi((p+pos)-5);
result->fe_time.tm_year = atoi((p+pos)-2);
if (result->fe_time.tm_year < 70)
result->fe_time.tm_year += 100;
FixupYear(&result->fe_time);
}
p = tokens[tokmarker+5];
@ -829,13 +814,7 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_time.tm_month--;
result->fe_time.tm_mday = atoi(tokens[0]+3);
result->fe_time.tm_year = atoi(tokens[0]+6);
/* if year has only two digits then assume that
00-79 is 2000-2079
80-99 is 1980-1999 */
if (result->fe_time.tm_year < 80)
result->fe_time.tm_year += 2000;
else if (result->fe_time.tm_year < 100)
result->fe_time.tm_year += 1900;
FixupYear(&result->fe_time);
}
result->fe_time.tm_hour = atoi(tokens[1]+0);
@ -947,8 +926,7 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_time.tm_month = atoi(&p[35-18]) - 1;
result->fe_time.tm_mday = atoi(&p[38-18]);
result->fe_time.tm_year = atoi(&p[41-18]);
if (result->fe_time.tm_year < 80)
result->fe_time.tm_year += 100;
FixupYear(&result->fe_time);
result->fe_time.tm_hour = atoi(&p[46-18]);
result->fe_time.tm_min = atoi(&p[49-18]);
@ -1161,8 +1139,8 @@ int ParseFTPList(const char *line, struct list_state *state,
if (!state->now_time)
{
state->now_time = PR_Now();
PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) );
state->now_time = nowTimeFn();
PR_ExplodeTime((state->now_time), timeParam, &(state->now_tm) );
}
result->fe_time.tm_year = state->now_tm.tm_year;
@ -1374,7 +1352,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
result->fe_time.tm_month = pos/3;
result->fe_time.tm_mday = atoi(tokens[3]);
result->fe_time.tm_year = atoi(tokens[4]) - 1900;
result->fe_time.tm_year = atoi(tokens[4]);
break;
}
}
@ -1385,8 +1363,7 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_time.tm_month = atoi(p+0)-1;
result->fe_time.tm_mday = atoi(p+3);
result->fe_time.tm_year = atoi(p+6);
if (result->fe_time.tm_year < 80) /* SuperTCP */
result->fe_time.tm_year += 100;
FixupYear(&result->fe_time); /* SuperTCP */
pos = 3; /* SuperTCP toknum of date field */
}
@ -1631,7 +1608,7 @@ int ParseFTPList(const char *line, struct list_state *state,
pos = atoi(p);
if (pos > 24)
result->fe_time.tm_year = pos-1900;
result->fe_time.tm_year = pos;
else
{
if (p[1] == ':')
@ -1640,8 +1617,8 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_time.tm_min = atoi(p+3);
if (!state->now_time)
{
state->now_time = PR_Now();
PR_ExplodeTime((state->now_time), PR_LocalTimeParameters, &(state->now_tm) );
state->now_time = nowTimeFn();
PR_ExplodeTime((state->now_time), timeParam, &(state->now_tm) );
}
result->fe_time.tm_year = state->now_tm.tm_year;
if ( (( state->now_tm.tm_month << 4) + state->now_tm.tm_mday) <
@ -1688,207 +1665,3 @@ int ParseFTPList(const char *line, struct list_state *state,
return ParsingFailed(state);
}
/* ==================================================================== */
/* standalone testing */
/* ==================================================================== */
#if 0
#include <stdio.h>
static int do_it(FILE *outfile,
char *line, size_t linelen, struct list_state *state,
char **cmnt_buf, unsigned int *cmnt_buf_sz,
char **list_buf, unsigned int *list_buf_sz )
{
struct list_result result;
char *p;
int rc;
rc = ParseFTPList( line, state, &result );
if (!outfile)
{
outfile = stdout;
if (rc == '?')
fprintf(outfile, "junk: %.*s\n", (int)linelen, line );
else if (rc == '"')
fprintf(outfile, "cmnt: %.*s\n", (int)linelen, line );
else
fprintf(outfile,
"list: %02u-%02u-%02u %02u:%02u%cM %20s %.*s%s%.*s\n",
(result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
result.fe_time.tm_mday,
(result.fe_time.tm_mday ? (result.fe_time.tm_year % 100) : 0),
result.fe_time.tm_hour -
((result.fe_time.tm_hour > 12)?(12):(0)),
result.fe_time.tm_min,
((result.fe_time.tm_hour >= 12) ? 'P' : 'A'),
(rc == 'd' ? "<DIR> " :
(rc == 'l' ? "<JUNCTION> " : result.fe_size)),
(int)result.fe_fnlen, result.fe_fname,
((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
(int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") );
}
else if (rc != '?') /* NOT junk */
{
char **bufp = list_buf;
unsigned int *bufz = list_buf_sz;
if (rc == '"') /* comment - make it a 'result' */
{
memset( &result, 0, sizeof(result));
result.fe_fname = line;
result.fe_fnlen = linelen;
result.fe_type = 'f';
if (line[linelen-1] == '/')
{
result.fe_type = 'd';
result.fe_fnlen--;
}
bufp = cmnt_buf;
bufz = cmnt_buf_sz;
rc = result.fe_type;
}
linelen = 80 + result.fe_fnlen + result.fe_lnlen;
p = (char *)realloc( *bufp, *bufz + linelen );
if (!p)
return -1;
sprintf( &p[*bufz],
"%02u-%02u-%04u %02u:%02u:%02u %20s %.*s%s%.*s\n",
(result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
result.fe_time.tm_mday,
(result.fe_time.tm_mday ? (result.fe_time.tm_year + 1900) : 0),
result.fe_time.tm_hour,
result.fe_time.tm_min,
result.fe_time.tm_sec,
(rc == 'd' ? "<DIR> " :
(rc == 'l' ? "<JUNCTION> " : result.fe_size)),
(int)result.fe_fnlen, result.fe_fname,
((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
(int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
((rc == 'l' && result.fe_lnlen) ? result.fe_lname : "") );
linelen = strlen(&p[*bufz]);
*bufp = p;
*bufz = *bufz + linelen;
}
return 0;
}
int main(int argc, char *argv[])
{
FILE *infile = (FILE *)0;
FILE *outfile = (FILE *)0;
int need_close_in = 0;
int need_close_out = 0;
if (argc > 1)
{
infile = stdin;
if (strcmp(argv[1], "-") == 0)
need_close_in = 0;
else if ((infile = fopen(argv[1], "r")) != ((FILE *)0))
need_close_in = 1;
else
fprintf(stderr, "Unable to open input file '%s'\n", argv[1]);
}
if (infile && argc > 2)
{
outfile = stdout;
if (strcmp(argv[2], "-") == 0)
need_close_out = 0;
else if ((outfile = fopen(argv[2], "w")) != ((FILE *)0))
need_close_out = 1;
else
{
fprintf(stderr, "Unable to open output file '%s'\n", argv[2]);
fclose(infile);
infile = (FILE *)0;
}
}
if (!infile)
{
char *appname = &(argv[0][strlen(argv[0])]);
while (appname > argv[0])
{
appname--;
if (*appname == '/' || *appname == '\\' || *appname == ':')
{
appname++;
break;
}
}
fprintf(stderr,
"Usage: %s <inputfilename> [<outputfilename>]\n"
"\nIf an outout file is specified the results will be"
"\nbe post-processed, and only the file entries will appear"
"\n(or all comments if there are no file entries)."
"\nNot specifying an output file causes %s to run in \"debug\""
"\nmode, ie results are printed as lines are parsed."
"\nIf a filename is a single dash ('-'), stdin/stdout is used."
"\n", appname, appname );
}
else
{
char *cmnt_buf = (char *)0;
unsigned int cmnt_buf_sz = 0;
char *list_buf = (char *)0;
unsigned int list_buf_sz = 0;
struct list_state state;
char line[512];
memset( &state, 0, sizeof(state) );
while (fgets(line, sizeof(line), infile))
{
size_t linelen = strlen(line);
if (linelen < (sizeof(line)-1))
{
if (linelen > 0 && line[linelen-1] == '\n')
linelen--;
if (do_it( outfile, line, linelen, &state,
&cmnt_buf, &cmnt_buf_sz, &list_buf, &list_buf_sz) != 0)
{
fprintf(stderr, "Insufficient memory. Listing may be incomplete.\n");
break;
}
}
else
{
/* no '\n' found. drop this and everything up to the next '\n' */
fprintf(stderr, "drop: %.*s", (int)linelen, line );
while (linelen == sizeof(line))
{
if (!fgets(line, sizeof(line), infile))
break;
linelen = 0;
while (linelen < sizeof(line) && line[linelen] != '\n')
linelen++;
fprintf(stderr, "%.*s", (int)linelen, line );
}
fprintf(stderr, "\n");
}
}
if (outfile)
{
if (list_buf)
fwrite( list_buf, 1, list_buf_sz, outfile );
else if (cmnt_buf)
fwrite( cmnt_buf, 1, cmnt_buf_sz, outfile );
}
if (list_buf)
free(list_buf);
if (cmnt_buf)
free(cmnt_buf);
if (need_close_in)
fclose(infile);
if (outfile && need_close_out)
fclose(outfile);
}
return 0;
}
#endif

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

@ -97,8 +97,12 @@ struct list_result
/* (converting all-upcase names may be desirable) */
};
typedef PRTime (*NowTimeFn)();
int ParseFTPList(const char *line,
struct list_state *state,
struct list_result *result );
struct list_result *result,
PRTimeParamFn timeParam = PR_LocalTimeParameters,
NowTimeFn nowTimeFn = PR_Now);
#endif /* !ParseRTPList_h___ */

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

@ -1,174 +0,0 @@
01-11-2001 10:38:25 <DIR> tmp
02-29-2000 03:01:45 <DIR> 1998-100
12-24-2000 21:44:06 1172 mirrors.html
02-29-2000 03:01:47 <DIR> 1999-275
05-08-2000 07:25:16 933 1999-275.html
05-08-2000 07:27:06 322 1998-401.html
02-29-2000 03:01:47 <DIR> 1998-401
02-29-2000 03:01:45 <DIR> 1997-275
05-08-2000 07:28:25 277 1998-515.html
05-08-2000 07:36:43 325 2000-436.html
12-23-2000 07:47:11 <DIR> checkpwd
07-04-2002 19:47:46 <DIR> bib
02-29-2000 03:01:47 <DIR> 1999-541
04-28-2001 05:02:35 <DIR> 2001-260
04-17-2002 20:27:55 <DIR> 2002-501
06-01-2001 22:56:17 <DIR> 1995-514
02-29-2000 03:01:45 <DIR> 1999-180
06-01-2001 04:44:23 475 2000-515.html
02-29-2000 03:01:47 <DIR> 1998-515
02-29-2000 03:01:47 <DIR> 2000-515
05-12-2000 09:46:13 <DIR> zpfft
10-27-2001 15:00:14 <DIR> psibound
02-29-2000 03:01:51 398 anonftpd.html
02-26-2002 03:34:22 <DIR> nistp224
08-10-2001 09:27:20 <DIR> cdb
05-01-2002 01:58:27 <DIR> talks
12-23-2000 22:34:35 <DIR> ftpparse
01-11-2000 06:50:10 <DIR> clockspeed
02-29-2000 03:01:52 1350 clockspeed.html
02-06-2002 21:52:34 <DIR> slashdoc
06-07-2002 18:00:40 <DIR> export
07-17-2001 03:48:36 <DIR> daemontools
07-12-2001 23:17:08 2187 daemontools.html
08-30-2001 22:51:16 175 data.html
12-28-2001 02:32:37 1901 crypto.html
11-11-2001 00:45:23 739 arith.html
11-30-2001 04:48:09 2733 cdb.html
11-11-2001 00:51:02 344 floatasm.html
12-21-2000 22:25:56 <DIR> djbfft
02-29-2000 03:01:52 1319 djbfft.html
07-09-2000 19:16:47 <DIR> dnscache
07-04-2000 00:57:44 4016 dnscache.html
07-10-2001 05:16:22 <DIR> docs
02-29-2000 03:01:52 293 docs.html
02-29-2000 03:01:52 952 dot-forward.html
01-11-2000 06:50:11 <DIR> etc-mta
02-29-2000 03:01:52 3174 etc-mta.html
02-29-2000 03:01:52 2990 ezmlm.html
12-21-2000 22:25:56 <DIR> ftp
02-29-2000 03:01:49 <DIR> im
02-29-2000 03:01:52 1823 fastforward.html
02-29-2000 03:01:52 1707 ftp.html
02-12-2001 03:17:10 <DIR> freebugtraq
06-02-2002 03:39:25 <DIR> hardware
06-01-2001 04:57:35 610 1995-514.html
12-21-2000 22:25:57 <DIR> hash127
05-19-2000 04:29:02 4835 hash127.html
02-29-2000 03:01:52 1515 im.html
02-12-2002 21:40:06 <DIR> immhf
04-25-2001 23:48:49 2902 immhf.html
01-25-2001 06:58:36 <DIR> lib
06-06-2001 01:45:50 <DIR> libtai
07-10-2000 20:54:16 2120 libtai.html
11-27-2001 18:52:40 14460 qmail.html
08-30-2001 22:51:23 2153 mail.html
02-29-2000 03:01:52 108 maildir.html
02-29-2000 03:01:50 <DIR> maildisasters
02-29-2000 03:01:52 873 maildisasters.html
02-29-2000 03:01:52 1688 mess822.html
02-25-2002 03:47:27 <DIR> mirror
12-26-2001 22:02:32 581 ntheory.html
06-03-2002 17:36:27 <DIR> papers
05-21-2002 18:50:15 <DIR> postpropter
01-11-2000 06:50:12 <DIR> postings
01-11-2000 06:50:12 <DIR> primegen
08-30-2001 22:51:33 415 precompiled.html
02-29-2000 03:01:52 1121 primegen.html
05-05-2002 03:37:55 <DIR> proto
02-29-2000 03:01:52 452 proto.html
07-10-2001 00:17:53 <DIR> publicfile
11-30-2001 04:59:17 5651 publicfile.html
08-30-2001 22:51:37 409 qlist.html
04-02-2002 06:25:45 <DIR> qmail
02-02-2002 06:52:28 7549 lists.html
02-29-2000 03:01:52 1166 qmailanalog.html
08-30-2001 22:51:39 1434 qmsmac.html
03-13-2000 06:38:06 465 rblsmtpd.html
02-29-2000 03:01:52 1225 rights.html
01-11-2000 06:50:13 <DIR> sarcasm
09-14-2001 04:34:14 1058 unix.html
02-29-2000 03:01:53 1568 serialmail.html
10-02-2000 21:13:44 840 sigs.html
05-21-2002 18:50:08 <DIR> smtp
04-26-2001 17:58:47 1723 smtp.html
12-21-2000 22:26:03 <DIR> software
02-29-2000 03:01:53 3671 softwarelaw.html
01-11-2000 06:50:13 <DIR> sortedsums
02-29-2000 03:01:53 2735 sortedsums.html
02-26-2002 03:06:38 <DIR> speed
07-04-2000 00:40:17 572 surveydns.html
10-04-2001 02:59:31 <DIR> surveys
10-04-2001 02:46:36 3503 surveys.html
11-10-2000 06:58:08 <DIR> syncookies
02-01-2002 03:03:37 7918 syncookies.html
08-30-2001 22:51:43 1115 tcpcontrol.html
03-18-2002 13:08:27 589 tcpip.html
02-29-2000 03:01:53 906 thoughts.html
07-30-2001 16:18:09 <DIR> threecubes
09-14-2001 03:25:58 525 time.html
05-22-2002 06:21:05 <DIR> ucspi-tcp
01-25-2002 00:41:31 4086 ucspi-tcp.html
08-30-2001 22:51:50 732 web.html
07-04-2002 19:47:48 <DIR> www
09-14-2001 03:26:08 2717 y2k.html
02-08-2001 10:28:09 <DIR> dnsroot
04-23-2001 08:01:37 2745 2001-260.html
02-29-2000 03:01:53 291 zeroseek.html
02-29-2000 03:01:53 <DIR> zmodexp
05-19-2000 04:29:07 1513 zmodexp.html
08-01-2000 03:08:54 <DIR> zmult
08-01-2000 03:24:53 669 zmult.html
12-23-2000 07:19:47 1097 checkpwd.html
12-31-2001 19:47:16 2077 focus.html
07-20-2000 01:11:31 3842 im2000.html
07-04-2002 19:47:43 <DIR> slashcommand
07-04-2002 19:47:44 <DIR> slashpackage
07-16-2001 00:24:39 602 slashpackage.html
05-03-2002 02:15:33 23874 talks.html
05-08-2000 07:24:59 566 1997-275.html
05-08-2000 07:31:04 313 1998-100.html
02-29-2000 03:01:47 <DIR> 2000-436
05-08-2000 07:37:26 660 1997-494.html
05-08-2000 07:40:41 730 1999-541.html
09-13-2000 09:08:58 2367 psibound.html
06-22-2000 04:09:00 1866 smallfactors.html
06-09-2000 03:35:19 <DIR> smallfactors
07-02-2000 00:55:19 <DIR> conferences
05-22-2002 07:52:29 <DIR> djbdns
07-17-2001 06:00:58 2712 djbdns.html
07-17-2000 03:15:39 216 zpfft.html
08-20-2000 20:56:37 <DIR> fastnewton
08-20-2000 20:57:31 333 fastnewton.html
09-01-2000 20:33:51 <DIR> patents
12-23-2000 22:40:17 1748 ftpparse.html
11-11-2001 00:57:50 3261 slash.html
01-17-2001 18:50:10 2663 slashdoc.html
03-18-2002 10:04:35 2344 dnsroot.html
12-26-2001 22:03:00 3238 software.html
09-14-2001 04:34:33 3481 compatibility.html
05-01-2002 05:30:34 5525 distributors.html
02-25-2001 04:49:03 212 freebugtraq.html
03-31-2002 04:59:50 806 hardware.html
03-31-2001 08:15:18 121 securesoftware.html
05-22-2002 04:39:56 10950 conferences.html
11-30-2001 18:51:50 <DIR> 2001-275
05-29-2001 02:15:07 459 rwb100.html
10-03-2001 07:40:42 863 nistp224.html
07-30-2001 16:15:46 778 threecubes.html
06-03-2002 17:32:16 27125 papers.html
11-21-2001 03:51:09 379 bib.html
11-30-2001 18:41:33 3791 2001-275.html
10-02-2001 20:08:59 169 dh224.html
03-16-2002 07:05:36 1126 courses.html
09-03-2001 00:22:28 355 slashcommand.html
03-27-2002 22:20:22 1199 patents.html
11-08-2001 20:27:15 640 mailcopyright.html
06-02-2002 01:27:50 1257 djb.html
01-07-2002 07:12:34 301 2002-501.html
05-01-2002 04:41:45 1655 export.html
03-24-2002 11:40:59 6446 index.html
03-16-2002 07:09:08 570 2005-590.html
03-16-2002 07:05:49 800 2004-494.html
06-02-2002 01:28:59 4459 positions.html
06-02-2002 00:55:31 466 contact.html

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

@ -1,72 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
cmnt: ftp> open ftp.microsoft.com
cmnt: Microsoft FTP Service
cmnt: Name (ftp.microsoft.com:cyp):
cmnt: 331 Anonymous access allowed, send identity (e-mail name) as password.
cmnt: 230-This is FTP.Microsoft.Com
cmnt: Anonymous user logged in.
cmnt: Remote system type is Windows_NT.
cmnt: ftp> quote dirstyle
cmnt: MSDOS-like directory output is on
cmnt: ftp> quote dirstyle
cmnt: MSDOS-like directory output is off
cmnt: ftp> ls
cmnt: PORT command successful.
cmnt: Opening ASCII mode data connection for /bin/ls.
list: 02-25-00 00:00AM <DIR> channel
list: 02-25-00 00:00AM <DIR> Education
list: 02-25-00 00:00AM <DIR> enterprise
list: 06-20-01 00:00AM <DIR> ISN
list: 05-31-01 00:00AM <DIR> Museum
list: 02-14-01 00:00AM <DIR> TechNet
list: 10-24-01 00:00AM <DIR> whql
list: 02-05-01 00:00AM <DIR> 20Year
list: 09-26-00 00:00AM <DIR> AccessFoxPro
list: 12-21-00 00:00AM <DIR> AlbGrp
list: 05-31-01 00:00AM <DIR> Alexandra
list: 01-19-01 00:00AM <DIR> anna
list: 04-06-00 00:00AM <DIR> anz
list: 05-10-00 00:00AM <DIR> Chase Bobko2
list: 03-29-00 00:00AM <DIR> cnn
list: 11-21-00 00:00AM <DIR> Darin
list: 03-09-00 00:00AM <DIR> digitalchicago
list: 09-06-00 00:00AM <DIR> Dublin
list: 01-30-01 00:00AM <DIR> eleanorf
list: 04-26-01 00:00AM <DIR> girvin
list: 04-26-00 00:00AM <DIR> Hires
list: 08-15-00 00:00AM <DIR> HR
list: 10-24-99 00:00AM 4368384 IMG00022.PCD
list: 05-18-01 00:00AM <DIR> jacubowsky
list: 10-12-00 00:00AM <DIR> JFalvey
list: 03-28-01 00:00AM <DIR> johnci
list: 07-14-00 00:00AM <DIR> Karin
list: 09-07-00 00:00AM <DIR> Kjung
list: 09-28-00 00:00AM <DIR> LarryE
list: 08-17-00 00:00AM <DIR> Larson
list: 09-12-00 00:00AM <DIR> marion
list: 08-09-00 00:00AM <DIR> ms25
list: 11-16-00 00:00AM <DIR> MS25Brochure
list: 03-29-00 00:00AM <DIR> MShistory
list: 09-05-00 00:00AM <DIR> Neils
list: 08-02-00 00:00AM <DIR> NLM
list: 09-06-00 00:00AM <DIR> PageOne
list: 06-27-00 00:00AM <DIR> pccomputing
list: 05-09-01 00:00AM <DIR> pictures
list: 07-21-00 00:00AM <DIR> pranks
list: 08-22-00 00:00AM <DIR> Sean
list: 08-10-00 00:00AM <DIR> SLeong
list: 09-07-00 00:00AM <DIR> svr
list: 07-21-00 00:00AM <DIR> Transcontinental
list: 10-23-00 00:00AM <DIR> veronist
list: 06-15-00 00:00AM <DIR> zoe
list: 07-14-00 00:00AM 2094926 canprankdesk.tif
list: 07-21-00 00:00AM 95077 Jon Kauffman Enjoys the Good Life.jpg
list: 07-21-00 00:00AM 52275 Name Plate.jpg
list: 07-14-00 00:00AM 2250540 Valentineoffprank-HiRes.jpg
junk: Transfer complete.
junk: ftp> close
junk: Thank-You For Using Microsoft Products!
junk: ftp>

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

@ -13,6 +13,10 @@ UNIFIED_SOURCES += [
'TestStandardURL.cpp',
]
TEST_DIRS += [
'parse-ftp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul-gtest'

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

@ -46,13 +46,15 @@ ftp> ls
08-22-00 10:36AM <DIR> Sean
08-10-00 01:54PM <DIR> SLeong
09-07-00 05:46PM <DIR> svr
07-21-00 07:22AM <JUNCTION> a_junction_sample
10-23-00 01:27PM <JUNCTION> b_junction_sample => foo
06-15-00 07:37AM <JUNCTION> c_junction_sample -> bar too
07-14-00 01:35PM 2094926 canprankdesk.tif
07-21-00 01:19PM 95077 Jon Kauffman Enjoys the Good Life.jpg
07-21-00 01:19PM 52275 Name Plate.jpg
07-14-00 01:38PM 2250540 Valentineoffprank-HiRes.jpg
01-11-14 12:25AM 18864566 Bug961346
10-10-2014 10:10AM <DIR> Bug1061898
01-18-12 19:00 30724571 Bug1125816
226 Transfer complete.
ftp> close
221 Thank-You For Using Microsoft Products!

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

@ -38,10 +38,12 @@
08-22-2000 10:36:00 <DIR> Sean
08-10-2000 13:54:00 <DIR> SLeong
09-07-2000 17:46:00 <DIR> svr
07-21-2000 07:22:00 <JUNCTION> a_junction_sample
10-23-2000 13:27:00 <JUNCTION> b_junction_sample -> foo
06-15-2000 07:37:00 <JUNCTION> c_junction_sample -> bar too
07-14-2000 13:35:00 2094926 canprankdesk.tif
07-21-2000 13:19:00 95077 Jon Kauffman Enjoys the Good Life.jpg
07-21-2000 13:19:00 52275 Name Plate.jpg
07-14-2000 13:38:00 2250540 Valentineoffprank-HiRes.jpg
01-11-2014 00:25:00 18864566 Bug961346
10-10-2014 10:10:00 <DIR> Bug1061898
01-18-2012 19:00:00 30724571 Bug1125816

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

@ -0,0 +1,178 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
01-11-2001 09:38:25 <DIR> tmp
02-29-2000 02:01:45 <DIR> 1998-100
12-24-2000 20:44:06 1172 mirrors.html
02-29-2000 02:01:47 <DIR> 1999-275
05-08-2000 05:25:16 933 1999-275.html
05-08-2000 05:27:06 322 1998-401.html
02-29-2000 02:01:47 <DIR> 1998-401
02-29-2000 02:01:45 <DIR> 1997-275
05-08-2000 05:28:25 277 1998-515.html
05-08-2000 05:36:43 325 2000-436.html
12-23-2000 06:47:11 <DIR> checkpwd
07-04-2002 17:47:46 <DIR> bib
02-29-2000 02:01:47 <DIR> 1999-541
04-28-2001 03:02:35 <DIR> 2001-260
04-17-2002 18:27:55 <DIR> 2002-501
06-01-2001 20:56:17 <DIR> 1995-514
02-29-2000 02:01:45 <DIR> 1999-180
06-01-2001 02:44:23 475 2000-515.html
02-29-2000 02:01:47 <DIR> 1998-515
02-29-2000 02:01:47 <DIR> 2000-515
05-12-2000 07:46:13 <DIR> zpfft
10-27-2001 13:00:14 <DIR> psibound
02-29-2000 02:01:51 398 anonftpd.html
02-26-2002 02:34:22 <DIR> nistp224
08-10-2001 07:27:20 <DIR> cdb
04-30-2002 23:58:27 <DIR> talks
12-23-2000 21:34:35 <DIR> ftpparse
01-11-2000 05:50:10 <DIR> clockspeed
02-29-2000 02:01:52 1350 clockspeed.html
02-06-2002 20:52:34 <DIR> slashdoc
06-07-2002 16:00:40 <DIR> export
07-17-2001 01:48:36 <DIR> daemontools
07-12-2001 21:17:08 2187 daemontools.html
08-30-2001 20:51:16 175 data.html
12-28-2001 01:32:37 1901 crypto.html
11-10-2001 23:45:23 739 arith.html
11-30-2001 03:48:09 2733 cdb.html
11-10-2001 23:51:02 344 floatasm.html
12-21-2000 21:25:56 <DIR> djbfft
02-29-2000 02:01:52 1319 djbfft.html
07-09-2000 17:16:47 <DIR> dnscache
07-03-2000 22:57:44 4016 dnscache.html
07-10-2001 03:16:22 <DIR> docs
02-29-2000 02:01:52 293 docs.html
02-29-2000 02:01:52 952 dot-forward.html
01-11-2000 05:50:11 <DIR> etc-mta
02-29-2000 02:01:52 3174 etc-mta.html
02-29-2000 02:01:52 2990 ezmlm.html
12-21-2000 21:25:56 <DIR> ftp
02-29-2000 02:01:49 <DIR> im
02-29-2000 02:01:52 1823 fastforward.html
02-29-2000 02:01:52 1707 ftp.html
02-12-2001 02:17:10 <DIR> freebugtraq
06-02-2002 01:39:25 <DIR> hardware
06-01-2001 02:57:35 610 1995-514.html
12-21-2000 21:25:57 <DIR> hash127
05-19-2000 02:29:02 4835 hash127.html
02-29-2000 02:01:52 1515 im.html
02-12-2002 20:40:06 <DIR> immhf
04-25-2001 21:48:49 2902 immhf.html
01-25-2001 05:58:36 <DIR> lib
06-05-2001 23:45:50 <DIR> libtai
07-10-2000 18:54:16 2120 libtai.html
11-27-2001 17:52:40 14460 qmail.html
08-30-2001 20:51:23 2153 mail.html
02-29-2000 02:01:52 108 maildir.html
02-29-2000 02:01:50 <DIR> maildisasters
02-29-2000 02:01:52 873 maildisasters.html
02-29-2000 02:01:52 1688 mess822.html
02-25-2002 02:47:27 <DIR> mirror
12-26-2001 21:02:32 581 ntheory.html
06-03-2002 15:36:27 <DIR> papers
05-21-2002 16:50:15 <DIR> postpropter
01-11-2000 05:50:12 <DIR> postings
01-11-2000 05:50:12 <DIR> primegen
08-30-2001 20:51:33 415 precompiled.html
02-29-2000 02:01:52 1121 primegen.html
05-05-2002 01:37:55 <DIR> proto
02-29-2000 02:01:52 452 proto.html
07-09-2001 22:17:53 <DIR> publicfile
11-30-2001 03:59:17 5651 publicfile.html
08-30-2001 20:51:37 409 qlist.html
04-02-2002 05:25:45 <DIR> qmail
02-02-2002 05:52:28 7549 lists.html
02-29-2000 02:01:52 1166 qmailanalog.html
08-30-2001 20:51:39 1434 qmsmac.html
03-13-2000 05:38:06 465 rblsmtpd.html
02-29-2000 02:01:52 1225 rights.html
01-11-2000 05:50:13 <DIR> sarcasm
09-14-2001 02:34:14 1058 unix.html
02-29-2000 02:01:53 1568 serialmail.html
10-02-2000 19:13:44 840 sigs.html
05-21-2002 16:50:08 <DIR> smtp
04-26-2001 15:58:47 1723 smtp.html
12-21-2000 21:26:03 <DIR> software
02-29-2000 02:01:53 3671 softwarelaw.html
01-11-2000 05:50:13 <DIR> sortedsums
02-29-2000 02:01:53 2735 sortedsums.html
02-26-2002 02:06:38 <DIR> speed
07-03-2000 22:40:17 572 surveydns.html
10-04-2001 00:59:31 <DIR> surveys
10-04-2001 00:46:36 3503 surveys.html
11-10-2000 05:58:08 <DIR> syncookies
02-01-2002 02:03:37 7918 syncookies.html
08-30-2001 20:51:43 1115 tcpcontrol.html
03-18-2002 12:08:27 589 tcpip.html
02-29-2000 02:01:53 906 thoughts.html
07-30-2001 14:18:09 <DIR> threecubes
09-14-2001 01:25:58 525 time.html
05-22-2002 04:21:05 <DIR> ucspi-tcp
01-24-2002 23:41:31 4086 ucspi-tcp.html
08-30-2001 20:51:50 732 web.html
07-04-2002 17:47:48 <DIR> www
09-14-2001 01:26:08 2717 y2k.html
02-08-2001 09:28:09 <DIR> dnsroot
04-23-2001 06:01:37 2745 2001-260.html
02-29-2000 02:01:53 291 zeroseek.html
02-29-2000 02:01:53 <DIR> zmodexp
05-19-2000 02:29:07 1513 zmodexp.html
08-01-2000 01:08:54 <DIR> zmult
08-01-2000 01:24:53 669 zmult.html
12-23-2000 06:19:47 1097 checkpwd.html
12-31-2001 18:47:16 2077 focus.html
07-19-2000 23:11:31 3842 im2000.html
07-04-2002 17:47:43 <DIR> slashcommand
07-04-2002 17:47:44 <DIR> slashpackage
07-15-2001 22:24:39 602 slashpackage.html
05-03-2002 00:15:33 23874 talks.html
05-08-2000 05:24:59 566 1997-275.html
05-08-2000 05:31:04 313 1998-100.html
02-29-2000 02:01:47 <DIR> 2000-436
05-08-2000 05:37:26 660 1997-494.html
05-08-2000 05:40:41 730 1999-541.html
09-13-2000 07:08:58 2367 psibound.html
06-22-2000 02:09:00 1866 smallfactors.html
06-09-2000 01:35:19 <DIR> smallfactors
07-01-2000 22:55:19 <DIR> conferences
05-22-2002 05:52:29 <DIR> djbdns
07-17-2001 04:00:58 2712 djbdns.html
07-17-2000 01:15:39 216 zpfft.html
08-20-2000 18:56:37 <DIR> fastnewton
08-20-2000 18:57:31 333 fastnewton.html
09-01-2000 18:33:51 <DIR> patents
12-23-2000 21:40:17 1748 ftpparse.html
11-10-2001 23:57:50 3261 slash.html
01-17-2001 17:50:10 2663 slashdoc.html
03-18-2002 09:04:35 2344 dnsroot.html
12-26-2001 21:03:00 3238 software.html
09-14-2001 02:34:33 3481 compatibility.html
05-01-2002 03:30:34 5525 distributors.html
02-25-2001 03:49:03 212 freebugtraq.html
03-31-2002 03:59:50 806 hardware.html
03-31-2001 07:15:18 121 securesoftware.html
05-22-2002 02:39:56 10950 conferences.html
11-30-2001 17:51:50 <DIR> 2001-275
05-29-2001 00:15:07 459 rwb100.html
10-03-2001 05:40:42 863 nistp224.html
07-30-2001 14:15:46 778 threecubes.html
06-03-2002 15:32:16 27125 papers.html
11-21-2001 02:51:09 379 bib.html
11-30-2001 17:41:33 3791 2001-275.html
10-02-2001 18:08:59 169 dh224.html
03-16-2002 06:05:36 1126 courses.html
09-02-2001 22:22:28 355 slashcommand.html
03-27-2002 21:20:22 1199 patents.html
11-08-2001 19:27:15 640 mailcopyright.html
06-01-2002 23:27:50 1257 djb.html
01-07-2002 06:12:34 301 2002-501.html
05-01-2002 02:41:45 1655 export.html
03-24-2002 10:40:59 6446 index.html
03-16-2002 06:09:08 570 2005-590.html
03-16-2002 06:05:49 800 2004-494.html
06-01-2002 23:28:59 4459 positions.html
06-01-2002 22:55:31 466 contact.html

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

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/ArrayUtils.h"
#include "nsPrintfCString.h"
#include <stdio.h>
#include "ParseFTPList.h"
PRTime gTestTime = 0;
// Pretend this is the current time for the purpose of running the
// test. The day and year matter because they are used to figure out a
// default value when no year is specified. Tests will fail if this is
// changed too much.
const char* kDefaultTestTime = "01-Aug-2002 00:00:00 GMT";
static PRTime
TestTime()
{
return gTestTime;
}
static void
ParseFTPLine(char* inputLine,
FILE* resultFile,
list_state* state)
{
struct list_result result;
int rc = ParseFTPList(inputLine, state, &result, PR_GMTParameters, TestTime);
if (rc == '?' || rc == '"') {
// Ignore junk and comments.
return;
}
if (!resultFile) {
// No result file was passed in, so there's nothing to check.
return;
}
char resultLine[512];
ASSERT_NE(fgets(resultLine, sizeof(resultLine), resultFile), nullptr);
nsPrintfCString parsed("%02u-%02u-%04u %02u:%02u:%02u %20s %.*s%s%.*s\n",
(result.fe_time.tm_mday ? (result.fe_time.tm_month + 1) : 0),
result.fe_time.tm_mday,
(result.fe_time.tm_mday ? result.fe_time.tm_year : 0),
result.fe_time.tm_hour,
result.fe_time.tm_min,
result.fe_time.tm_sec,
(rc == 'd' ? "<DIR> " :
(rc == 'l' ? "<JUNCTION> " : result.fe_size)),
(int)result.fe_fnlen, result.fe_fname,
((rc == 'l' && result.fe_lnlen) ? " -> " : ""),
(int)((rc == 'l' && result.fe_lnlen) ? result.fe_lnlen : 0),
((rc == 'l' && result.fe_lnlen) ? result.fe_lname : ""));
ASSERT_STREQ(parsed.get(), resultLine);
}
FILE*
OpenResultFile(const char* resultFileName)
{
if (!resultFileName) {
return nullptr;
}
FILE* resultFile = fopen(resultFileName, "r");
EXPECT_NE(resultFile, nullptr);
// Ignore anything in the expected result file before and including the first blank line.
char resultLine[512];
while (fgets(resultLine, sizeof(resultLine), resultFile)) {
size_t lineLen = strlen(resultLine);
EXPECT_LT(lineLen, sizeof(resultLine) - 1);
if (lineLen > 0 && resultLine[lineLen - 1] == '\n') {
lineLen--;
}
if (lineLen == 0) {
break;
}
}
// There must be a blank line somewhere in the result file.
EXPECT_EQ(strcmp(resultLine, "\n"), 0);
return resultFile;
}
void
ParseFTPFile(const char* inputFileName,
const char* resultFileName)
{
printf("Checking %s\n", inputFileName);
FILE* inFile = fopen(inputFileName, "r");
ASSERT_NE(inFile, nullptr);
FILE* resultFile = OpenResultFile(resultFileName);
char inputLine[512];
struct list_state state;
memset(&state, 0, sizeof(state));
while (fgets(inputLine, sizeof(inputLine), inFile)) {
size_t lineLen = strlen(inputLine);
EXPECT_LT(lineLen, sizeof(inputLine) - 1);
if (lineLen > 0 && inputLine[lineLen - 1] == '\n') {
lineLen--;
}
ParseFTPLine(inputLine, resultFile, &state);
}
// Make sure there are no extra lines in the result file.
if (resultFile) {
char resultLine[512];
EXPECT_EQ(fgets(resultLine, sizeof(resultLine), resultFile), nullptr) <<
"There should not be more lines in the expected results file than in the parser output.";
fclose(resultFile);
}
fclose(inFile);
}
static const char* testFiles[] = {
"3-guess",
"C-VMold",
"C-zVM",
"D-WinNT",
"E-EPLF",
"O-guess",
"R-dls",
"U-HellSoft",
"U-hethmon",
"U-murksw",
"U-ncFTPd",
"U-NetPresenz",
"U-NetWare",
"U-nogid",
"U-no_ug",
"U-Novonyx",
"U-proftpd",
"U-Surge",
"U-WarFTPd",
"U-WebStar",
"U-WinNT",
"U-wu",
"V-MultiNet",
"V-VMS-mix",
};
TEST(ParseFTPTest, Check)
{
PRStatus result = PR_ParseTimeString(kDefaultTestTime, true, &gTestTime);
ASSERT_EQ(PR_SUCCESS, result);
char inputFileName[200];
char resultFileName[200];
for (size_t test = 0; test < mozilla::ArrayLength(testFiles); ++test) {
snprintf(inputFileName, mozilla::ArrayLength(inputFileName), "%s.in", testFiles[test]);
snprintf(resultFileName, mozilla::ArrayLength(inputFileName), "%s.out", testFiles[test]);
ParseFTPFile(inputFileName, resultFileName);
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше