зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
678f85fb2d
|
@ -1568,9 +1568,6 @@ pref("browser.contentblocking.database.enabled", true);
|
|||
|
||||
pref("dom.storage_access.enabled", true);
|
||||
|
||||
pref("dom.storage_access.auto_grants", true);
|
||||
pref("dom.storage_access.max_concurrent_auto_grants", 5);
|
||||
|
||||
pref("browser.contentblocking.control-center.ui.showBlockedLabels", true);
|
||||
pref("browser.contentblocking.control-center.ui.showAllowedLabels", false);
|
||||
|
||||
|
|
|
@ -291,7 +291,7 @@ add_task(async function() {
|
|||
});
|
||||
}
|
||||
|
||||
let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
|
||||
let tmpPath = expandWhitelistPath("TmpD:").toLowerCase();
|
||||
let shouldPass = true;
|
||||
for (let procName in processes) {
|
||||
let whitelist = processes[procName];
|
||||
|
@ -345,15 +345,22 @@ add_task(async function() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!WIN) {
|
||||
if (filename == "/dev/urandom") {
|
||||
continue;
|
||||
}
|
||||
if (!WIN && filename == "/dev/urandom") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore I/O due to IPC. This doesn't really touch the disk.
|
||||
if (filename.startsWith(tmpPath + "/org.chromium.")) {
|
||||
continue;
|
||||
}
|
||||
// /dev/shm is always tmpfs (a memory filesystem); this isn't
|
||||
// really I/O any more than mmap/munmap are.
|
||||
if (LINUX && filename.startsWith("/dev/shm/")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Shared memory uses temporary files on MacOS <= 10.11 to avoid
|
||||
// a kernel security bug that will never be patched (see
|
||||
// https://crbug.com/project-zero/1671 for details). This can
|
||||
// be removed when we no longer support those OS versions.
|
||||
if (MAC && filename.startsWith(tmpPath + "/org.mozilla.ipc.")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected = false;
|
||||
|
|
|
@ -884,7 +884,7 @@ add_task(async function() {
|
|||
});
|
||||
}
|
||||
|
||||
let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
|
||||
let tmpPath = expandWhitelistPath("TmpD:").toLowerCase();
|
||||
let shouldPass = true;
|
||||
for (let phase in phases) {
|
||||
let whitelist = startupPhases[phase];
|
||||
|
@ -919,15 +919,22 @@ add_task(async function() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!WIN) {
|
||||
if (filename == "/dev/urandom") {
|
||||
continue;
|
||||
}
|
||||
if (!WIN && filename == "/dev/urandom") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore I/O due to IPC. This doesn't really touch the disk.
|
||||
if (filename.startsWith(tmpPath + "/org.chromium.")) {
|
||||
continue;
|
||||
}
|
||||
// /dev/shm is always tmpfs (a memory filesystem); this isn't
|
||||
// really I/O any more than mmap/munmap are.
|
||||
if (LINUX && filename.startsWith("/dev/shm/")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Shared memory uses temporary files on MacOS <= 10.11 to avoid
|
||||
// a kernel security bug that will never be patched (see
|
||||
// https://crbug.com/project-zero/1671 for details). This can
|
||||
// be removed when we no longer support those OS versions.
|
||||
if (MAC && filename.startsWith(tmpPath + "/org.mozilla.ipc.")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected = false;
|
||||
|
|
|
@ -35,6 +35,10 @@ class UrlbarView {
|
|||
this.document = this.panel.ownerDocument;
|
||||
this.window = this.document.defaultView;
|
||||
|
||||
if (this.input.megabar) {
|
||||
this.panel.classList.add("megabar");
|
||||
}
|
||||
|
||||
this._mainContainer = this.panel.querySelector(".urlbarView-body-inner");
|
||||
this._rows = this.panel.querySelector("#urlbarView-results");
|
||||
|
||||
|
@ -423,66 +427,76 @@ class UrlbarView {
|
|||
|
||||
this.panel.removeAttribute("actionoverride");
|
||||
|
||||
// Make the panel span the width of the window.
|
||||
let px = number => number.toFixed(2) + "px";
|
||||
let documentRect = this._getBoundsWithoutFlushing(
|
||||
this.document.documentElement
|
||||
);
|
||||
let width = documentRect.right - documentRect.left;
|
||||
this.panel.setAttribute("width", width);
|
||||
this._mainContainer.style.maxWidth = px(width);
|
||||
|
||||
// Keep the popup items' site icons aligned with the input's identity
|
||||
// icon if it's not too far from the edge of the window. We define
|
||||
// "too far" as "more than 30% of the window's width AND more than
|
||||
// 250px".
|
||||
let boundToCheck = this.window.RTL_UI ? "right" : "left";
|
||||
let inputRect = this._getBoundsWithoutFlushing(this.input.textbox);
|
||||
let startOffset = Math.abs(
|
||||
inputRect[boundToCheck] - documentRect[boundToCheck]
|
||||
);
|
||||
let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
|
||||
if (alignSiteIcons) {
|
||||
// Calculate the end margin if we have a start margin.
|
||||
let boundToCheckEnd = this.window.RTL_UI ? "left" : "right";
|
||||
let endOffset = Math.abs(
|
||||
inputRect[boundToCheckEnd] - documentRect[boundToCheckEnd]
|
||||
);
|
||||
if (endOffset > startOffset * 2) {
|
||||
// Provide more space when aligning would result in an unbalanced
|
||||
// margin. This allows the location bar to be moved to the start
|
||||
// of the navigation toolbar to reclaim space for results.
|
||||
endOffset = startOffset;
|
||||
}
|
||||
|
||||
// We need to align with the tracking protection icon if the
|
||||
// 'pageproxystate' is valid since the tracking protection icon would be
|
||||
// at the first position instead of the identity icon in this case.
|
||||
let alignIcon;
|
||||
if (this.input.getAttribute("pageproxystate") === "valid") {
|
||||
alignIcon = this.document.getElementById(
|
||||
"tracking-protection-icon-box"
|
||||
);
|
||||
} else {
|
||||
alignIcon = this.document.getElementById("identity-icon");
|
||||
}
|
||||
let alignRect = this._getBoundsWithoutFlushing(alignIcon);
|
||||
let start = this.window.RTL_UI
|
||||
? documentRect.right - alignRect.right
|
||||
: alignRect.left;
|
||||
|
||||
this.panel.style.setProperty("--item-padding-start", px(start));
|
||||
this.panel.style.setProperty("--item-padding-end", px(endOffset));
|
||||
let px = number => number.toFixed(2) + "px";
|
||||
let width;
|
||||
if (this.input.megabar) {
|
||||
// Make the panel span the width of the textbox.
|
||||
width = inputRect.width;
|
||||
} else {
|
||||
this.panel.style.removeProperty("--item-padding-start");
|
||||
this.panel.style.removeProperty("--item-padding-end");
|
||||
// Make the panel span the width of the window.
|
||||
let documentRect = this._getBoundsWithoutFlushing(
|
||||
this.document.documentElement
|
||||
);
|
||||
width = documentRect.right - documentRect.left;
|
||||
|
||||
// Keep the popup items' site icons aligned with the input's identity
|
||||
// icon if it's not too far from the edge of the window. We define
|
||||
// "too far" as "more than 30% of the window's width AND more than
|
||||
// 250px".
|
||||
let boundToCheck = this.window.RTL_UI ? "right" : "left";
|
||||
let startOffset = Math.abs(
|
||||
inputRect[boundToCheck] - documentRect[boundToCheck]
|
||||
);
|
||||
let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
|
||||
|
||||
if (alignSiteIcons) {
|
||||
// Calculate the end margin if we have a start margin.
|
||||
let boundToCheckEnd = this.window.RTL_UI ? "left" : "right";
|
||||
let endOffset = Math.abs(
|
||||
inputRect[boundToCheckEnd] - documentRect[boundToCheckEnd]
|
||||
);
|
||||
if (endOffset > startOffset * 2) {
|
||||
// Provide more space when aligning would result in an unbalanced
|
||||
// margin. This allows the location bar to be moved to the start
|
||||
// of the navigation toolbar to reclaim space for results.
|
||||
endOffset = startOffset;
|
||||
}
|
||||
|
||||
// We need to align with the tracking protection icon if the
|
||||
// 'pageproxystate' is valid since the tracking protection icon would be
|
||||
// at the first position instead of the identity icon in this case.
|
||||
let alignIcon;
|
||||
if (this.input.getAttribute("pageproxystate") === "valid") {
|
||||
alignIcon = this.document.getElementById(
|
||||
"tracking-protection-icon-box"
|
||||
);
|
||||
} else {
|
||||
alignIcon = this.document.getElementById("identity-icon");
|
||||
}
|
||||
let alignRect = this._getBoundsWithoutFlushing(alignIcon);
|
||||
let start = this.window.RTL_UI
|
||||
? documentRect.right - alignRect.right
|
||||
: alignRect.left;
|
||||
|
||||
this.panel.style.setProperty("--item-padding-start", px(start));
|
||||
this.panel.style.setProperty("--item-padding-end", px(endOffset));
|
||||
} else {
|
||||
this.panel.style.removeProperty("--item-padding-start");
|
||||
this.panel.style.removeProperty("--item-padding-end");
|
||||
}
|
||||
}
|
||||
|
||||
// Align the panel with the input's parent toolbar.
|
||||
let toolbarRect = this._getBoundsWithoutFlushing(
|
||||
this.input.textbox.closest("toolbar")
|
||||
);
|
||||
this.panel.style.top = px(toolbarRect.bottom);
|
||||
this.panel.style.width = px(width);
|
||||
this._mainContainer.style.maxWidth = px(width);
|
||||
|
||||
// Align the panel with the input or the input's parent toolbar, depending
|
||||
// on megabar status.
|
||||
let alignmentRect = this.input.megabar
|
||||
? this._getBoundsWithoutFlushing(this.input.textbox)
|
||||
: this._getBoundsWithoutFlushing(this.input.textbox.closest("toolbar"));
|
||||
this.panel.style.top = px(alignmentRect.bottom);
|
||||
|
||||
this.panel.removeAttribute("hidden");
|
||||
this.input.inputField.setAttribute("aria-expanded", "true");
|
||||
|
|
|
@ -5,7 +5,5 @@
|
|||
- Transliterated.
|
||||
- Translated. -->
|
||||
|
||||
<!ENTITY sendShortName "Send">
|
||||
<!ENTITY sendFullName "Firefox Send">
|
||||
<!ENTITY monitorShortName "Monitor">
|
||||
<!ENTITY monitorFullName "Firefox Monitor">
|
||||
|
|
|
@ -112,10 +112,6 @@ convenience of Safari and Chrome users on macOS. See bug 1398988. -->
|
|||
|
||||
<!-- LOCALIZATION NOTE (fxa.service) Used to define the different FxA services
|
||||
for the Firefox Account toolbar menu screen. -->
|
||||
<!ENTITY fxa.service.send.description "Upload and share files with end-to-end encryption plus a link that automatically expires.">
|
||||
<!ENTITY fxa.service.send.launch "Launch &sendShortName;">
|
||||
<!ENTITY fxa.service.monitor.description "Check if your email has appeared in a data breach and get alerted if it appears in a new breach.">
|
||||
<!ENTITY fxa.service.monitor.launch "Launch &monitorShortName;">
|
||||
<!ENTITY fxa.service.sendTab.description "Send a tab instantly to any device you’re signed in on.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (fullscreenWarning.beforeDomain.label,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"BrowserUsageTelemetry",
|
||||
"getUniqueDomainsVisitedInPast24Hours",
|
||||
"URICountListener",
|
||||
"URLBAR_SELECTED_RESULT_TYPES",
|
||||
"URLBAR_SELECTED_RESULT_METHODS",
|
||||
|
@ -890,3 +891,8 @@ let BrowserUsageTelemetry = {
|
|||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Used by nsIBrowserUsage
|
||||
function getUniqueDomainsVisitedInPast24Hours() {
|
||||
return URICountListener.uniqueDomainsVisitedInPast24Hours;
|
||||
}
|
||||
|
|
|
@ -76,11 +76,6 @@ ChromeUtils.defineModuleGetter(
|
|||
"PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"URICountListener",
|
||||
"resource:///modules/BrowserUsageTelemetry.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"PermissionUITelemetry",
|
||||
|
@ -1154,17 +1149,6 @@ PermissionUI.MIDIPermissionPrompt = MIDIPermissionPrompt;
|
|||
|
||||
function StorageAccessPermissionPrompt(request) {
|
||||
this.request = request;
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
"_autoGrants",
|
||||
"dom.storage_access.auto_grants"
|
||||
);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
"_maxConcurrentAutoGrants",
|
||||
"dom.storage_access.max_concurrent_auto_grants"
|
||||
);
|
||||
}
|
||||
|
||||
StorageAccessPermissionPrompt.prototype = {
|
||||
|
@ -1248,10 +1232,6 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
get promptActions() {
|
||||
let self = this;
|
||||
|
||||
let storageAccessHistogram = Services.telemetry.getHistogramById(
|
||||
"STORAGE_ACCESS_API_UI"
|
||||
);
|
||||
|
||||
return [
|
||||
{
|
||||
label: gBrowserBundle.GetStringFromName(
|
||||
|
@ -1262,7 +1242,6 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
),
|
||||
action: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
callback(state) {
|
||||
storageAccessHistogram.add("Deny");
|
||||
self.cancel();
|
||||
},
|
||||
},
|
||||
|
@ -1273,7 +1252,6 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
),
|
||||
action: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
callback(state) {
|
||||
storageAccessHistogram.add("Allow");
|
||||
self.allow({ "storage-access": "allow" });
|
||||
},
|
||||
},
|
||||
|
@ -1286,7 +1264,6 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
),
|
||||
action: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
callback(state) {
|
||||
storageAccessHistogram.add("AllowOnAnySite");
|
||||
self.allow({ "storage-access": "allow-on-any-site" });
|
||||
},
|
||||
},
|
||||
|
@ -1296,60 +1273,6 @@ StorageAccessPermissionPrompt.prototype = {
|
|||
get topLevelPrincipal() {
|
||||
return this.request.topLevelPrincipal;
|
||||
},
|
||||
|
||||
get maxConcurrentAutomaticGrants() {
|
||||
// one percent of the number of top-levels origins visited in the current
|
||||
// session (but not to exceed 24 hours), or the value of the
|
||||
// dom.storage_access.max_concurrent_auto_grants preference, whichever is
|
||||
// higher.
|
||||
return Math.max(
|
||||
Math.max(
|
||||
Math.floor(URICountListener.uniqueDomainsVisitedInPast24Hours / 100),
|
||||
this._maxConcurrentAutoGrants
|
||||
),
|
||||
0
|
||||
);
|
||||
},
|
||||
|
||||
getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) {
|
||||
let prefix = `3rdPartyStorage^${thirdPartyOrigin}`;
|
||||
let perms = Services.perms.getAllWithTypePrefix(prefix);
|
||||
let origins = new Set();
|
||||
while (perms.length) {
|
||||
let perm = perms.shift();
|
||||
// Let's make sure that we're not looking at a permission for
|
||||
// https://exampletracker.company when we mean to look for the
|
||||
// permisison for https://exampletracker.com!
|
||||
if (perm.type != prefix && !perm.type.startsWith(`${prefix}^`)) {
|
||||
continue;
|
||||
}
|
||||
origins.add(perm.principal.origin);
|
||||
}
|
||||
return origins.size;
|
||||
},
|
||||
|
||||
onBeforeShow() {
|
||||
let storageAccessHistogram = Services.telemetry.getHistogramById(
|
||||
"STORAGE_ACCESS_API_UI"
|
||||
);
|
||||
|
||||
storageAccessHistogram.add("Request");
|
||||
|
||||
let thirdPartyOrigin = this.request.principal.origin;
|
||||
if (
|
||||
this._autoGrants &&
|
||||
this.getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) <
|
||||
this.maxConcurrentAutomaticGrants
|
||||
) {
|
||||
// Automatically accept the prompt
|
||||
this.allow({ "storage-access": "allow-auto-grant" });
|
||||
|
||||
storageAccessHistogram.add("AllowAutomatically");
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
PermissionUI.StorageAccessPermissionPrompt = StorageAccessPermissionPrompt;
|
||||
|
|
|
@ -85,6 +85,11 @@ treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
|
|||
color: HighlightText;
|
||||
}
|
||||
|
||||
/* Remove tiny spacing in separators appearing after the twisty column */
|
||||
treechildren::-moz-tree-twisty(title, separator) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(cutting) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
|
|
@ -29,11 +29,18 @@
|
|||
#urlbar-results {
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--autocomplete-popup-background);
|
||||
color: var(--autocomplete-popup-color);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
#urlbar-results.megabar {
|
||||
margin-inline-start: 5px;
|
||||
}
|
||||
|
||||
#urlbar-results:not(.megabar) {
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-block: 1px solid var(--chrome-content-separator-color);
|
||||
}
|
||||
|
||||
|
@ -251,12 +258,15 @@
|
|||
/* Search one-offs. */
|
||||
#urlbar-results > .search-one-offs {
|
||||
-moz-box-orient: horizontal;
|
||||
padding-inline-start: var(--item-padding-start, 5px);
|
||||
padding-inline-end: var(--item-padding-end, 5px);
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
#urlbar-results:not(.megabar) > .search-one-offs {
|
||||
padding-inline-start: var(--item-padding-start, 5px);
|
||||
padding-inline-end: var(--item-padding-end, 5px);
|
||||
}
|
||||
|
||||
#urlbar-results .search-panel-one-offs {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
|
|
13
build.gradle
13
build.gradle
|
@ -209,16 +209,6 @@ def createMachStagePackageTask(name) {
|
|||
|
||||
dependsOn rootProject.machBuildFaster
|
||||
|
||||
// We'd prefer to take these from the :omnijar project directly, but
|
||||
// it's awkward to reach across projects at evaluation time, so we
|
||||
// duplicate the list here.
|
||||
inputs.dir "${topsrcdir}/mobile/android/chrome"
|
||||
inputs.dir "${topsrcdir}/mobile/android/components"
|
||||
inputs.dir "${topsrcdir}/mobile/android/locales"
|
||||
inputs.dir "${topsrcdir}/mobile/android/modules"
|
||||
inputs.dir "${topsrcdir}/mobile/android/themes"
|
||||
inputs.dir "${topsrcdir}/toolkit"
|
||||
|
||||
workingDir "${topobjdir}"
|
||||
|
||||
// We'd prefer this to be a `mach` invocation, but `mach build
|
||||
|
@ -231,6 +221,9 @@ def createMachStagePackageTask(name) {
|
|||
outputs.file "${topobjdir}/dist/fennec/assets/${mozconfig.substs.ANDROID_CPU_ARCH}/libxul.so"
|
||||
outputs.file "${topobjdir}/dist/fennec/lib/${mozconfig.substs.ANDROID_CPU_ARCH}/libmozglue.so"
|
||||
|
||||
// Force running `stage-package`.
|
||||
outputs.upToDateWhen { false }
|
||||
|
||||
// `path` is like `:machStagePackage`.
|
||||
standardOutput = new TaggedLogOutputStream("${path}>", logger)
|
||||
errorOutput = standardOutput
|
||||
|
|
|
@ -160,9 +160,14 @@ DebuggerPanel.prototype = {
|
|||
return this._actions.selectSourceURL(cx, url, { line, column });
|
||||
},
|
||||
|
||||
selectSource(sourceId, line, column) {
|
||||
async selectSource(sourceId, line, column) {
|
||||
const cx = this._selectors.getContext(this._getState());
|
||||
return this._actions.selectSource(cx, sourceId, { line, column });
|
||||
const location = { sourceId, line, column };
|
||||
|
||||
await this._actions.selectSource(cx, sourceId, location);
|
||||
if (this._selectors.hasLogpoint(this._getState(), location)) {
|
||||
this._actions.openConditionalPanel(location, true);
|
||||
}
|
||||
},
|
||||
|
||||
canLoadSource(sourceId) {
|
||||
|
|
|
@ -184,7 +184,8 @@ function removeXHRBreakpoint(path: string, method: string) {
|
|||
function locationKey(location: BreakpointLocation) {
|
||||
const { sourceUrl, line, column } = location;
|
||||
const sourceId = location.sourceId || "";
|
||||
return `${(sourceUrl: any)}:${(sourceId: any)}:${line}:${(column: any)}`;
|
||||
// $FlowIgnore
|
||||
return `${sourceUrl}:${sourceId}:${line}:${column}`;
|
||||
}
|
||||
|
||||
function detachWorkers() {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { toEditorLine } from "../../utils/editor";
|
|||
import actions from "../../actions";
|
||||
|
||||
import {
|
||||
getBreakpointForLocation,
|
||||
getBreakpoint,
|
||||
getConditionalPanelLocation,
|
||||
getLogPointStatus,
|
||||
getContext,
|
||||
|
@ -231,7 +231,7 @@ const mapStateToProps = state => {
|
|||
const location = getConditionalPanelLocation(state);
|
||||
return {
|
||||
cx: getContext(state),
|
||||
breakpoint: getBreakpointForLocation(state, location),
|
||||
breakpoint: getBreakpoint(state, location),
|
||||
location,
|
||||
log: getLogPointStatus(state),
|
||||
};
|
||||
|
|
|
@ -52,6 +52,10 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.popover .preview-popup .object-node {
|
||||
padding-inline-start: 0px;
|
||||
}
|
||||
|
||||
.preview-token:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
|
|
@ -84,10 +84,14 @@ export class Popup extends Component<Props> {
|
|||
if (!editorRef) {
|
||||
return "auto";
|
||||
}
|
||||
return (
|
||||
editorRef.getBoundingClientRect().height +
|
||||
editorRef.getBoundingClientRect().top
|
||||
);
|
||||
|
||||
const { height, top } = editorRef.getBoundingClientRect();
|
||||
const maxHeight = height + top;
|
||||
if (maxHeight < 250) {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
return 250;
|
||||
};
|
||||
|
||||
renderFunctionPreview() {
|
||||
|
|
|
@ -141,15 +141,15 @@ class SearchBar extends Component<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
closeSearch = (e: SyntheticEvent<HTMLElement>) => {
|
||||
const { cx, closeFileSearch, editor, searchOn } = this.props;
|
||||
closeSearch = (e: SyntheticKeyboardEvent<HTMLElement>) => {
|
||||
const { cx, closeFileSearch, editor, searchOn, query } = this.props;
|
||||
this.clearSearch();
|
||||
if (editor && searchOn) {
|
||||
this.clearSearch();
|
||||
closeFileSearch(cx, editor);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
this.setState({ query: "", inputFocused: false });
|
||||
this.setState({ query, inputFocused: false });
|
||||
};
|
||||
|
||||
toggleSearch = (e: SyntheticKeyboardEvent<HTMLElement>) => {
|
||||
|
@ -164,7 +164,7 @@ class SearchBar extends Component<Props, State> {
|
|||
setActiveSearch("file");
|
||||
}
|
||||
|
||||
if (searchOn && editor) {
|
||||
if (this.props.searchOn && editor) {
|
||||
const query = editor.codeMirror.getSelection() || this.state.query;
|
||||
|
||||
if (query !== "") {
|
||||
|
|
|
@ -335,12 +335,19 @@ class Editor extends PureComponent<Props, State> {
|
|||
breakpointActions,
|
||||
editorActions,
|
||||
isPaused,
|
||||
conditionalPanelLocation,
|
||||
closeConditionalPanel,
|
||||
} = this.props;
|
||||
const { editor } = this.state;
|
||||
if (!selectedSourceWithContent || !editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only allow one conditionalPanel location.
|
||||
if (conditionalPanelLocation) {
|
||||
closeConditionalPanel();
|
||||
}
|
||||
|
||||
const target: Element = (event.target: any);
|
||||
const { id: sourceId } = selectedSourceWithContent.source;
|
||||
const line = lineAtHeight(editor, sourceId, event);
|
||||
|
|
|
@ -178,8 +178,12 @@ export function getBreakpointCount(state: OuterState): number {
|
|||
|
||||
export function getBreakpoint(
|
||||
state: OuterState,
|
||||
location: SourceLocation
|
||||
location: ?SourceLocation
|
||||
): ?Breakpoint {
|
||||
if (!location) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const breakpoints = getBreakpointsMap(state);
|
||||
return breakpoints[makeBreakpointId(location)];
|
||||
}
|
||||
|
@ -208,9 +212,9 @@ export function getBreakpointsForSource(
|
|||
|
||||
export function getBreakpointForLocation(
|
||||
state: OuterState,
|
||||
location: SourceLocation | null
|
||||
location: ?SourceLocation
|
||||
): ?Breakpoint {
|
||||
if (!location || !location.sourceId) {
|
||||
if (!location) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -226,4 +230,12 @@ export function getHiddenBreakpoint(state: OuterState): ?Breakpoint {
|
|||
return breakpoints.find(bp => bp.options.hidden);
|
||||
}
|
||||
|
||||
export function hasLogpoint(
|
||||
state: OuterState,
|
||||
location: ?SourceLocation
|
||||
): ?string {
|
||||
const breakpoint = getBreakpoint(state, location);
|
||||
return breakpoint && breakpoint.options.logValue;
|
||||
}
|
||||
|
||||
export default update;
|
||||
|
|
|
@ -108,6 +108,8 @@ skip-if = os == "win"
|
|||
skip-if = true
|
||||
[browser_dbg-pause-points.js]
|
||||
[browser_dbg-scopes-mutations.js]
|
||||
[browser_dbg-search-file-retains-query.js]
|
||||
skip-if = os == "win" # Bug 1393121
|
||||
[browser_dbg-search-file.js]
|
||||
skip-if = os == "win" # Bug 1393121
|
||||
[browser_dbg-search-file-paused.js]
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* 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/>. */
|
||||
|
||||
// Tests the search bar retains previous query on re-opening.
|
||||
|
||||
function waitForSearchState(dbg) {
|
||||
return waitForState(dbg, () => getCM(dbg).state.search);
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
const dbg = await initDebugger("doc-scripts.html", "simple1.js");
|
||||
const {
|
||||
selectors: { getActiveSearch, getFileSearchQuery },
|
||||
} = dbg;
|
||||
const source = findSource(dbg, "simple1.js");
|
||||
|
||||
await selectSource(dbg, source.url);
|
||||
|
||||
// Open search bar
|
||||
pressKey(dbg, "fileSearch");
|
||||
await waitFor(() => getActiveSearch() === "file");
|
||||
is(getActiveSearch(), "file");
|
||||
|
||||
// Type a search query
|
||||
type(dbg, "con");
|
||||
await waitForSearchState(dbg);
|
||||
is(getFileSearchQuery(), "con");
|
||||
is(getCM(dbg).state.search.query, "con");
|
||||
|
||||
// Close the search bar
|
||||
pressKey(dbg, "Escape");
|
||||
await waitFor(() => getActiveSearch() === null);
|
||||
is(getActiveSearch(), null);
|
||||
|
||||
// Re-open search bar
|
||||
pressKey(dbg, "fileSearch");
|
||||
await waitFor(() => getActiveSearch() === "file");
|
||||
is(getActiveSearch(), "file");
|
||||
|
||||
// Test for the retained query
|
||||
is(getCM(dbg).state.search.query, "con");
|
||||
await waitForDispatch(dbg, "UPDATE_FILE_SEARCH_QUERY");
|
||||
is(getFileSearchQuery(), "con");
|
||||
});
|
|
@ -39,9 +39,10 @@ add_task(async function() {
|
|||
|
||||
type(dbg, "con");
|
||||
await waitForSearchState(dbg);
|
||||
await waitForDispatch(dbg, "UPDATE_SEARCH_RESULTS");
|
||||
|
||||
const state = cm.state.search;
|
||||
|
||||
|
||||
pressKey(dbg, "Enter");
|
||||
is(state.posFrom.line, 3);
|
||||
|
||||
|
@ -75,4 +76,5 @@ add_task(async function() {
|
|||
await clickElement(dbg, "codeMirror");
|
||||
pressKey(dbg, "fileSearch");
|
||||
is(dbg.win.document.activeElement.tagName, "INPUT", "Search field focused");
|
||||
|
||||
});
|
||||
|
|
|
@ -204,11 +204,11 @@ class Frame extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
// If the message comes from a logPoint or conditional breakpoint,
|
||||
// If the message comes from a logPoint,
|
||||
// prefix the source location accordingly
|
||||
if (frame.origin) {
|
||||
if (frame.options) {
|
||||
let locationPrefix;
|
||||
if (frame.origin === "logPoint") {
|
||||
if (frame.options.logPoint) {
|
||||
locationPrefix = "Logpoint @ ";
|
||||
}
|
||||
|
||||
|
|
|
@ -333,10 +333,10 @@ window.onload = async function () {
|
|||
// a prefix should render before source
|
||||
await checkFrameComponent({
|
||||
frame: {
|
||||
origin: "logPoint",
|
||||
source: "http://myfile.com/mahscripts.js",
|
||||
line: 55,
|
||||
column: 10,
|
||||
options: { logPoint: true },
|
||||
}
|
||||
}, {
|
||||
locationPrefix: "Logpoint @ ",
|
||||
|
|
|
@ -52,7 +52,7 @@ exports.viewSourceInStyleEditor = async function(
|
|||
* @param {number} sourceLine
|
||||
* @param {number} sourceColumn
|
||||
* @param {string} sourceID
|
||||
* @param {string} [reason=unknown]
|
||||
* @param {(string|object)} [reason=unknown]
|
||||
*
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
|
|
|
@ -91,6 +91,9 @@ support-files =
|
|||
test-location-debugger-link-console-log.js
|
||||
test-location-debugger-link-errors.js
|
||||
test-location-debugger-link.html
|
||||
test-location-debugger-link-logpoint-1.js
|
||||
test-location-debugger-link-logpoint-2.js
|
||||
test-location-debugger-link-logpoint.html
|
||||
test-location-styleeditor-link-1.css
|
||||
test-location-styleeditor-link-2.css
|
||||
test-location-styleeditor-link-minified.css
|
||||
|
@ -359,6 +362,7 @@ skip-if = fission
|
|||
skip-if = fission
|
||||
[browser_webconsole_keyboard_accessibility.js]
|
||||
[browser_webconsole_location_debugger_link.js]
|
||||
[browser_webconsole_location_logpoint_debugger_link.js]
|
||||
[browser_webconsole_location_scratchpad_link.js]
|
||||
[browser_webconsole_location_styleeditor_link.js]
|
||||
[browser_webconsole_logErrorInPage.js]
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test clicking locations of logpoint logs and errors will open corresponding
|
||||
// conditional panels in the debugger.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI =
|
||||
"http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/browser/test-location-debugger-link-logpoint.html";
|
||||
|
||||
add_task(async function() {
|
||||
// On e10s, the exception thrown in test-location-debugger-link-errors.js
|
||||
// is triggered in child process and is ignored by test harness
|
||||
if (!Services.appinfo.browserTabsRemoteAutostart) {
|
||||
expectUncaughtException();
|
||||
}
|
||||
|
||||
// Eliminate interference from "saved" breakpoints
|
||||
// when running the test multiple times
|
||||
await clearDebuggerPreferences();
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Open the Debugger panel");
|
||||
await openDebugger();
|
||||
|
||||
const toolbox = gDevTools.getToolbox(hud.target);
|
||||
const dbg = createDebuggerContext(toolbox);
|
||||
await selectSource(dbg, "test-location-debugger-link-logpoint-1.js");
|
||||
|
||||
info("Add a logpoint with an invalid expression");
|
||||
await setLogPoint(dbg, 9, "undefinedVariable");
|
||||
|
||||
info("Add a logpoint with a valid expression");
|
||||
await setLogPoint(dbg, 10, "`a is ${a}`");
|
||||
|
||||
await assertEditorLogpoint(dbg, 9, { hasLog: true });
|
||||
await assertEditorLogpoint(dbg, 10, { hasLog: true });
|
||||
|
||||
info("Close the file in the debugger");
|
||||
await closeTab(dbg, "test-location-debugger-link-logpoint-1.js");
|
||||
|
||||
info("Selecting the console");
|
||||
await toolbox.selectTool("webconsole");
|
||||
|
||||
info("Call the function");
|
||||
await invokeInTab("add");
|
||||
|
||||
info("Wait for two messages");
|
||||
await waitFor(() => findMessages(hud, "").length === 2);
|
||||
|
||||
await testOpenInDebugger(
|
||||
hud,
|
||||
toolbox,
|
||||
"[Logpoint threw]: undefinedVariable is not defined",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"undefinedVariable"
|
||||
);
|
||||
|
||||
info("Selecting the console again");
|
||||
await toolbox.selectTool("webconsole");
|
||||
await testOpenInDebugger(
|
||||
hud,
|
||||
toolbox,
|
||||
"a is 1",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"`a is ${a}`"
|
||||
);
|
||||
|
||||
// Test clicking location of a removed logpoint, or a newly added breakpoint
|
||||
// at an old logpoint's location will only highlight its line
|
||||
info("Remove the logpoints");
|
||||
const source = await findSource(
|
||||
dbg,
|
||||
"test-location-debugger-link-logpoint-1.js"
|
||||
);
|
||||
await removeBreakpoint(dbg, source.id, 9);
|
||||
await removeBreakpoint(dbg, source.id, 10);
|
||||
await addBreakpoint(dbg, "test-location-debugger-link-logpoint-1.js", 10);
|
||||
|
||||
info("Selecting the console");
|
||||
await toolbox.selectTool("webconsole");
|
||||
await testOpenInDebugger(
|
||||
hud,
|
||||
toolbox,
|
||||
"[Logpoint threw]: undefinedVariable is not defined",
|
||||
true,
|
||||
9,
|
||||
12
|
||||
);
|
||||
|
||||
info("Selecting the console again");
|
||||
await toolbox.selectTool("webconsole");
|
||||
await testOpenInDebugger(hud, toolbox, "a is 1", true, 10, 12);
|
||||
});
|
||||
|
||||
// Test clicking locations of logpoints from different files
|
||||
add_task(async function() {
|
||||
if (!Services.appinfo.browserTabsRemoteAutostart) {
|
||||
expectUncaughtException();
|
||||
}
|
||||
|
||||
await clearDebuggerPreferences();
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Open the Debugger panel");
|
||||
await openDebugger();
|
||||
|
||||
const toolbox = gDevTools.getToolbox(hud.target);
|
||||
const dbg = createDebuggerContext(toolbox);
|
||||
|
||||
info("Add a logpoint to the first file");
|
||||
await selectSource(dbg, "test-location-debugger-link-logpoint-1.js");
|
||||
await setLogPoint(dbg, 10, "`a is ${a}`");
|
||||
|
||||
info("Add a logpoint to the second file");
|
||||
await selectSource(dbg, "test-location-debugger-link-logpoint-2.js");
|
||||
await setLogPoint(dbg, 10, "`c is ${c}`");
|
||||
|
||||
info("Selecting the console");
|
||||
await toolbox.selectTool("webconsole");
|
||||
|
||||
info("Call the function from the first file");
|
||||
await invokeInTab("add");
|
||||
|
||||
info("Wait for the first message");
|
||||
await waitFor(() => findMessages(hud, "").length === 1);
|
||||
await testOpenInDebugger(
|
||||
hud,
|
||||
toolbox,
|
||||
"a is 1",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"`a is ${a}`"
|
||||
);
|
||||
|
||||
info("Selecting the console again");
|
||||
await toolbox.selectTool("webconsole");
|
||||
|
||||
info("Call the function from the second file");
|
||||
await invokeInTab("subtract");
|
||||
|
||||
info("Wait for the second message");
|
||||
await waitFor(() => findMessages(hud, "").length === 2);
|
||||
await testOpenInDebugger(
|
||||
hud,
|
||||
toolbox,
|
||||
"c is 1",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
"`c is ${c}`"
|
||||
);
|
||||
});
|
||||
|
||||
async function setLogPoint(dbg, index, expression) {
|
||||
rightClickElement(dbg, "gutter", index);
|
||||
selectContextMenuItem(
|
||||
dbg,
|
||||
`${selectors.addLogItem},${selectors.editLogItem}`
|
||||
);
|
||||
const onBreakpointSet = waitForDispatch(dbg, "SET_BREAKPOINT");
|
||||
await typeInPanel(dbg, expression);
|
||||
await onBreakpointSet;
|
||||
}
|
||||
|
||||
function getLineEl(dbg, line) {
|
||||
const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
|
||||
return lines[line - 1];
|
||||
}
|
||||
|
||||
function assertEditorLogpoint(dbg, line, { hasLog = false } = {}) {
|
||||
const hasLogClass = getLineEl(dbg, line).classList.contains("has-log");
|
||||
|
||||
ok(
|
||||
hasLogClass === hasLog,
|
||||
`Breakpoint log ${hasLog ? "exists" : "does not exist"} on line ${line}`
|
||||
);
|
||||
}
|
|
@ -381,17 +381,18 @@ function waitForNodeMutation(node, observeConfig = {}) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Search for a given message. When found, simulate a click on the
|
||||
* Search for a given message. When found, simulate a click on the
|
||||
* message's location, checking to make sure that the debugger opens
|
||||
* the corresponding URL.
|
||||
* the corresponding URL. If the message was generated by a logpoint,
|
||||
* check if the corresponding logpoint editing panel is opened.
|
||||
*
|
||||
* @param {Object} hud
|
||||
* The webconsole
|
||||
* @param {Object} toolbox
|
||||
* The toolbox
|
||||
* @param {String} text
|
||||
* The text to search for. This should be contained in the
|
||||
* message. The searching is done with @see findMessage.
|
||||
* The text to search for. This should be contained in the
|
||||
* message. The searching is done with @see findMessage.
|
||||
* @param {boolean} expectUrl
|
||||
* Whether the URL in the opened source should match the link, or whether
|
||||
* it is expected to be null.
|
||||
|
@ -399,6 +400,8 @@ function waitForNodeMutation(node, observeConfig = {}) {
|
|||
* It indicates if there is the need to check the line.
|
||||
* @param {boolean} expectColumn
|
||||
* It indicates if there is the need to check the column.
|
||||
* @param {String} logPointExpr
|
||||
* The logpoint expression
|
||||
*/
|
||||
async function testOpenInDebugger(
|
||||
hud,
|
||||
|
@ -406,7 +409,8 @@ async function testOpenInDebugger(
|
|||
text,
|
||||
expectUrl = true,
|
||||
expectLine = true,
|
||||
expectColumn = true
|
||||
expectColumn = true,
|
||||
logPointExpr = undefined
|
||||
) {
|
||||
info(`Finding message for open-in-debugger test; text is "${text}"`);
|
||||
const messageNode = await waitFor(() => findMessage(hud, text));
|
||||
|
@ -420,7 +424,8 @@ async function testOpenInDebugger(
|
|||
frameLinkNode,
|
||||
expectUrl,
|
||||
expectLine,
|
||||
expectColumn
|
||||
expectColumn,
|
||||
logPointExpr
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -433,7 +438,8 @@ async function checkClickOnNode(
|
|||
frameLinkNode,
|
||||
expectUrl,
|
||||
expectLine,
|
||||
expectColumn
|
||||
expectColumn,
|
||||
logPointExpr
|
||||
) {
|
||||
info("checking click on node location");
|
||||
|
||||
|
@ -485,6 +491,22 @@ async function checkClickOnNode(
|
|||
"expected source column"
|
||||
);
|
||||
}
|
||||
|
||||
if (logPointExpr !== undefined && logPointExpr !== "") {
|
||||
const inputEl = dbg.panelWin.document.activeElement;
|
||||
is(
|
||||
inputEl.tagName,
|
||||
"TEXTAREA",
|
||||
"The textarea of logpoint panel is focused"
|
||||
);
|
||||
|
||||
const inputValue = inputEl.parentElement.parentElement.innerText.trim();
|
||||
is(
|
||||
inputValue,
|
||||
logPointExpr,
|
||||
"The input in the open logpoint panel matches the logpoint expression"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function add() {
|
||||
const a = 1;
|
||||
const b = 2;
|
||||
|
||||
return a + b;
|
||||
}
|
||||
|
||||
add();
|
|
@ -0,0 +1,15 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function subtract() {
|
||||
const c = 1;
|
||||
const d = 2;
|
||||
|
||||
return c - d;
|
||||
}
|
||||
|
||||
subtract();
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
Web Console test for opening logpoint message links in Debugger
|
||||
</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="test-location-debugger-link-logpoint-1.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="test-location-debugger-link-logpoint-2.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Web Console test for opening logpoint message links in Debugger.</p>
|
||||
<button onclick="add()">Add</button>
|
||||
<button onclick="subtract()">Subtract</button>
|
||||
</body>
|
||||
</html>
|
|
@ -247,7 +247,7 @@ function transformConsoleAPICallPacket(packet) {
|
|||
: null;
|
||||
|
||||
if (type === "logPointError" || type === "logPoint") {
|
||||
frame.origin = "logPoint";
|
||||
frame.options = { logPoint: true };
|
||||
}
|
||||
|
||||
return new ConsoleMessage({
|
||||
|
|
|
@ -9970,6 +9970,21 @@ exports.CSS_PROPERTIES = {
|
|||
"unset"
|
||||
]
|
||||
},
|
||||
"text-decoration-skip-ink": {
|
||||
"isInherited": true,
|
||||
"subproperties": [
|
||||
"text-decoration-skip-ink"
|
||||
],
|
||||
"supports": [],
|
||||
"values": [
|
||||
"auto",
|
||||
"inherit",
|
||||
"initial",
|
||||
"none",
|
||||
"revert",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"text-decoration-style": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
|
@ -9989,6 +10004,21 @@ exports.CSS_PROPERTIES = {
|
|||
"wavy"
|
||||
]
|
||||
},
|
||||
"text-decoration-thickness": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
"text-decoration-thickness"
|
||||
],
|
||||
"supports": [],
|
||||
"values": [
|
||||
"auto",
|
||||
"from-font",
|
||||
"inherit",
|
||||
"initial",
|
||||
"revert",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"text-emphasis": {
|
||||
"isInherited": true,
|
||||
"subproperties": [
|
||||
|
@ -10202,6 +10232,21 @@ exports.CSS_PROPERTIES = {
|
|||
"uppercase"
|
||||
]
|
||||
},
|
||||
"text-underline-offset": {
|
||||
"isInherited": true,
|
||||
"subproperties": [
|
||||
"text-underline-offset"
|
||||
],
|
||||
"supports": [],
|
||||
"values": [
|
||||
"auto",
|
||||
"from-font",
|
||||
"inherit",
|
||||
"initial",
|
||||
"revert",
|
||||
"unset"
|
||||
]
|
||||
},
|
||||
"top": {
|
||||
"isInherited": false,
|
||||
"subproperties": [
|
||||
|
|
|
@ -11349,6 +11349,10 @@ nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
|
|||
}
|
||||
newSHEntry->SetURI(aNewURI);
|
||||
newSHEntry->SetOriginalURI(aNewURI);
|
||||
// Setting the resultPrincipalURI to nullptr is fine here: it will cause
|
||||
// NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
|
||||
// in our case. We could also set it to aNewURI, with the same result.
|
||||
newSHEntry->SetResultPrincipalURI(nullptr);
|
||||
newSHEntry->SetLoadReplace(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<html>
|
||||
<body>You should never see this</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
HTTP 302 Moved Temporarily
|
||||
Location: ../navigation/blank.html?x=y
|
|
@ -55,6 +55,11 @@ support-files =
|
|||
[test_anchor_scroll_after_document_open.html]
|
||||
[test_bfcache_plus_hash.html]
|
||||
[test_bug123696.html]
|
||||
[test_bug1422334.html]
|
||||
support-files =
|
||||
bug1422334_redirect.html
|
||||
bug1422334_redirect.html^headers^
|
||||
!/docshell/test/navigation/blank.html
|
||||
[test_bug384014.html]
|
||||
[test_bug385434.html]
|
||||
[test_bug387979.html]
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Ensure that reload after replaceState after 3xx redirect does the right thing.</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ifr = document.querySelector("iframe");
|
||||
var win = ifr.contentWindow;
|
||||
is(win.location.href,
|
||||
location.href.replace("mochitest/test_bug1422334.html",
|
||||
"navigation/blank.html?x=y"),
|
||||
"Should have the right location on initial load");
|
||||
|
||||
win.history.replaceState(null, '', win.location.pathname);
|
||||
is(win.location.href,
|
||||
location.href.replace("mochitest/test_bug1422334.html",
|
||||
"navigation/blank.html"),
|
||||
"Should have the right location after replaceState call");
|
||||
|
||||
ifr.onload = function() {
|
||||
is(win.location.href,
|
||||
location.href.replace("mochitest/test_bug1422334.html",
|
||||
"navigation/blank.html"),
|
||||
"Should have the right location after reload");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
win.location.reload();
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"><iframe src="bug1422334_redirect.html"></iframe></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -25,6 +25,7 @@
|
|||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/ServoBindings.h" // Servo_GetProperties_Overriding_Animation
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/StaticPrefs_layers.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TypeTraits.h" // For std::forward<>
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -73,7 +74,7 @@ bool EffectCompositor::AllowCompositorAnimationsOnFrame(
|
|||
}
|
||||
|
||||
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
if (StaticPrefs::layers_offmainthreadcomposition_log_animations()) {
|
||||
nsCString message;
|
||||
message.AppendLiteral(
|
||||
"Performance warning: Async animations are "
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
#include "mozilla/StaticPrefs_layers.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
#include "Layers.h" // For Layer
|
||||
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetComputedStyle
|
||||
|
@ -49,7 +50,7 @@ void AnimationProperty::SetPerformanceWarning(
|
|||
mPerformanceWarning = Some(aWarning);
|
||||
|
||||
nsAutoString localizedString;
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled() &&
|
||||
if (StaticPrefs::layers_offmainthreadcomposition_log_animations() &&
|
||||
mPerformanceWarning->ToLocalizedString(localizedString)) {
|
||||
nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
|
||||
AnimationUtils::LogAsyncAnimationFailure(logMessage, aElement);
|
||||
|
|
|
@ -122,6 +122,7 @@
|
|||
#include "nsLayoutUtils.h" // for GetFrameForPoint
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIBrowserChild.h"
|
||||
#include "nsImportModule.h"
|
||||
|
||||
#include "nsRange.h"
|
||||
#include "mozilla/dom/DocumentType.h"
|
||||
|
@ -142,6 +143,7 @@
|
|||
#include "nsPresContext.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
#include "nsIBrowserUsage.h"
|
||||
#include "nsIEditingSession.h"
|
||||
#include "nsIFileChannel.h"
|
||||
#include "nsIMultiPartChannel.h"
|
||||
|
@ -158,7 +160,8 @@
|
|||
#include "nsIAuthPrompt2.h"
|
||||
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPermission.h"
|
||||
#include "nsPermissionManager.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIPrivateBrowsingChannel.h"
|
||||
#include "ExpandedPrincipal.h"
|
||||
|
@ -15438,7 +15441,9 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
|
|||
parent->GetDocumentURI(), false, isOnAllowList)),
|
||||
!isOnAllowList);
|
||||
|
||||
auto performFinalChecks = [inner]()
|
||||
RefPtr<Document> self(this);
|
||||
|
||||
auto performFinalChecks = [inner, self]()
|
||||
-> RefPtr<AntiTrackingCommon::StorageAccessFinalCheckPromise> {
|
||||
RefPtr<AntiTrackingCommon::StorageAccessFinalCheckPromise::Private> p =
|
||||
new AntiTrackingCommon::StorageAccessFinalCheckPromise::Private(
|
||||
|
@ -15447,17 +15452,23 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
|
|||
StorageAccessPermissionRequest::Create(
|
||||
inner,
|
||||
// Allow
|
||||
[p] { p->Resolve(AntiTrackingCommon::eAllow, __func__); },
|
||||
// Allow auto grant
|
||||
[p] {
|
||||
p->Resolve(AntiTrackingCommon::eAllowAutoGrant, __func__);
|
||||
Telemetry::AccumulateCategorical(
|
||||
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
|
||||
p->Resolve(AntiTrackingCommon::eAllow, __func__);
|
||||
},
|
||||
// Allow on any site
|
||||
[p] {
|
||||
Telemetry::AccumulateCategorical(
|
||||
Telemetry::LABELS_STORAGE_ACCESS_API_UI::AllowOnAnySite);
|
||||
p->Resolve(AntiTrackingCommon::eAllowOnAnySite, __func__);
|
||||
},
|
||||
// Block
|
||||
[p] { p->Reject(false, __func__); });
|
||||
[p] {
|
||||
Telemetry::AccumulateCategorical(
|
||||
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
|
||||
p->Reject(false, __func__);
|
||||
});
|
||||
|
||||
typedef ContentPermissionRequestBase::PromptResult PromptResult;
|
||||
PromptResult pr = sapr->CheckPromptPrefs();
|
||||
|
@ -15473,22 +15484,65 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
|
|||
}
|
||||
}
|
||||
|
||||
if (pr != PromptResult::Pending) {
|
||||
MOZ_ASSERT_IF(pr != PromptResult::Granted,
|
||||
pr == PromptResult::Denied);
|
||||
if (pr == PromptResult::Granted) {
|
||||
return AntiTrackingCommon::StorageAccessFinalCheckPromise::
|
||||
CreateAndResolve(onAnySite ? AntiTrackingCommon::eAllowOnAnySite
|
||||
: AntiTrackingCommon::eAllow,
|
||||
__func__);
|
||||
}
|
||||
return AntiTrackingCommon::StorageAccessFinalCheckPromise::
|
||||
CreateAndReject(false, __func__);
|
||||
if (pr == PromptResult::Pending) {
|
||||
// We're about to show a prompt, record the request attempt
|
||||
Telemetry::AccumulateCategorical(
|
||||
Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
|
||||
}
|
||||
|
||||
sapr->RequestDelayedTask(
|
||||
inner->EventTargetFor(TaskCategory::Other),
|
||||
ContentPermissionRequestBase::DelayedTaskType::Request);
|
||||
self->AutomaticStorageAccessCanBeGranted()->Then(
|
||||
GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[p, pr, sapr, inner, onAnySite](
|
||||
const AutomaticStorageAccessGrantPromise::ResolveOrRejectValue&
|
||||
aValue) -> void {
|
||||
// Make a copy because we can't modified copy-captured lambda
|
||||
// variables.
|
||||
PromptResult pr2 = pr;
|
||||
|
||||
bool storageAccessCanBeGrantedAutomatically =
|
||||
aValue.IsResolve() && aValue.ResolveValue();
|
||||
|
||||
bool autoGrant = false;
|
||||
if (pr2 == PromptResult::Pending &&
|
||||
storageAccessCanBeGrantedAutomatically) {
|
||||
pr2 = PromptResult::Granted;
|
||||
autoGrant = true;
|
||||
|
||||
Telemetry::AccumulateCategorical(
|
||||
Telemetry::LABELS_STORAGE_ACCESS_API_UI::
|
||||
AllowAutomatically);
|
||||
}
|
||||
|
||||
if (pr2 != PromptResult::Pending) {
|
||||
MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
|
||||
pr2 == PromptResult::Denied);
|
||||
if (pr2 == PromptResult::Granted) {
|
||||
AntiTrackingCommon::StorageAccessPromptChoices choice =
|
||||
AntiTrackingCommon::eAllow;
|
||||
if (onAnySite) {
|
||||
choice = AntiTrackingCommon::eAllowOnAnySite;
|
||||
} else if (autoGrant) {
|
||||
choice = AntiTrackingCommon::eAllowAutoGrant;
|
||||
}
|
||||
if (!autoGrant) {
|
||||
p->Resolve(choice, __func__);
|
||||
} else {
|
||||
sapr->MaybeDelayAutomaticGrants()->Then(
|
||||
GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[p, choice] { p->Resolve(choice, __func__); },
|
||||
[p] { p->Reject(false, __func__); });
|
||||
}
|
||||
return;
|
||||
}
|
||||
p->Reject(false, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
sapr->RequestDelayedTask(
|
||||
inner->EventTargetFor(TaskCategory::Other),
|
||||
ContentPermissionRequestBase::DelayedTaskType::Request);
|
||||
});
|
||||
|
||||
return p.forget();
|
||||
};
|
||||
AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
|
||||
|
@ -15518,6 +15572,122 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<Document::AutomaticStorageAccessGrantPromise>
|
||||
Document::AutomaticStorageAccessCanBeGranted() {
|
||||
if (XRE_IsContentProcess()) {
|
||||
// In the content process, we need to ask the parent process to compute
|
||||
// this. The reason is that nsIPermissionManager::GetAllWithTypePrefix()
|
||||
// isn't accessible in the content process.
|
||||
ContentChild* cc = ContentChild::GetSingleton();
|
||||
MOZ_ASSERT(cc);
|
||||
|
||||
return cc
|
||||
->SendAutomaticStorageAccessCanBeGranted(
|
||||
IPC::Principal(NodePrincipal()))
|
||||
->Then(
|
||||
GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[](const ContentChild::AutomaticStorageAccessCanBeGrantedPromise::
|
||||
ResolveOrRejectValue& aValue) {
|
||||
if (aValue.IsResolve()) {
|
||||
return AutomaticStorageAccessGrantPromise::CreateAndResolve(
|
||||
aValue.ResolveValue(), __func__);
|
||||
}
|
||||
|
||||
return AutomaticStorageAccessGrantPromise::CreateAndReject(
|
||||
false, __func__);
|
||||
});
|
||||
}
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// In the parent process, we can directly compute this.
|
||||
return AutomaticStorageAccessGrantPromise::CreateAndResolve(
|
||||
AutomaticStorageAccessCanBeGranted(NodePrincipal()), __func__);
|
||||
}
|
||||
|
||||
return AutomaticStorageAccessGrantPromise::CreateAndReject(false, __func__);
|
||||
}
|
||||
|
||||
bool Document::AutomaticStorageAccessCanBeGranted(nsIPrincipal* aPrincipal) {
|
||||
nsAutoCString prefix;
|
||||
AntiTrackingCommon::CreateStoragePermissionKey(aPrincipal, prefix);
|
||||
|
||||
nsPermissionManager* permManager = nsPermissionManager::GetInstance();
|
||||
if (NS_WARN_IF(!permManager)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef nsTArray<RefPtr<nsIPermission>> Permissions;
|
||||
Permissions perms;
|
||||
nsresult rv = permManager->GetAllWithTypePrefix(prefix, perms);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoCString prefix2(prefix);
|
||||
prefix2.Append('^');
|
||||
typedef nsTArray<nsCString> Origins;
|
||||
Origins origins;
|
||||
|
||||
for (const auto& perm : perms) {
|
||||
nsAutoCString type;
|
||||
rv = perm->GetType(type);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
// Let's make sure that we're not looking at a permission for
|
||||
// https://exampletracker.company when we mean to look for the
|
||||
// permission for https://exampletracker.com!
|
||||
if (type != prefix && StringHead(type, prefix2.Length()) != prefix2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = perm->GetPrincipal(getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoCString origin;
|
||||
rv = principal->GetOrigin(origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ToLowerCase(origin);
|
||||
|
||||
if (origins.IndexOf(origin) == Origins::NoIndex) {
|
||||
origins.AppendElement(origin);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBrowserUsage> bu =
|
||||
do_ImportModule("resource:///modules/BrowserUsageTelemetry.jsm");
|
||||
if (NS_WARN_IF(!bu)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t uniqueDomainsVisitedInPast24Hours = 0;
|
||||
rv = bu->GetUniqueDomainsVisitedInPast24Hours(
|
||||
&uniqueDomainsVisitedInPast24Hours);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// one percent of the number of top-levels origins visited in the current
|
||||
// session (but not to exceed 24 hours), or the value of the
|
||||
// dom.storage_access.max_concurrent_auto_grants preference, whichever is
|
||||
// higher.
|
||||
size_t maxConcurrentAutomaticGrants = std::max(
|
||||
std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours / 100)),
|
||||
StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
|
||||
0);
|
||||
|
||||
size_t originsThirdPartyHasAccessTo = origins.Length();
|
||||
|
||||
return StaticPrefs::dom_storage_access_auto_grants() &&
|
||||
originsThirdPartyHasAccessTo < maxConcurrentAutomaticGrants;
|
||||
}
|
||||
|
||||
void Document::RecordNavigationTiming(ReadyState aReadyState) {
|
||||
if (!XRE_IsContentProcess()) {
|
||||
return;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/EventStates.h" // for EventStates
|
||||
#include "mozilla/FlushType.h" // for enum
|
||||
#include "mozilla/MozPromise.h" // for MozPromise
|
||||
#include "mozilla/Pair.h" // for Pair
|
||||
#include "mozilla/Saturate.h" // for SaturateUint32
|
||||
#include "nsAutoPtr.h" // for member
|
||||
|
@ -4129,6 +4130,8 @@ class Document : public nsINode,
|
|||
|
||||
static bool HasRecentlyStartedForegroundLoads();
|
||||
|
||||
static bool AutomaticStorageAccessCanBeGranted(nsIPrincipal* aPrincipal);
|
||||
|
||||
protected:
|
||||
void DoUpdateSVGUseElementShadowTrees();
|
||||
|
||||
|
@ -4397,6 +4400,10 @@ class Document : public nsINode,
|
|||
|
||||
void MaybeResolveReadyForIdle();
|
||||
|
||||
typedef MozPromise<bool, bool, true> AutomaticStorageAccessGrantPromise;
|
||||
MOZ_MUST_USE RefPtr<AutomaticStorageAccessGrantPromise>
|
||||
AutomaticStorageAccessCanBeGranted();
|
||||
|
||||
// This should *ONLY* be used in GetCookie/SetCookie.
|
||||
already_AddRefed<nsIChannel> CreateDummyChannelForCookies(
|
||||
nsIURI* aContentURI);
|
||||
|
|
|
@ -21,14 +21,12 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(StorageAccessPermissionRequest,
|
|||
StorageAccessPermissionRequest::StorageAccessPermissionRequest(
|
||||
nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback)
|
||||
: ContentPermissionRequestBase(aNodePrincipal, aWindow,
|
||||
NS_LITERAL_CSTRING("dom.storage_access"),
|
||||
NS_LITERAL_CSTRING("storage-access")),
|
||||
mAllowCallback(std::move(aAllowCallback)),
|
||||
mAllowAutoGrantCallback(std::move(aAllowAutoGrantCallback)),
|
||||
mAllowAnySiteCallback(std::move(aAllowAnySiteCallback)),
|
||||
mCancelCallback(std::move(aCancelCallback)),
|
||||
mCallbackCalled(false) {
|
||||
|
@ -42,7 +40,6 @@ NS_IMETHODIMP
|
|||
StorageAccessPermissionRequest::Cancel() {
|
||||
if (!mCallbackCalled) {
|
||||
mCallbackCalled = true;
|
||||
mTimer = nullptr;
|
||||
mCancelCallback();
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -56,38 +53,58 @@ StorageAccessPermissionRequest::Allow(JS::HandleValue aChoices) {
|
|||
return rv;
|
||||
}
|
||||
|
||||
// There is no support to allow grants automatically from the prompting code
|
||||
// path.
|
||||
|
||||
if (!mCallbackCalled) {
|
||||
mCallbackCalled = true;
|
||||
if (choices.Length() == 1 &&
|
||||
choices[0].choice().EqualsLiteral("allow-on-any-site")) {
|
||||
mAllowAnySiteCallback();
|
||||
} else if (choices.Length() == 1 &&
|
||||
choices[0].choice().EqualsLiteral("allow-auto-grant")) {
|
||||
unsigned simulatedDelay = CalculateSimulatedDelay();
|
||||
if (simulatedDelay) {
|
||||
MOZ_ASSERT(!mTimer);
|
||||
RefPtr<StorageAccessPermissionRequest> self = this;
|
||||
rv = NS_NewTimerWithFuncCallback(
|
||||
getter_AddRefs(mTimer), CallAutoGrantCallback, this, simulatedDelay,
|
||||
nsITimer::TYPE_ONE_SHOT, "DelayedAllowAutoGrantCallback");
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
NS_ADDREF(this);
|
||||
} else {
|
||||
mAllowAutoGrantCallback();
|
||||
}
|
||||
} else {
|
||||
choices[0].choice().EqualsLiteral("allow")) {
|
||||
mAllowCallback();
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<StorageAccessPermissionRequest::AutoGrantDelayPromise>
|
||||
StorageAccessPermissionRequest::MaybeDelayAutomaticGrants() {
|
||||
RefPtr<AutoGrantDelayPromise::Private> p =
|
||||
new AutoGrantDelayPromise::Private(__func__);
|
||||
|
||||
unsigned simulatedDelay = CalculateSimulatedDelay();
|
||||
if (simulatedDelay) {
|
||||
nsCOMPtr<nsITimer> timer;
|
||||
RefPtr<AutoGrantDelayPromise::Private> promise = p;
|
||||
nsresult rv = NS_NewTimerWithFuncCallback(
|
||||
getter_AddRefs(timer),
|
||||
[](nsITimer* aTimer, void* aClosure) -> void {
|
||||
auto* promise =
|
||||
static_cast<AutoGrantDelayPromise::Private*>(aClosure);
|
||||
promise->Resolve(true, __func__);
|
||||
NS_RELEASE(aTimer);
|
||||
NS_RELEASE(promise);
|
||||
},
|
||||
promise, simulatedDelay, nsITimer::TYPE_ONE_SHOT,
|
||||
"DelayedAllowAutoGrantCallback");
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
p->Reject(false, __func__);
|
||||
} else {
|
||||
// Leak the references here! We'll release them inside the callback.
|
||||
Unused << timer.forget();
|
||||
Unused << promise.forget();
|
||||
}
|
||||
} else {
|
||||
p->Resolve(false, __func__);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
already_AddRefed<StorageAccessPermissionRequest>
|
||||
StorageAccessPermissionRequest::Create(
|
||||
nsPIDOMWindowInner* aWindow, AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback) {
|
||||
if (!aWindow) {
|
||||
|
@ -100,8 +117,7 @@ StorageAccessPermissionRequest::Create(
|
|||
RefPtr<StorageAccessPermissionRequest> request =
|
||||
new StorageAccessPermissionRequest(
|
||||
aWindow, win->GetPrincipal(), std::move(aAllowCallback),
|
||||
std::move(aAllowAutoGrantCallback), std::move(aAllowAnySiteCallback),
|
||||
std::move(aCancelCallback));
|
||||
std::move(aAllowAnySiteCallback), std::move(aCancelCallback));
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
|
@ -121,13 +137,5 @@ unsigned StorageAccessPermissionRequest::CalculateSimulatedDelay() {
|
|||
return kMin + random % (kMax - kMin);
|
||||
}
|
||||
|
||||
void StorageAccessPermissionRequest::CallAutoGrantCallback(nsITimer* aTimer,
|
||||
void* aClosure) {
|
||||
auto self = static_cast<StorageAccessPermissionRequest*>(aClosure);
|
||||
self->mAllowAutoGrantCallback();
|
||||
self->mTimer = nullptr;
|
||||
NS_RELEASE(self);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define StorageAccessPermissionRequest_h_
|
||||
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
@ -28,34 +29,30 @@ class StorageAccessPermissionRequest final
|
|||
NS_IMETHOD Allow(JS::HandleValue choices) override;
|
||||
|
||||
typedef std::function<void()> AllowCallback;
|
||||
typedef std::function<void()> AllowAutoGrantCallback;
|
||||
typedef std::function<void()> AllowAnySiteCallback;
|
||||
typedef std::function<void()> CancelCallback;
|
||||
|
||||
static already_AddRefed<StorageAccessPermissionRequest> Create(
|
||||
nsPIDOMWindowInner* aWindow, AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback);
|
||||
|
||||
typedef MozPromise<bool, bool, true> AutoGrantDelayPromise;
|
||||
RefPtr<AutoGrantDelayPromise> MaybeDelayAutomaticGrants();
|
||||
|
||||
private:
|
||||
StorageAccessPermissionRequest(
|
||||
nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAutoGrantCallback&& aAllowAutoGrantCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback);
|
||||
StorageAccessPermissionRequest(nsPIDOMWindowInner* aWindow,
|
||||
nsIPrincipal* aNodePrincipal,
|
||||
AllowCallback&& aAllowCallback,
|
||||
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
||||
CancelCallback&& aCancelCallback);
|
||||
~StorageAccessPermissionRequest();
|
||||
|
||||
unsigned CalculateSimulatedDelay();
|
||||
|
||||
static void CallAutoGrantCallback(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
AllowCallback mAllowCallback;
|
||||
AllowAutoGrantCallback mAllowAutoGrantCallback;
|
||||
AllowAnySiteCallback mAllowAnySiteCallback;
|
||||
CancelCallback mCancelCallback;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsTArray<PermissionRequest> mPermissionRequests;
|
||||
bool mCallbackCalled;
|
||||
};
|
||||
|
|
|
@ -497,6 +497,7 @@ LOCAL_INCLUDES += [
|
|||
'/dom/xml',
|
||||
'/dom/xslt/xpath',
|
||||
'/dom/xul',
|
||||
'/extensions/permissions',
|
||||
'/gfx/2d',
|
||||
'/image',
|
||||
'/js/xpconnect/loader',
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<!doctype html>
|
||||
<base href="a:">
|
||||
<script>
|
||||
document.createElement("canvas").getContext("2d").filter = "url()";
|
||||
</script>
|
|
@ -55,3 +55,4 @@ pref(gfx.offscreencanvas.enabled,true) load 1443671.html
|
|||
pref(gfx.offscreencanvas.enabled,true) load 1546390.html
|
||||
load 1549853.html
|
||||
load 1551745.html
|
||||
load 1569648.html
|
||||
|
|
|
@ -37,6 +37,28 @@ enum MozContentPolicyType {
|
|||
"other"
|
||||
};
|
||||
|
||||
/**
|
||||
* String versions of CLASSIFIED_* tracking flags from nsHttpChannel.idl
|
||||
*/
|
||||
enum MozUrlClassificationFlags {
|
||||
"fingerprinting",
|
||||
"fingerprinting_content",
|
||||
"cryptomining",
|
||||
"cryptomining_content",
|
||||
"tracking",
|
||||
"tracking_ad",
|
||||
"tracking_analytics",
|
||||
"tracking_social",
|
||||
"tracking_content",
|
||||
"socialtracking",
|
||||
"socialtracking_facebook",
|
||||
"socialtracking_linkedin",
|
||||
"socialtracking_twitter",
|
||||
"any_basic_tracking",
|
||||
"any_strict_tracking",
|
||||
"any_social_tracking"
|
||||
};
|
||||
|
||||
/**
|
||||
* A thin wrapper around nsIChannel and nsIHttpChannel that allows JS
|
||||
* callers to access them without XPConnect overhead.
|
||||
|
@ -382,6 +404,20 @@ interface ChannelWrapper : EventTarget {
|
|||
void setResponseHeader(ByteString header,
|
||||
ByteString value,
|
||||
optional boolean merge = false);
|
||||
|
||||
/**
|
||||
* Provides the tracking classification data when it is available.
|
||||
*/
|
||||
[Cached, Frozen, GetterThrows, Pure]
|
||||
readonly attribute MozUrlClassification? urlClassification;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for first and third party tracking classification data.
|
||||
*/
|
||||
dictionary MozUrlClassification {
|
||||
required sequence<MozUrlClassificationFlags> firstParty;
|
||||
required sequence<MozUrlClassificationFlags> thirdParty;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,53 +16,394 @@ IPC Diagram
|
|||
JS Window Actor
|
||||
===============
|
||||
|
||||
What are actors?
|
||||
----------------
|
||||
What are JS Window Actors?
|
||||
--------------------------
|
||||
|
||||
In the Fission world, Actors will be the replacement for framescripts. Framescripts were how we structured code to be aware of the parent (UI) and child (content) separation, including establishing the communication channel between the two (via the message manager).
|
||||
In the Fission world, JS Window Actors will be the replacement for framescripts. Framescripts were how we structured code to be aware of the parent (UI) and child (content) separation, including establishing the communication channel between the two (via the Frame Message Manager).
|
||||
|
||||
However, the framescripts had no way to establish further process separation downwards (that is, for out-of-process iframes). Actors will be the replacement.
|
||||
However, the framescripts had no way to establish further process separation downwards (that is, for out-of-process iframes). JS Window Actors will be the replacement.
|
||||
|
||||
How are they structured?
|
||||
------------------------
|
||||
|
||||
Currently, in the post-e10s Firefox codebase, we have code living in the parent process (UI) that is in plain JS (.js files) or in JS modules (.jsm). In the child process (hosting the content), we use framescripts (.js) and also JS modules. The framescripts are instantiated once per top-level frame (or, in simpler terms, once per tab). This code has access to all of the DOM from the web content, including iframes on it.
|
||||
A review of the current Message Manager mechanism
|
||||
`````````````````````````````````````````````````
|
||||
|
||||
The two processes communicate between them through the message manager (mm) using the sendAsyncMessage API, and any code in the parent can communicate with any code in the child (and vice versa), by just listening to the messsages of interest.
|
||||
.. note::
|
||||
There are actually several types of Message Managers: Frame Message Managers, Window Message Managers, Group Message Managers and Process Message Managers. For the purposes of this documentation, it's simplest to refer to all of these mechanisms altogether as the "Message Manager mechanism". Most of the examples in this document will be operating on the assumption that the Message Manager is a Frame Message Manager, which is the most commonly used one.
|
||||
|
||||
Currently, in the post `Electrolysis Project`_ Firefox codebase, we have code living in the parent process (UI) that is in plain JS (.js files) or in JS modules (.jsm files). In the child process (hosting the content), we use framescripts (.js) and also JS modules. The framescripts are instantiated once per top-level frame (or, in simpler terms, once per tab). This code has access to all of the DOM from the web content, including all iframes within it.
|
||||
|
||||
The two processes communicate via the Frame Message Manager (mm) using the ``sendAsyncMessage`` / ``receiveMessage`` API, and any code in the parent can communicate with any code in the child (and vice versa), by just listening to the messages of interest.
|
||||
|
||||
The Frame Message Manager communication mechanism follows a publish / subscribe pattern similar to how Events work in Firefox:
|
||||
|
||||
1. Something exposes a mechanism for subscribing to notifications (``addMessageListener`` for the Frame Message Manager, ``addEventListener`` for Events).
|
||||
2. The subscriber is responsible for unsubscribing when there's no longer interest in the notifications (``removeMessageListener`` for the Frame Message Manager, ``removeEventListener`` for Events).
|
||||
3. Any number of subscribers can be attached at any one time.
|
||||
|
||||
.. figure:: Fission-framescripts.png
|
||||
:width: 320px
|
||||
:height: 200px
|
||||
|
||||
For fission, the actors replacing FrameScript will be structured in pairs. A pair of actors will be instantiated lazily: one in the parent and one in the child process, and a direct channel of communication between the two will be established.
|
||||
These actors are "managed" by the WindowGlobal actors, and are implemented as JS classes instantiated when requested for any particular window.
|
||||
How JS Window Actors differ from the Frame Message Manager
|
||||
``````````````````````````````````````````````````````````
|
||||
|
||||
For Fission, the JS Window Actors replacing framescripts will be structured in pairs. A pair of JS Window Actors will be instantiated lazily: one in the parent and one in the child process, and a direct channel of communication between the two will be established. The JS Window Actor in the parent must extend the global ``JSWindowActorParent`` class, and the JS Window Actor in the child must extend the global ``JSWindowActorChild`` class.
|
||||
|
||||
The JS Window Actor mechanism is similar to how `IPC Actors`_ work in the native layer of Firefox:
|
||||
|
||||
#. Every Actor has one counterpart in another process that they can communicate directly with.
|
||||
#. Every Actor inherits a common communications API from a parent class.
|
||||
#. Every Actor has a name that ends in either ``Parent`` or ``Child``.
|
||||
#. There is no built-in mechanism for subscribing to messages. When one JS Window Actor sends a message, the counterpart JS Window Actor on the other side will receive it without needing to explicitly listen for it.
|
||||
|
||||
Other notable differences between JSWindowActor's and Message Manager / framescripts:
|
||||
|
||||
#. Each JSWindowActor pair is associated with a particular frame. For example, given the following DOM hierarchy::
|
||||
|
||||
<browser src="https://www.example.com">
|
||||
<iframe src="https://www.a.com" />
|
||||
<iframe src="https://www.b.com" />
|
||||
|
||||
A ``JSWindowActorParent / ``JSWindowActorChild`` pair instantiated for either of the ``iframe``'s would only be sending messages to and from that ``iframe``.
|
||||
|
||||
#. There's only one pair per actor type, per frame.
|
||||
|
||||
For example, suppose we have a ``ContextMenu`` actor. The parent process can have up to N instances of the ``ContextMenuParent`` actor, where N is the number of frames that are currently loaded. For any individual frame though, there's only ever one `ContextMenuChild` associated with that frame.
|
||||
|
||||
#. We can no longer assume full, synchronous access to the frame tree, even in content processes.
|
||||
|
||||
This is a natural consequence of splitting frames to run out-of-process.
|
||||
|
||||
#. ``JSWindowActorChild``'s live as long as the ``BrowsingContext`` they're associated with.
|
||||
|
||||
If in the previously mentioned DOM hierarchy, one of the ``<iframe>``'s unload, any associated JSWindowActor pairs will be torn down.
|
||||
|
||||
.. hint::
|
||||
JS Window Actors are "managed" by the WindowGlobal IPC Actors, and are implemented as JS classes (subclasses of ``JSWindowActorParent`` and ``JSWindowActorChild``) instantiated when requested for any particular window. Like the Frame Message Manager, they are ultimately using IPC Actors to communicate under the hood.
|
||||
|
||||
.. figure:: Fission-actors-diagram.png
|
||||
:width: 233px
|
||||
:height: 240px
|
||||
|
||||
Communicating between actors
|
||||
----------------------------
|
||||
|
||||
The actor will only communicate between each other. To communicate, the actor supports ``sendAsyncMessage`` and ``sendQuery`` which acts like send async message, but also returns a promise, and it will be present for both in-process and out-of-process windows.
|
||||
Cross-process communication with JS Window Actors
|
||||
-------------------------------------------------
|
||||
|
||||
.. note::
|
||||
Like the Message Manager, JSWindowActors are implemented for both in-process and out-of-process frame communication. This means that porting to JSWindowActors can be done immediately without waiting for out-of-process iframes to be enabled.
|
||||
|
||||
Sync IPC is not supported on JSWindowActors, and code which needs to send sync messages should be modified, or must send them over the per-process message manager.
|
||||
The ``JSWindowActorParent`` and ``JSWindowActorChild`` base classes expose two methods for sending messages:
|
||||
|
||||
Things are exposed on a JS Window Actor
|
||||
---------------------------------------
|
||||
``sendAsyncMessage``
|
||||
````````````````````
|
||||
|
||||
See `JSWindowActor.webidl <https://searchfox.org/mozilla-central/source/dom/chrome-webidl/JSWindowActor.webidl>`_ for more detail.
|
||||
This has a similar signature as the ``sendAsyncMessage`` method for Message Managers::
|
||||
|
||||
sendAsyncMessage("SomeMessage", { key: "value" }, { transferredObject });
|
||||
|
||||
Like messages sent via the Message Manager, anything that can be serialized using the structured clone algorithm can be sent down through the second argument. Additionally, ``nsIPrincipal``'s can be sent without manually serializing and deserializing them.
|
||||
|
||||
The third argument sends `Transferables`_ to the receiver, for example an ``ArrayBuffer``.
|
||||
|
||||
.. note::
|
||||
Cross Process Object Wrappers (CPOWs) cannot be sent over JSWindowActors.
|
||||
|
||||
.. note::
|
||||
Notably absent is ``sendSyncMessage`` or ``sendRPCMessage``. Sync IPC is not supported on JSWindowActors, and code which needs to send sync messages should be modified to use async messages, or must send them over the per-process message manager.
|
||||
|
||||
``sendQuery``
|
||||
`````````````
|
||||
|
||||
``sendQuery`` improves upon ``sendAsyncMessage`` by returning a ``Promise``. The receiver of the message must then return a ``Promise`` that can eventually resolve into a value - at which time the ``sendQuery`` ``Promise`` resolves with that value.
|
||||
|
||||
The ``sendQuery`` method arguments follow the same conventions as ``sendAsyncMessage``, with the second argument being a structured clone, and the third being for `Transferables`_.
|
||||
|
||||
``receiveMessage``
|
||||
``````````````````
|
||||
|
||||
This is identical to the Message Manager implementation of ``receiveMessage``. The method receives a single argument, which is the de-serialized arguments that were sent via either ``sendAsyncMessage`` or ``sendQuery``. Note that upon handling a ``sendQuery`` message, the ``receiveMessage`` handler must return a ``Promise`` for that message.
|
||||
|
||||
.. hint::
|
||||
Using ``sendQuery``, and the ``receiveMessage`` is able to return a value right away? Try using ``Promise.resolve(value);`` to return ``value``, or you could also make your ``receiveMessage`` method an async function, presuming none of the other messages it handles need to get a non-Promise return value.
|
||||
|
||||
Other JSWindowActor methods that can be overridden
|
||||
--------------------------------------------------
|
||||
|
||||
``constructor()``
|
||||
|
||||
If there's something you need to do as soon as the JSWindowActor is instantiated, the ``constructor`` function is a great place to do that.
|
||||
|
||||
``observe(subject, topic, data)``
|
||||
`````````````````````````````````
|
||||
|
||||
If you register your Actor to listen for ``nsIObserver`` notifications, implement an ``observe`` method with the above signature to handle the notification.
|
||||
|
||||
``handleEvent(event)``
|
||||
``````````````````````
|
||||
|
||||
If you register your Actor to listen for content events, implement a ``handleEvent`` method with the above signature to handle the event.
|
||||
|
||||
``willDestroy``
|
||||
```````````````
|
||||
|
||||
This method will be called when we know that the JSWindowActor pair is going to be destroyed because the associated BrowsingContext is going away. You should override this method if you have any cleanup you need to do before going away.
|
||||
|
||||
You can also use ``willDestroy`` as a last opportunity to send messages to the other side, as the communications channel at this point is still running.
|
||||
|
||||
.. note::
|
||||
This method cannot be async.
|
||||
|
||||
``didDestroy``
|
||||
``````````````
|
||||
|
||||
This is another point to clean-up an Actor before it is destroyed, but at this point, no communication is possible with the other side.
|
||||
|
||||
.. note::
|
||||
This method cannot be async.
|
||||
|
||||
|
||||
Other things exposed on a JSWindowActorParent
|
||||
---------------------------------------------
|
||||
|
||||
``CanonicalBrowsingContext``
|
||||
````````````````````````````
|
||||
|
||||
TODO
|
||||
|
||||
``WindowGlobalParent``
|
||||
``````````````````````
|
||||
|
||||
TODO
|
||||
|
||||
Other things exposed on a JSWindowActorChild
|
||||
--------------------------------------------
|
||||
|
||||
``BrowsingContext``
|
||||
```````````````````
|
||||
|
||||
TODO
|
||||
|
||||
``WindowGlobalChild``
|
||||
`````````````````````
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
Helpful getters
|
||||
```````````````
|
||||
|
||||
A number of helpful getters exist on a ``JSWindowActorChild``, including:
|
||||
|
||||
``this.document``
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The currently loaded document in the frame associated with this ``JSWindowActorChild``.
|
||||
|
||||
``this.contentWindow``
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The outer window for the frame associated with this ``JSWindowActorChild``.
|
||||
|
||||
``this.docShell``
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``nsIDocShell`` for the frame associated with this ``JSWindowActorChild``.
|
||||
|
||||
See `JSWindowActor.webidl`_ for more detail on exactly what is exposed on both ``JSWindowActorParent`` and ``JSWindowActorChild`` implementations.
|
||||
|
||||
How to port from message manager and framescripts to JS Window Actors
|
||||
---------------------------------------------------------------------
|
||||
|
||||
TBD
|
||||
.. _fission.message-manager-actors:
|
||||
|
||||
Example
|
||||
-------
|
||||
Message Manager Actors
|
||||
``````````````````````
|
||||
|
||||
While the JSWindowActor mechanism was being designed and developed, large sections of our framescripts were converted to an "actor style" pattern to make eventual porting to JSWindowActors easier. These Actors use the Message Manager under the hood, but made it much easier to shrink our framescripts, and also allowed us to gain significant memory savings by having the actors be lazily instantiated.
|
||||
|
||||
You can find the list of Message Manager Actors (or "Legacy Actors") in `BrowserGlue.jsm <https://searchfox.org/mozilla-central/source/browser/components/BrowserGlue.jsm>`_ and `ActorManagerParent.jsm <https://searchfox.org/mozilla-central/source/toolkit/modules/ActorManagerParent.jsm>`_, in the ``LEGACY_ACTORS`` lists.
|
||||
|
||||
.. note::
|
||||
The split in Message Manager Actors defined between ``BrowserGlue`` and ``ActorManagerParent`` is mainly to keep Firefox Desktop specific Actors separate from Actors that can (in theory) be instantiated for non-Desktop browsers (like Fennec and GeckoView-based browsers). Firefox Desktop-specific Actors should be registered in ``BrowserGlue``. Shared "toolkit" Actors should go into ``ActorManagerParent``.
|
||||
|
||||
"Porting" these Actors often means doing what is necessary in order to move their registration entries from ``LEGACY_ACTORS`` to the ``ACTORS`` list.
|
||||
|
||||
Figuring out the lifetime of a new Actor pair
|
||||
`````````````````````````````````````````````
|
||||
|
||||
In the old model, framescript were loaded and executed as soon as possible by the top-level frame. In the JSWindowActor model, the Actors are much lazier, and only instantiate when:
|
||||
|
||||
1. They're instantiated explicitly by calling ``getActor`` on a ``WindowGlobal``, and passing in the name of the Actor.
|
||||
2. A message is sent to them.
|
||||
3. A pre-defined ``nsIObserver`` observer notification fires
|
||||
4. A pre-defined content Event fires
|
||||
|
||||
Making the Actors lazy like this saves on processing time to get a frame ready to load web pages, as well as the overhead of loading the Actor into memory.
|
||||
|
||||
When porting a framescript to JSWindowActors, often the first question to ask is: what's the entrypoint? At what point should the Actors instantiate and become active?
|
||||
|
||||
For example, when porting the content area context menu for Firefox, it was noted that the ``contextmenu`` event firing in content was a natural event to wait for to instantiate the Actor pair. Once the ``ContextMenuChild`` instantiated, the ``handleEvent`` method was used to inspect the event and prepare a message to be sent to the ``ContextMenuParent``. This example can be found by looking at the patch for the `Context Menu Fission Port`_.
|
||||
|
||||
.. _fission.registering-a-new-jswindowactor:
|
||||
|
||||
Registering a new JSWindowActor
|
||||
```````````````````````````````
|
||||
|
||||
``ChromeUtils`` exposes an API for registering window actors, but both ``BrowserGlue`` and ``ActorManagerParent`` are the main entry points where the registration occurs. If you want to register an actor, you should put them in one of the ``ACTORS`` lists in one of those two files. See :ref:`fission.message-manager-actors` for details.
|
||||
|
||||
The ``ACTORS`` lists expect a key-value pair, where the key is the name of the actor pair (example: ``ContextMenu``), and the value is an ``Object`` of registration parameters.
|
||||
|
||||
The full list of registration parameters can be found in the `JSWindowActor.webidl`_ file as ``WindowActorOptions``, ``WindowActorSidedOptions`` and ``WindowActorChildOptions``.
|
||||
|
||||
Here's an example JSWindowActor registration pulled from ``BrowserGlue.jsm``:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
Plugin: {
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/PluginParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource:///actors/PluginChild.jsm",
|
||||
events: {
|
||||
PluginBindingAttached: { capture: true, wantUntrusted: true },
|
||||
PluginCrashed: { capture: true },
|
||||
PluginOutdated: { capture: true },
|
||||
PluginInstantiated: { capture: true },
|
||||
PluginRemoved: { capture: true },
|
||||
HiddenPlugin: { capture: true },
|
||||
},
|
||||
|
||||
observers: ["decoder-doctor-notification"],
|
||||
},
|
||||
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
This example is for the JSWindowActor implementation of click-to-play for Flash.
|
||||
|
||||
Let's examine the first chunk:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
parent: {
|
||||
moduleURI: "resource:///actors/PluginParent.jsm",
|
||||
},
|
||||
|
||||
Here, we're declaring that the ``PluginParent`` subclassing ``JSWindowActorParent`` will be defined and exported inside the ``PluginParent.jsm`` file. That's all we have to say for the parent (main process) side of things.
|
||||
|
||||
.. note::
|
||||
It's not sufficient to just add a new .jsm file to the actors subdirectories. You also need to update the ``moz.build`` files in the same directory to get the ``resource://`` linkages set up correctly.
|
||||
|
||||
Let's look at the second chunk:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
child: {
|
||||
moduleURI: "resource:///actors/PluginChild.jsm",
|
||||
events: {
|
||||
PluginBindingAttached: { capture: true, wantUntrusted: true },
|
||||
PluginCrashed: { capture: true },
|
||||
PluginOutdated: { capture: true },
|
||||
PluginInstantiated: { capture: true },
|
||||
PluginRemoved: { capture: true },
|
||||
HiddenPlugin: { capture: true },
|
||||
},
|
||||
|
||||
observers: ["decoder-doctor-notification"],
|
||||
},
|
||||
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
We're similarly declaring where the ``PluginChild`` subclassing ``JSWindowActorChild`` can be found.
|
||||
|
||||
Next, we declare the content events, if fired in a BrowsingContext, will cause the JSWindowActor pair to instantiate if it doesn't already exist, and then have ``handleEvent`` called on the ``PluginChild`` instance. For each event name, an Object of event listener options can be passed. You can use the same event listener options as accepted by ``addEventListener``.
|
||||
|
||||
Next, we declare that ``PluginChild`` should observe the ``decoder-doctor-notification`` ``nsIObserver`` notification. When that observer notification fires, the ``PluginChild`` will be instantiated for all ``BrowsingContext``'s, and the ``observe`` method on the ``PluginChild`` implementation will be called.
|
||||
|
||||
Finally, we say that the ``PluginChild`` actor should apply to ``allFrames``. This means that the ``PluginChild`` is allowed to be loaded in any subframe. If ``allFrames`` is set to false, the actor will only ever load in the top-level frame.
|
||||
|
||||
Using ContentDOMReference instead of CPOWs
|
||||
``````````````````````````````````````````
|
||||
|
||||
Despite being outlawed as a way of synchronously accessing the properties of objects in other processes, CPOWs ended up being useful as a way of passing handles for DOM elements between processes.
|
||||
|
||||
CPOW messages, however, cannot be sent over the JSWindowActor communications pipe, so this handy mechanism will no longer work.
|
||||
|
||||
Instead, a new module called `ContentDOMReference.jsm`_ has been created which supplies the same capability. See that file for documentation.
|
||||
|
||||
How to start porting parent-process browser code to use JSWindowActors
|
||||
``````````````````````````````````````````````````````````````````````
|
||||
|
||||
The :ref:`fission.message-manager-actors` work made it much easier to migrate away from framescripts towards something that is similar to ``JSWindowActors``. It did not, however, substantially change how the parent process interacted with those framescripts.
|
||||
|
||||
So when porting code to work with ``JSWindowActors``, we find that this is often where the time goes - refactoring the parent process browser code to accomodate the new ``JSWindowActor`` model.
|
||||
|
||||
Usually, the first thing to do is to find a reasonable name for your actor pair, and get them registered (see :ref:`fission.registering-a-new-jswindowactor`), even if the actors implementations themselves are nothing but unmodified subclasses of ``JSWindowActorParent`` and ``JSWindowActorChild``.
|
||||
|
||||
Next, it's often helpful to find and note all of the places where ``sendAsyncMessage`` is being used to send messages through the old message manager interface for the component you're porting, and where any messages listeners are defined.
|
||||
|
||||
Let's look at a hypothetical example. Suppose we're porting part of the Page Info dialog, which scans each frame for useful information to display in the dialog. Given a chunk of code like this:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// This is some hypothetical Page Info dialog code.
|
||||
|
||||
let mm = browser.messageManager;
|
||||
mm.sendAsyncMessage("PageInfo:getInfoFromAllFrames", { someArgument: 123 });
|
||||
|
||||
// ... and then later on
|
||||
|
||||
mm.addMessageListener("PageInfo:info", async function onmessage(message) {
|
||||
// ...
|
||||
});
|
||||
|
||||
If a ``PageInfo`` pair of ``JSWindowActor``'s is registered, it might be tempting to simply replace the first part with:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
let actor = browser.browsingContext.currentWindowGlobal.getActor("PageInfo");
|
||||
actor.sendAsyncMessage("PageInfo:getInfoFromAllFrames", { someArgument: 123 });
|
||||
|
||||
However, if any of the frames on the page are running in their own process, they're not going to receive that ``PageInfo:getInfoFromAllFrames`` message. Instead, in this case, we should walk the ``BrowsingContext`` tree, and instantiate a ``PageInfo`` actor for each global, and send one message each to get information for each frame. Perhaps something like this:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
let contextsToVisit = [browser.browsingContext];
|
||||
while (contextsToVisit.length) {
|
||||
let currentContext = contextsToVisit.pop();
|
||||
let global = currentContext.currentWindowGlobal;
|
||||
|
||||
if (!global) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let actor = global.getActor("PageInfo");
|
||||
actor.sendAsyncMessage("PageInfo:getInfoForFrame", { someArgument: 123 });
|
||||
|
||||
contextsToVisit.push(...currentContext.getChildren());
|
||||
}
|
||||
|
||||
The original ``"PageInfo:info"`` message listener will need to be updated, too. Any responses from the ``PageInfoChild`` actor will end up being passed to the ``receiveMessage`` method of the ``PageInfoParent`` actor. It will be necessary to pass that information along to the interested party (in this case, the dialog code which is showing the table of interesting Page Info).
|
||||
|
||||
It might be necessary to refactor or rearchitect the original senders and consumers of message manager messages in order to accommodate the ``JSWindowActor`` model. Sometimes it's also helpful to have a singleton management object that manages all ``JSWindowActorParent`` instances and does something with their results. See ``PermitUnloader`` inside the implementation of `BrowserElementParent.jsm`_ for example.
|
||||
|
||||
Where to store state
|
||||
````````````````````
|
||||
|
||||
It's not a good idea to store any state within a ``JSWindowActorChild`` that you want to last beyond the lifetime of its ``BrowsingContext``. An out-of-process ``<iframe>`` can be closed at any time, and if it's the only one for a particular content process, that content process will soon be shut down, and any state you may have stored there will go away.
|
||||
|
||||
Your best bet for storing state is in the parent process.
|
||||
|
||||
.. hint::
|
||||
If each individual frame needs state, consider using a ``WeakMap`` in the parent process, mapping ``CanonicalBrowsingContext``'s with that state. That way, if the associates frames ever go away, you don't have to do any cleaning up yourself.
|
||||
|
||||
If you have state that you want multiple ``JSWindowActorParent``'s to have access to, consider having a "manager" of those ``JSWindowActorParent``'s inside of the same .jsm file to hold that state. See ``PermitUnloader`` inside the implementation of `BrowserElementParent.jsm`_ for example.
|
||||
|
||||
Minimal Example Actors
|
||||
----------------------
|
||||
|
||||
**Define an Actor**
|
||||
|
||||
|
@ -89,30 +430,6 @@ Example
|
|||
}
|
||||
|
||||
|
||||
**Registering an Actor**
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
ChromeUtils.registerWindowActor("Test", {
|
||||
parent: {
|
||||
moduleURI: "resource://testing-common/TestParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource://testing-common/TestChild.jsm",
|
||||
|
||||
events: {
|
||||
"mozshowdropdown": {},
|
||||
},
|
||||
|
||||
observers: [
|
||||
"test-js-window-actor-child-observer",
|
||||
],
|
||||
},
|
||||
|
||||
allFrames: true,
|
||||
});
|
||||
|
||||
|
||||
**Get a JS window actor for a specific window**
|
||||
|
||||
.. code-block:: javascript
|
||||
|
@ -122,3 +439,11 @@ Example
|
|||
|
||||
// get child side actor
|
||||
let childActor = content.window.getWindowGlobalChild().getActor("Test");
|
||||
|
||||
.. _Electrolysis Project: https://wiki.mozilla.org/Electrolysis
|
||||
.. _IPC Actors: https://developer.mozilla.org/en-US/docs/Mozilla/IPDL/Tutorial
|
||||
.. _Transferables: https://developer.mozilla.org/en-US/docs/Web/API/Transferable
|
||||
.. _Context Menu Fission Port: https://hg.mozilla.org/mozilla-central/rev/adc60720b7b8
|
||||
.. _ContentDOMReference.jsm: https://searchfox.org/mozilla-central/source/toolkit/modules/ContentDOMReference.jsm
|
||||
.. _JSWindowActor.webidl: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/JSWindowActor.webidl
|
||||
.. _BrowserElementParent.jsm: https://searchfox.org/mozilla-central/rev/ec806131cb7bcd1c26c254d25cd5ab8a61b2aeb6/toolkit/actors/BrowserElementParent.jsm
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
#include "mozilla/dom/WheelEventBinding.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_layout.h"
|
||||
#include "mozilla/StaticPrefs_mousewheel.h"
|
||||
#include "mozilla/StaticPrefs_ui.h"
|
||||
#include "mozilla/StaticPrefs_zoom.h"
|
||||
|
||||
#include "ContentEventHandler.h"
|
||||
#include "IMEContentObserver.h"
|
||||
|
@ -250,7 +252,6 @@ EventStateManager::EventStateManager()
|
|||
UpdateUserActivityTimer();
|
||||
}
|
||||
++sESMInstanceCount;
|
||||
WheelTransaction::InitializeStatics();
|
||||
}
|
||||
|
||||
nsresult EventStateManager::UpdateUserActivityTimer() {
|
||||
|
@ -2127,8 +2128,8 @@ nsresult EventStateManager::ChangeTextSize(int32_t change) {
|
|||
|
||||
if (cv) {
|
||||
float textzoom;
|
||||
float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
|
||||
float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
|
||||
float zoomMin = ((float)StaticPrefs::zoom_minPercent()) / 100;
|
||||
float zoomMax = ((float)StaticPrefs::zoom_maxPercent()) / 100;
|
||||
cv->GetTextZoom(&textzoom);
|
||||
textzoom += ((float)change) / 10;
|
||||
if (textzoom < zoomMin)
|
||||
|
@ -2148,8 +2149,8 @@ nsresult EventStateManager::ChangeFullZoom(int32_t change) {
|
|||
|
||||
if (cv) {
|
||||
float fullzoom;
|
||||
float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
|
||||
float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
|
||||
float zoomMin = ((float)StaticPrefs::zoom_minPercent()) / 100;
|
||||
float zoomMax = ((float)StaticPrefs::zoom_maxPercent()) / 100;
|
||||
cv->GetFullZoom(&fullzoom);
|
||||
fullzoom += ((float)change) / 10;
|
||||
if (fullzoom < zoomMin)
|
||||
|
@ -5840,7 +5841,8 @@ void EventStateManager::DeltaAccumulator::InitLineOrPageDelta(
|
|||
// Reset if the previous wheel event is too old.
|
||||
if (!mLastTime.IsNull()) {
|
||||
TimeDuration duration = TimeStamp::Now() - mLastTime;
|
||||
if (duration.ToMilliseconds() > WheelTransaction::GetTimeoutTime()) {
|
||||
if (duration.ToMilliseconds() >
|
||||
StaticPrefs::mousewheel_transaction_timeout()) {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StaticPrefs_mousewheel.h"
|
||||
#include "mozilla/StaticPrefs_test.h"
|
||||
#include "mozilla/dom/WheelEventBinding.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -223,7 +225,7 @@ void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (OutOfTime(sTime, GetTimeoutTime())) {
|
||||
if (OutOfTime(sTime, StaticPrefs::mousewheel_transaction_timeout())) {
|
||||
// Even if the scroll event which is handled after timeout, but onTimeout
|
||||
// was not fired by timer, then the scroll event will scroll old frame,
|
||||
// therefore, we should call OnTimeout here and ensure to finish the old
|
||||
|
@ -235,7 +237,8 @@ void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
|
|||
switch (aEvent->mMessage) {
|
||||
case eWheel:
|
||||
if (sMouseMoved != 0 &&
|
||||
OutOfTime(sMouseMoved, GetIgnoreMoveDelayTime())) {
|
||||
OutOfTime(sMouseMoved,
|
||||
StaticPrefs::mousewheel_transaction_ignoremovedelay())) {
|
||||
// Terminate the current mousewheel transaction if the mouse moved more
|
||||
// than ignoremovedelay milliseconds ago
|
||||
EndTransaction();
|
||||
|
@ -260,7 +263,9 @@ void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
|
|||
// ignoremovedelay milliseconds since the last scroll operation, ignore
|
||||
// the mouse move; otherwise, record the current mouse move time to be
|
||||
// checked later
|
||||
if (!sMouseMoved && OutOfTime(sTime, GetIgnoreMoveDelayTime())) {
|
||||
if (!sMouseMoved &&
|
||||
OutOfTime(sTime,
|
||||
StaticPrefs::mousewheel_transaction_ignoremovedelay())) {
|
||||
sMouseMoved = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +295,7 @@ void WheelTransaction::Shutdown() { NS_IF_RELEASE(sTimer); }
|
|||
void WheelTransaction::OnFailToScrollTarget() {
|
||||
MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction");
|
||||
|
||||
if (Prefs::sTestMouseScroll) {
|
||||
if (StaticPrefs::test_mousescroll()) {
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
sTargetFrame->GetContent()->OwnerDoc(), sTargetFrame->GetContent(),
|
||||
|
@ -317,7 +322,7 @@ void WheelTransaction::OnTimeout(nsITimer* aTimer, void* aClosure) {
|
|||
// the next DOM event might create strange situation for us.
|
||||
MayEndTransaction();
|
||||
|
||||
if (Prefs::sTestMouseScroll) {
|
||||
if (StaticPrefs::test_mousescroll()) {
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
frame->GetContent()->OwnerDoc(), frame->GetContent(),
|
||||
|
@ -336,8 +341,8 @@ void WheelTransaction::SetTimeout() {
|
|||
}
|
||||
sTimer->Cancel();
|
||||
DebugOnly<nsresult> rv = sTimer->InitWithNamedFuncCallback(
|
||||
OnTimeout, nullptr, GetTimeoutTime(), nsITimer::TYPE_ONE_SHOT,
|
||||
"WheelTransaction::SetTimeout");
|
||||
OnTimeout, nullptr, StaticPrefs::mousewheel_transaction_timeout(),
|
||||
nsITimer::TYPE_ONE_SHOT, "WheelTransaction::SetTimeout");
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"nsITimer::InitWithFuncCallback failed");
|
||||
}
|
||||
|
@ -364,9 +369,9 @@ DeltaValues WheelTransaction::AccelerateWheelDelta(
|
|||
}
|
||||
|
||||
// Accelerate by the sScrollSeriesCounter
|
||||
int32_t start = GetAccelerationStart();
|
||||
int32_t start = StaticPrefs::mousewheel_acceleration_start();
|
||||
if (start >= 0 && sScrollSeriesCounter >= start) {
|
||||
int32_t factor = GetAccelerationFactor();
|
||||
int32_t factor = StaticPrefs::mousewheel_acceleration_factor();
|
||||
if (factor > 0) {
|
||||
result.deltaX = ComputeAcceleratedWheelDelta(result.deltaX, factor);
|
||||
result.deltaY = ComputeAcceleratedWheelDelta(result.deltaY, factor);
|
||||
|
@ -518,33 +523,6 @@ void ScrollbarsForWheel::DeactivateAllTemporarilyActivatedScrollTargets() {
|
|||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* mozilla::WheelTransaction::Prefs */
|
||||
/******************************************************************/
|
||||
|
||||
int32_t WheelTransaction::Prefs::sMouseWheelAccelerationStart = -1;
|
||||
int32_t WheelTransaction::Prefs::sMouseWheelAccelerationFactor = -1;
|
||||
uint32_t WheelTransaction::Prefs::sMouseWheelTransactionTimeout = 1500;
|
||||
uint32_t WheelTransaction::Prefs::sMouseWheelTransactionIgnoreMoveDelay = 100;
|
||||
bool WheelTransaction::Prefs::sTestMouseScroll = false;
|
||||
|
||||
/* static */
|
||||
void WheelTransaction::Prefs::InitializeStatics() {
|
||||
static bool sIsInitialized = false;
|
||||
if (!sIsInitialized) {
|
||||
Preferences::AddIntVarCache(&sMouseWheelAccelerationStart,
|
||||
"mousewheel.acceleration.start", -1);
|
||||
Preferences::AddIntVarCache(&sMouseWheelAccelerationFactor,
|
||||
"mousewheel.acceleration.factor", -1);
|
||||
Preferences::AddUintVarCache(&sMouseWheelTransactionTimeout,
|
||||
"mousewheel.transaction.timeout", 1500);
|
||||
Preferences::AddUintVarCache(&sMouseWheelTransactionIgnoreMoveDelay,
|
||||
"mousewheel.transaction.ignoremovedelay", 100);
|
||||
Preferences::AddBoolVarCache(&sTestMouseScroll, "test.mousescroll", false);
|
||||
sIsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* mozilla::WheelDeltaHorizontalizer */
|
||||
/******************************************************************/
|
||||
|
|
|
@ -135,15 +135,11 @@ class WheelTransaction {
|
|||
}
|
||||
static void OnEvent(WidgetEvent* aEvent);
|
||||
static void Shutdown();
|
||||
static uint32_t GetTimeoutTime() {
|
||||
return Prefs::sMouseWheelTransactionTimeout;
|
||||
}
|
||||
|
||||
static void OwnScrollbars(bool aOwn);
|
||||
|
||||
static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent,
|
||||
bool aAllowScrollSpeedOverride);
|
||||
static void InitializeStatics() { Prefs::InitializeStatics(); }
|
||||
|
||||
protected:
|
||||
static void BeginTransaction(nsIFrame* aTargetFrame,
|
||||
|
@ -157,15 +153,6 @@ class WheelTransaction {
|
|||
static void OnFailToScrollTarget();
|
||||
static void OnTimeout(nsITimer* aTimer, void* aClosure);
|
||||
static void SetTimeout();
|
||||
static uint32_t GetIgnoreMoveDelayTime() {
|
||||
return Prefs::sMouseWheelTransactionIgnoreMoveDelay;
|
||||
}
|
||||
static int32_t GetAccelerationStart() {
|
||||
return Prefs::sMouseWheelAccelerationStart;
|
||||
}
|
||||
static int32_t GetAccelerationFactor() {
|
||||
return Prefs::sMouseWheelAccelerationFactor;
|
||||
}
|
||||
static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
|
||||
static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
|
||||
static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
|
||||
|
@ -176,16 +163,6 @@ class WheelTransaction {
|
|||
static nsITimer* sTimer;
|
||||
static int32_t sScrollSeriesCounter;
|
||||
static bool sOwnScrollbars;
|
||||
|
||||
class Prefs {
|
||||
public:
|
||||
static void InitializeStatics();
|
||||
static int32_t sMouseWheelAccelerationStart;
|
||||
static int32_t sMouseWheelAccelerationFactor;
|
||||
static uint32_t sMouseWheelTransactionTimeout;
|
||||
static uint32_t sMouseWheelTransactionIgnoreMoveDelay;
|
||||
static bool sTestMouseScroll;
|
||||
};
|
||||
};
|
||||
|
||||
// For some kinds of scrollings, the delta values of WidgetWheelEvent are
|
||||
|
|
|
@ -278,7 +278,7 @@ var W3CTest = {
|
|||
* Timeout the current test. Intended to be used from harness code, not
|
||||
* from tests.
|
||||
*/
|
||||
"timeout": function() {
|
||||
"timeout": async function() {
|
||||
this.logFailure("Timeout", "Test runner timed us out.");
|
||||
timeout();
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ XPIDL_SOURCES += [
|
|||
'nsIBrowser.idl',
|
||||
'nsIBrowserChild.idl',
|
||||
'nsIBrowserDOMWindow.idl',
|
||||
'nsIBrowserUsage.idl',
|
||||
'nsIContentPermissionPrompt.idl',
|
||||
'nsIContentPrefService2.idl',
|
||||
'nsIContentProcess.idl',
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/* 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 "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(2703b5ed-a41f-42be-8764-b795eb67ed25)]
|
||||
interface nsIBrowserUsage : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns the number of unique domains (eTLD+1) visited in the past
|
||||
* 24 hours by the user.
|
||||
*/
|
||||
uint32_t getUniqueDomainsVisitedInPast24Hours();
|
||||
};
|
|
@ -5706,6 +5706,13 @@ mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvAutomaticStorageAccessCanBeGranted(
|
||||
const Principal& aPrincipal,
|
||||
AutomaticStorageAccessCanBeGrantedResolver&& aResolver) {
|
||||
aResolver(Document::AutomaticStorageAccessCanBeGranted(aPrincipal));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(
|
||||
const Principal& aParentPrincipal, const Principal& aTrackingPrincipal,
|
||||
|
|
|
@ -1154,6 +1154,10 @@ class ContentParent final : public PContentParent,
|
|||
|
||||
mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aHangDetails);
|
||||
|
||||
mozilla::ipc::IPCResult RecvAutomaticStorageAccessCanBeGranted(
|
||||
const Principal& aPrincipal,
|
||||
AutomaticStorageAccessCanBeGrantedResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvFirstPartyStorageAccessGrantedForOrigin(
|
||||
const Principal& aParentPrincipal, const Principal& aTrackingPrincipal,
|
||||
const nsCString& aTrackingOrigin, const nsCString& aGrantedOrigin,
|
||||
|
|
|
@ -6,146 +6,41 @@
|
|||
|
||||
#include "MemMapSnapshot.h"
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/file_util.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/AutoMemMap.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/ipc/FileDescriptorUtils.h"
|
||||
#include "nsIFile.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include "mozilla/RandomNum.h"
|
||||
# include "mozilla/WindowsVersion.h"
|
||||
# include "nsDebug.h"
|
||||
# include "nsString.h"
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef XP_UNIX
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using loader::AutoMemMap;
|
||||
|
||||
namespace ipc {
|
||||
|
||||
Result<Ok, nsresult> MemMapSnapshot::Init(size_t aSize) {
|
||||
MOZ_ASSERT(!mInitialized);
|
||||
|
||||
MOZ_TRY(Create(aSize));
|
||||
if (NS_WARN_IF(!mMem.CreateFreezeable(aSize))) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (NS_WARN_IF(!mMem.Map(aSize))) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> MemMapSnapshot::Finalize(AutoMemMap& aMem) {
|
||||
Result<Ok, nsresult> MemMapSnapshot::Finalize(loader::AutoMemMap& aMem) {
|
||||
MOZ_ASSERT(mInitialized);
|
||||
|
||||
MOZ_TRY(Freeze(aMem));
|
||||
if (NS_WARN_IF(!mMem.Freeze())) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
// TakeHandle resets mMem, so call max_size first.
|
||||
size_t size = mMem.max_size();
|
||||
FileDescriptor memHandle(mMem.TakeHandle());
|
||||
MOZ_TRY(aMem.initWithHandle(memHandle, size));
|
||||
|
||||
mInitialized = false;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
Result<Ok, nsresult> MemMapSnapshot::Create(size_t aSize) {
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
ACL dacl;
|
||||
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = &sd;
|
||||
sa.bInheritHandle = FALSE;
|
||||
|
||||
if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
|
||||
NS_WARN_IF(
|
||||
!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) ||
|
||||
NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
nsAutoStringN<sizeof("MozSharedMem_") + 16 * 4> name;
|
||||
if (!IsWin8Point1OrLater()) {
|
||||
name.AssignLiteral("MozSharedMem_");
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
Maybe<uint64_t> randomNum = RandomUint64();
|
||||
if (NS_WARN_IF(randomNum.isNothing())) {
|
||||
return Err(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
name.AppendPrintf("%016llx", *randomNum);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE handle =
|
||||
CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
|
||||
DWORD(aSize), name.IsEmpty() ? nullptr : name.get());
|
||||
|
||||
if (!handle) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mFile.emplace(handle);
|
||||
return mMem.initWithHandle(mFile.ref(), aSize, PR_PROT_READWRITE);
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> MemMapSnapshot::Freeze(AutoMemMap& aMem) {
|
||||
auto orig = mFile.ref().ClonePlatformHandle();
|
||||
mFile.reset();
|
||||
|
||||
HANDLE handle;
|
||||
if (!::DuplicateHandle(
|
||||
GetCurrentProcess(), orig.release(), GetCurrentProcess(), &handle,
|
||||
GENERIC_READ | FILE_MAP_READ, false, DUPLICATE_CLOSE_SOURCE)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return aMem.initWithHandle(FileDescriptor(handle), mMem.size());
|
||||
}
|
||||
|
||||
#elif defined(XP_UNIX)
|
||||
|
||||
Result<Ok, nsresult> MemMapSnapshot::Create(size_t aSize) {
|
||||
FilePath path;
|
||||
ScopedCloseFile fd(file_util::CreateAndOpenTemporaryShmemFile(&path));
|
||||
if (!fd) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (HANDLE_EINTR(ftruncate(fileno(fd), aSize)) != 0) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
MOZ_TRY(mMem.init(FILEToFileDescriptor(fd), PR_PROT_READWRITE));
|
||||
|
||||
mPath.Assign(path.value().data(), path.value().length());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> MemMapSnapshot::Freeze(AutoMemMap& aMem) {
|
||||
// Delete the shm file after we're done here, whether we succeed or not. The
|
||||
// open file descriptor will keep it alive until all remaining references
|
||||
// are closed, at which point it will be automatically freed.
|
||||
auto cleanup = MakeScopeExit([&]() { PR_Delete(mPath.get()); });
|
||||
|
||||
// Make the shm file readonly. This doesn't make a difference in practice,
|
||||
// since we open and share a read-only file descriptor, and then delete the
|
||||
// file. But it doesn't hurt, either.
|
||||
chmod(mPath.get(), 0400);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
MOZ_TRY(NS_NewNativeLocalFile(mPath, /* followLinks = */ false,
|
||||
getter_AddRefs(file)));
|
||||
|
||||
return aMem.init(file);
|
||||
}
|
||||
|
||||
#else
|
||||
# error "Unsupported build configuration"
|
||||
#endif
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,16 +7,17 @@
|
|||
#ifndef dom_ipc_MemMapSnapshot_h
|
||||
#define dom_ipc_MemMapSnapshot_h
|
||||
|
||||
#include "mozilla/AutoMemMap.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RangedPtr.h"
|
||||
#include "mozilla/Result.h"
|
||||
#ifdef XP_WIN
|
||||
# include "mozilla/ipc/FileDescriptor.h"
|
||||
#endif
|
||||
#include "base/shared_memory.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
class AutoMemMap;
|
||||
}
|
||||
|
||||
namespace ipc {
|
||||
|
||||
/**
|
||||
|
@ -35,25 +36,15 @@ class MOZ_RAII MemMapSnapshot {
|
|||
Result<Ok, nsresult> Init(size_t aSize);
|
||||
Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
|
||||
|
||||
template <typename T = void>
|
||||
template <typename T>
|
||||
RangedPtr<T> Get() {
|
||||
MOZ_ASSERT(mInitialized);
|
||||
return mMem.get<T>();
|
||||
return {static_cast<T*>(mMem.memory()), mMem.max_size() / sizeof(T)};
|
||||
}
|
||||
|
||||
private:
|
||||
Result<Ok, nsresult> Create(size_t aSize);
|
||||
Result<Ok, nsresult> Freeze(loader::AutoMemMap& aMem);
|
||||
|
||||
loader::AutoMemMap mMem;
|
||||
|
||||
base::SharedMemory mMem;
|
||||
bool mInitialized = false;
|
||||
|
||||
#ifdef XP_WIN
|
||||
Maybe<FileDescriptor> mFile;
|
||||
#else
|
||||
nsCString mPath;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
|
|
|
@ -1378,6 +1378,13 @@ parent:
|
|||
|
||||
async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics);
|
||||
|
||||
/*
|
||||
* Determines whether storage access can be granted automatically by the
|
||||
* storage access API without showing a user prompt.
|
||||
*/
|
||||
async AutomaticStorageAccessCanBeGranted(Principal aPrincipal)
|
||||
returns (bool success);
|
||||
|
||||
/*
|
||||
* A 3rd party tracking origin (aTrackingOrigin) has received the permission
|
||||
* granted to have access to aGrantedOrigin when loaded by aParentPrincipal.
|
||||
|
|
|
@ -21,6 +21,8 @@ using namespace ipc;
|
|||
namespace dom {
|
||||
namespace ipc {
|
||||
|
||||
static constexpr uint32_t kSharedStringMapMagic = 0x9e3779b9;
|
||||
|
||||
static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) {
|
||||
auto mod = aOffset % aAlign;
|
||||
return mod ? aAlign - mod : 0;
|
||||
|
@ -30,6 +32,7 @@ SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile,
|
|||
size_t aMapSize) {
|
||||
auto result = mMap.initWithHandle(aMapFile, aMapSize);
|
||||
MOZ_RELEASE_ASSERT(result.isOk());
|
||||
MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
|
||||
// We return literal nsStrings and nsCStrings pointing to the mapped data,
|
||||
// which means that we may still have references to the mapped data even
|
||||
// after this instance is destroyed. That means that we need to keep the
|
||||
|
@ -40,6 +43,7 @@ SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile,
|
|||
SharedStringMap::SharedStringMap(SharedStringMapBuilder&& aBuilder) {
|
||||
auto result = aBuilder.Finalize(mMap);
|
||||
MOZ_RELEASE_ASSERT(result.isOk());
|
||||
MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
|
||||
mMap.setPersistent();
|
||||
}
|
||||
|
||||
|
@ -92,7 +96,7 @@ Result<Ok, nsresult> SharedStringMapBuilder::Finalize(
|
|||
}
|
||||
keys.Sort();
|
||||
|
||||
Header header = {uint32_t(keys.Length())};
|
||||
Header header = {kSharedStringMapMagic, uint32_t(keys.Length())};
|
||||
|
||||
size_t offset = sizeof(header);
|
||||
offset += GetAlignmentOffset(offset, alignof(Header));
|
||||
|
|
|
@ -59,6 +59,7 @@ class SharedStringMap {
|
|||
* encoded as character (*not* byte) offsets into this region.
|
||||
*/
|
||||
struct Header {
|
||||
uint32_t mMagic;
|
||||
// The number of entries in this map.
|
||||
uint32_t mEntryCount;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ child:
|
|||
|
||||
// cdm::ContentDecryptionModule9+10
|
||||
async Init(bool aAllowDistinctiveIdentifier,
|
||||
bool aAllowPersistentState) returns (bool unused);
|
||||
bool aAllowPersistentState) returns (bool aSuccess);
|
||||
|
||||
async GetStatusForPolicy(uint32_t aPromiseId,
|
||||
nsCString aMinHdcpVersion);
|
||||
|
|
|
@ -183,12 +183,12 @@ function fetchAndLoadAsync(sb, prefix, chunks, suffix) {
|
|||
}
|
||||
|
||||
// Register timeout function to dump debugging logs.
|
||||
SimpleTest.registerTimeoutFunction(function() {
|
||||
SimpleTest.registerTimeoutFunction(async function() {
|
||||
for (const v of document.getElementsByTagName("video")) {
|
||||
v.mozDumpDebugInfo();
|
||||
console.log(await SpecialPowers.wrap(v).mozRequestDebugInfo());
|
||||
}
|
||||
for (const a of document.getElementsByTagName("audio")) {
|
||||
a.mozDumpDebugInfo();
|
||||
console.log(await SpecialPowers.wrap(a).mozRequestDebugInfo());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ runWithMSE(async (ms, el) => {
|
|||
ok(true, "appendBuffer succeeded");
|
||||
} catch (ex) {
|
||||
ok(false, "Shouldn't throw another time when data can be evicted");
|
||||
el.mozDumpDebugInfo();
|
||||
dump(JSON.stringify(await SpecialPowers.wrap(el).mozRequestDebugInfo()));
|
||||
}
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
|
|
@ -61,9 +61,9 @@ runWithMSE(async function(ms, el) {
|
|||
await once(el, "seeked");
|
||||
dump("dump: seeked to " + seekTime);
|
||||
is(el.currentTime, seekTime, "correctly seeked to " + seekTime);
|
||||
await audiosb.appendBufferAsync(audioBuffer).catch(function(ex2) {
|
||||
await audiosb.appendBufferAsync(audioBuffer).catch(async function(ex2) {
|
||||
ok(false, "Shouldn't throw another time when data can be evicted");
|
||||
el.mozDumpDebugInfo();
|
||||
dump(JSON.stringify(await SpecialPowers.wrap(el).mozRequestDebugInfo()));
|
||||
SimpleTest.finish();
|
||||
});
|
||||
// Test that an error in remove return a rejected promise
|
||||
|
|
|
@ -477,8 +477,9 @@ class RemoteAudioDecoder : public RemoteDataDecoder {
|
|||
BufferInfo::LocalRef info = aSample->Info();
|
||||
MOZ_ASSERT(info);
|
||||
|
||||
int32_t flags;
|
||||
int32_t flags = 0;
|
||||
bool ok = NS_SUCCEEDED(info->Flags(&flags));
|
||||
bool isEOS = !!(flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM);
|
||||
|
||||
int32_t offset;
|
||||
ok &= NS_SUCCEEDED(info->Offset(&offset));
|
||||
|
@ -490,7 +491,8 @@ class RemoteAudioDecoder : public RemoteDataDecoder {
|
|||
ok &= NS_SUCCEEDED(info->Size(&size));
|
||||
|
||||
if (!ok ||
|
||||
IsSampleTimeSmallerThanFirstDemuxedSampleTime(presentationTimeUs)) {
|
||||
(IsSampleTimeSmallerThanFirstDemuxedSampleTime(presentationTimeUs) &&
|
||||
!isEOS)) {
|
||||
Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
|
||||
return;
|
||||
}
|
||||
|
@ -518,7 +520,7 @@ class RemoteAudioDecoder : public RemoteDataDecoder {
|
|||
UpdateOutputStatus(std::move(data));
|
||||
}
|
||||
|
||||
if ((flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||
if (isEOS) {
|
||||
DrainComplete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "AppleATDecoder.h"
|
||||
#include "AppleDecoderModule.h"
|
||||
#include "AppleVTDecoder.h"
|
||||
#include "mozilla/gfx/MacIOSurface.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
@ -27,11 +26,7 @@ void AppleDecoderModule::Init() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Ensure IOSurface framework is loaded.
|
||||
MacIOSurfaceLib::LoadLibrary();
|
||||
|
||||
sCanUseHardwareVideoDecoder =
|
||||
MacIOSurfaceLib::isInit() && gfx::gfxVars::CanUseHardwareVideoDecoding();
|
||||
sCanUseHardwareVideoDecoder = gfx::gfxVars::CanUseHardwareVideoDecoding();
|
||||
|
||||
sInitialized = true;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
#include "AppleVTDecoder.h"
|
||||
|
||||
#include <CoreVideo/CVPixelBufferIOSurface.h>
|
||||
#include <IOSurface/IOSurface.h>
|
||||
|
||||
#include "AppleDecoderModule.h"
|
||||
#include "AppleUtils.h"
|
||||
#include "MacIOSurfaceImage.h"
|
||||
|
@ -391,7 +394,7 @@ void AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage,
|
|||
CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
|
||||
} else {
|
||||
#ifndef MOZ_WIDGET_UIKIT
|
||||
IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
|
||||
IOSurfacePtr surface = (IOSurfacePtr)CVPixelBufferGetIOSurface(aImage);
|
||||
MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
|
||||
|
||||
RefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
|
||||
|
@ -557,7 +560,7 @@ CFDictionaryRef AppleVTDecoder::CreateOutputConfiguration() {
|
|||
AutoCFRelease<CFNumberRef> PixelFormatTypeNumber = CFNumberCreate(
|
||||
kCFAllocatorDefault, kCFNumberSInt32Type, &PixelFormatTypeValue);
|
||||
// Construct IOSurface Properties
|
||||
const void* IOSurfaceKeys[] = {MacIOSurfaceLib::kPropIsGlobal};
|
||||
const void* IOSurfaceKeys[] = {kIOSurfaceIsGlobal};
|
||||
const void* IOSurfaceValues[] = {kCFBooleanTrue};
|
||||
static_assert(ArrayLength(IOSurfaceKeys) == ArrayLength(IOSurfaceValues),
|
||||
"Non matching keys/values array size");
|
||||
|
|
|
@ -1857,24 +1857,16 @@ function mediaTestCleanup(callback) {
|
|||
async function dumpDebugInfoForToken(token) {
|
||||
for (let v of document.getElementsByTagName("video")) {
|
||||
if (token === v.token) {
|
||||
return v.mozDumpDebugInfo();
|
||||
info(JSON.stringify(await SpecialPowers.wrap(v).mozRequestDebugInfo()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (let a of document.getElementsByTagName("audio")) {
|
||||
if (token === a.token) {
|
||||
return a.mozDumpDebugInfo();
|
||||
info(JSON.stringify(await SpecialPowers.wrap(a).mozRequestDebugInfo()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function dumpDebugInfo() {
|
||||
for (var v of document.getElementsByTagName("video")) {
|
||||
v.mozDumpDebugInfo();
|
||||
}
|
||||
for (var a of document.getElementsByTagName("audio")) {
|
||||
a.mozDumpDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// Could be undefined in a page opened by the parent test page
|
||||
|
@ -1883,5 +1875,12 @@ if ("SimpleTest" in window) {
|
|||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
// Register timeout function to dump debugging logs.
|
||||
SimpleTest.registerTimeoutFunction(dumpDebugInfo);
|
||||
SimpleTest.registerTimeoutFunction(async function() {
|
||||
for (const v of document.getElementsByTagName("video")) {
|
||||
SimpleTest.info(JSON.stringify(await SpecialPowers.wrap(v).mozRequestDebugInfo()));
|
||||
}
|
||||
for (const a of document.getElementsByTagName("audio")) {
|
||||
SimpleTest.info(JSON.stringify(await SpecialPowers.wrap(a).mozRequestDebugInfo()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,279 +5,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MacIOSurface.h"
|
||||
#include <IOSurface/IOSurface.h>
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/CGLIOSurface.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
#include <dlfcn.h>
|
||||
#include "GLConsts.h"
|
||||
#include "GLContextCGL.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
using namespace mozilla;
|
||||
// IOSurface signatures
|
||||
#define IOSURFACE_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/IOSurface.framework/IOSurface"
|
||||
#define OPENGL_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/OpenGL.framework/OpenGL"
|
||||
#define COREGRAPHICS_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
|
||||
"CoreGraphics.framework/CoreGraphics"
|
||||
#define COREVIDEO_FRAMEWORK_PATH \
|
||||
"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
|
||||
"CoreVideo.framework/CoreVideo"
|
||||
|
||||
#define GET_CONST(const_name) \
|
||||
((CFStringRef*)dlsym(sIOSurfaceFramework, const_name))
|
||||
#define GET_IOSYM(dest, sym_name) \
|
||||
(typeof(dest)) dlsym(sIOSurfaceFramework, sym_name)
|
||||
#define GET_CGLSYM(dest, sym_name) \
|
||||
(typeof(dest)) dlsym(sOpenGLFramework, sym_name)
|
||||
#define GET_CGSYM(dest, sym_name) \
|
||||
(typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name)
|
||||
#define GET_CVSYM(dest, sym_name) \
|
||||
(typeof(dest)) dlsym(sCoreVideoFramework, sym_name)
|
||||
|
||||
MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader;
|
||||
bool MacIOSurfaceLib::isLoaded = false;
|
||||
void* MacIOSurfaceLib::sIOSurfaceFramework;
|
||||
void* MacIOSurfaceLib::sOpenGLFramework;
|
||||
void* MacIOSurfaceLib::sCoreGraphicsFramework;
|
||||
void* MacIOSurfaceLib::sCoreVideoFramework;
|
||||
IOSurfaceCreateFunc MacIOSurfaceLib::sCreate;
|
||||
IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID;
|
||||
IOSurfaceLookupFunc MacIOSurfaceLib::sLookup;
|
||||
IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress;
|
||||
IOSurfaceGetBaseAddressOfPlaneFunc MacIOSurfaceLib::sGetBaseAddressOfPlane;
|
||||
IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sWidth;
|
||||
IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sHeight;
|
||||
IOSurfaceSizeTFunc MacIOSurfaceLib::sPlaneCount;
|
||||
IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sBytesPerRow;
|
||||
IOSurfaceGetPropertyMaximumFunc MacIOSurfaceLib::sGetPropertyMaximum;
|
||||
IOSurfaceVoidFunc MacIOSurfaceLib::sIncrementUseCount;
|
||||
IOSurfaceVoidFunc MacIOSurfaceLib::sDecrementUseCount;
|
||||
IOSurfaceLockFunc MacIOSurfaceLib::sLock;
|
||||
IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock;
|
||||
CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage;
|
||||
IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate;
|
||||
IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage;
|
||||
IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface;
|
||||
CVPixelBufferGetIOSurfaceFunc MacIOSurfaceLib::sCVPixelBufferGetIOSurface;
|
||||
unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr)(CGContextRef) = nullptr;
|
||||
IOSurfacePixelFormatFunc MacIOSurfaceLib::sPixelFormat;
|
||||
|
||||
CFStringRef MacIOSurfaceLib::kPropWidth;
|
||||
CFStringRef MacIOSurfaceLib::kPropHeight;
|
||||
CFStringRef MacIOSurfaceLib::kPropBytesPerElem;
|
||||
CFStringRef MacIOSurfaceLib::kPropBytesPerRow;
|
||||
CFStringRef MacIOSurfaceLib::kPropIsGlobal;
|
||||
|
||||
bool MacIOSurfaceLib::isInit() {
|
||||
// Guard against trying to reload the library
|
||||
// if it is not available.
|
||||
if (!isLoaded) LoadLibrary();
|
||||
MOZ_ASSERT(sIOSurfaceFramework);
|
||||
return sIOSurfaceFramework;
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) {
|
||||
return sCreate(properties);
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) {
|
||||
return sLookup(aIOSurfaceID);
|
||||
}
|
||||
|
||||
IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sGetID(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sGetBaseAddress(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(
|
||||
IOSurfacePtr aIOSurfacePtr, size_t planeIndex) {
|
||||
return sGetBaseAddressOfPlane(aIOSurfacePtr, planeIndex);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sPlaneCount(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t plane) {
|
||||
return sWidth(aIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t plane) {
|
||||
return sHeight(aIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t plane) {
|
||||
return sBytesPerRow(aIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
|
||||
return sGetPropertyMaximum(property);
|
||||
}
|
||||
|
||||
OSType MacIOSurfaceLib::IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr) {
|
||||
return sPixelFormat(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
|
||||
uint32_t options, uint32_t* seed) {
|
||||
return sLock(aIOSurfacePtr, options, seed);
|
||||
}
|
||||
|
||||
IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
|
||||
uint32_t options, uint32_t* seed) {
|
||||
return sUnlock(aIOSurfacePtr, options, seed);
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr) {
|
||||
sIncrementUseCount(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr) {
|
||||
sDecrementUseCount(aIOSurfacePtr);
|
||||
}
|
||||
|
||||
CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(
|
||||
CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width,
|
||||
GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface,
|
||||
GLuint plane) {
|
||||
return sTexImage(ctxt, target, internalFormat, width, height, format, type,
|
||||
ioSurface, plane);
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::CVPixelBufferGetIOSurface(
|
||||
CVPixelBufferRef aPixelBuffer) {
|
||||
return sCVPixelBufferGetIOSurface(aPixelBuffer);
|
||||
}
|
||||
|
||||
CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(
|
||||
IOSurfacePtr aIOSurfacePtr, unsigned aWidth, unsigned aHeight,
|
||||
unsigned aBitsPerComponent, unsigned aBytes, CGColorSpaceRef aColorSpace,
|
||||
CGBitmapInfo bitmapInfo) {
|
||||
if (!sIOSurfaceContextCreate) return nullptr;
|
||||
return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight,
|
||||
aBitsPerComponent, aBytes, aColorSpace,
|
||||
bitmapInfo);
|
||||
}
|
||||
|
||||
CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) {
|
||||
if (!sIOSurfaceContextCreateImage) return nullptr;
|
||||
return sIOSurfaceContextCreateImage(aContext);
|
||||
}
|
||||
|
||||
IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(
|
||||
CGContextRef aContext) {
|
||||
if (!sIOSurfaceContextGetSurface) return nullptr;
|
||||
return sIOSurfaceContextGetSurface(aContext);
|
||||
}
|
||||
|
||||
CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) {
|
||||
CFStringRef* address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole);
|
||||
if (!address) return nullptr;
|
||||
|
||||
return *address;
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::LoadLibrary() {
|
||||
if (isLoaded) {
|
||||
return;
|
||||
}
|
||||
isLoaded = true;
|
||||
sIOSurfaceFramework =
|
||||
dlopen(IOSURFACE_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
|
||||
sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
sCoreGraphicsFramework =
|
||||
dlopen(COREGRAPHICS_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
sCoreVideoFramework =
|
||||
dlopen(COREVIDEO_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework ||
|
||||
!sCoreVideoFramework) {
|
||||
if (sIOSurfaceFramework) dlclose(sIOSurfaceFramework);
|
||||
if (sOpenGLFramework) dlclose(sOpenGLFramework);
|
||||
if (sCoreGraphicsFramework) dlclose(sCoreGraphicsFramework);
|
||||
if (sCoreVideoFramework) dlclose(sCoreVideoFramework);
|
||||
sIOSurfaceFramework = nullptr;
|
||||
sOpenGLFramework = nullptr;
|
||||
sCoreGraphicsFramework = nullptr;
|
||||
sCoreVideoFramework = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
kPropWidth = GetIOConst("kIOSurfaceWidth");
|
||||
kPropHeight = GetIOConst("kIOSurfaceHeight");
|
||||
kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
|
||||
kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
|
||||
kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
|
||||
sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
|
||||
sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID");
|
||||
sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidthOfPlane");
|
||||
sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeightOfPlane");
|
||||
sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRowOfPlane");
|
||||
sGetPropertyMaximum =
|
||||
GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum");
|
||||
sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
|
||||
sLock = GET_IOSYM(sLock, "IOSurfaceLock");
|
||||
sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
|
||||
sIncrementUseCount =
|
||||
GET_IOSYM(sIncrementUseCount, "IOSurfaceIncrementUseCount");
|
||||
sDecrementUseCount =
|
||||
GET_IOSYM(sDecrementUseCount, "IOSurfaceDecrementUseCount");
|
||||
sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
|
||||
sGetBaseAddressOfPlane =
|
||||
GET_IOSYM(sGetBaseAddressOfPlane, "IOSurfaceGetBaseAddressOfPlane");
|
||||
sPlaneCount = GET_IOSYM(sPlaneCount, "IOSurfaceGetPlaneCount");
|
||||
sPixelFormat = GET_IOSYM(sPixelFormat, "IOSurfaceGetPixelFormat");
|
||||
|
||||
sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
|
||||
sCGContextGetTypePtr =
|
||||
(unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
|
||||
|
||||
sCVPixelBufferGetIOSurface =
|
||||
GET_CVSYM(sCVPixelBufferGetIOSurface, "CVPixelBufferGetIOSurface");
|
||||
|
||||
// Optional symbols
|
||||
sIOSurfaceContextCreate =
|
||||
GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate");
|
||||
sIOSurfaceContextCreateImage =
|
||||
GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage");
|
||||
sIOSurfaceContextGetSurface =
|
||||
GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface");
|
||||
|
||||
if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress ||
|
||||
!sGetBaseAddressOfPlane || !sPlaneCount || !kPropWidth || !kPropHeight ||
|
||||
!kPropBytesPerElem || !kPropIsGlobal || !sLock || !sUnlock ||
|
||||
!sIncrementUseCount || !sDecrementUseCount || !sWidth || !sHeight ||
|
||||
!kPropBytesPerRow || !sBytesPerRow || !sGetPropertyMaximum ||
|
||||
!sCVPixelBufferGetIOSurface) {
|
||||
CloseLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
void MacIOSurfaceLib::CloseLibrary() {
|
||||
if (sIOSurfaceFramework) {
|
||||
dlclose(sIOSurfaceFramework);
|
||||
}
|
||||
if (sOpenGLFramework) {
|
||||
dlclose(sOpenGLFramework);
|
||||
}
|
||||
if (sCoreVideoFramework) {
|
||||
dlclose(sCoreVideoFramework);
|
||||
}
|
||||
sIOSurfaceFramework = nullptr;
|
||||
sOpenGLFramework = nullptr;
|
||||
sCoreVideoFramework = nullptr;
|
||||
}
|
||||
|
||||
MacIOSurface::MacIOSurface(IOSurfacePtr aIOSurfacePtr,
|
||||
double aContentsScaleFactor, bool aHasAlpha,
|
||||
|
@ -298,7 +35,7 @@ MacIOSurface::~MacIOSurface() {
|
|||
/* static */
|
||||
already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(
|
||||
int aWidth, int aHeight, double aContentsScaleFactor, bool aHasAlpha) {
|
||||
if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) return nullptr;
|
||||
if (aContentsScaleFactor <= 0) return nullptr;
|
||||
|
||||
CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
|
||||
kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks,
|
||||
|
@ -317,16 +54,15 @@ already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(
|
|||
::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
|
||||
CFNumberRef cfBytesPerElem =
|
||||
::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth, cfWidth);
|
||||
::CFDictionaryAddValue(props, kIOSurfaceWidth, cfWidth);
|
||||
::CFRelease(cfWidth);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight, cfHeight);
|
||||
::CFDictionaryAddValue(props, kIOSurfaceHeight, cfHeight);
|
||||
::CFRelease(cfHeight);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem,
|
||||
cfBytesPerElem);
|
||||
::CFDictionaryAddValue(props, kIOSurfaceBytesPerElement, cfBytesPerElem);
|
||||
::CFRelease(cfBytesPerElem);
|
||||
::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal, kCFBooleanTrue);
|
||||
::CFDictionaryAddValue(props, kIOSurfaceIsGlobal, kCFBooleanTrue);
|
||||
|
||||
IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props);
|
||||
IOSurfacePtr surfaceRef = ::IOSurfaceCreate(props);
|
||||
::CFRelease(props);
|
||||
|
||||
if (!surfaceRef) return nullptr;
|
||||
|
@ -344,9 +80,9 @@ already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(
|
|||
already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
|
||||
IOSurfaceID aIOSurfaceID, double aContentsScaleFactor, bool aHasAlpha,
|
||||
gfx::YUVColorSpace aColorSpace) {
|
||||
if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) return nullptr;
|
||||
if (aContentsScaleFactor <= 0) return nullptr;
|
||||
|
||||
IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
|
||||
IOSurfacePtr surfaceRef = (IOSurfacePtr)::IOSurfaceLookup(aIOSurfaceID);
|
||||
if (!surfaceRef) return nullptr;
|
||||
|
||||
RefPtr<MacIOSurface> ioSurface = new MacIOSurface(
|
||||
|
@ -359,16 +95,16 @@ already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
|
|||
}
|
||||
|
||||
IOSurfaceID MacIOSurface::GetIOSurfaceID() const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr);
|
||||
return ::IOSurfaceGetID((IOSurfaceRef)mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurface::GetBaseAddress() const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr);
|
||||
return ::IOSurfaceGetBaseAddress((IOSurfaceRef)mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(mIOSurfacePtr,
|
||||
aPlaneIndex);
|
||||
return ::IOSurfaceGetBaseAddressOfPlane((IOSurfaceRef)mIOSurfacePtr,
|
||||
aPlaneIndex);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetWidth(size_t plane) const {
|
||||
|
@ -382,56 +118,51 @@ size_t MacIOSurface::GetHeight(size_t plane) const {
|
|||
}
|
||||
|
||||
size_t MacIOSurface::GetPlaneCount() const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
|
||||
return ::IOSurfaceGetPlaneCount((IOSurfaceRef)mIOSurfacePtr);
|
||||
}
|
||||
|
||||
/*static*/
|
||||
size_t MacIOSurface::GetMaxWidth() {
|
||||
if (!MacIOSurfaceLib::isInit()) return -1;
|
||||
return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(
|
||||
MacIOSurfaceLib::kPropWidth);
|
||||
return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth);
|
||||
}
|
||||
|
||||
/*static*/
|
||||
size_t MacIOSurface::GetMaxHeight() {
|
||||
if (!MacIOSurfaceLib::isInit()) return -1;
|
||||
return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(
|
||||
MacIOSurfaceLib::kPropHeight);
|
||||
return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr, plane);
|
||||
return ::IOSurfaceGetWidthOfPlane((IOSurfaceRef)mIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr, plane);
|
||||
return ::IOSurfaceGetHeightOfPlane((IOSurfaceRef)mIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
size_t MacIOSurface::GetBytesPerRow(size_t plane) const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr, plane);
|
||||
return ::IOSurfaceGetBytesPerRowOfPlane((IOSurfaceRef)mIOSurfacePtr, plane);
|
||||
}
|
||||
|
||||
OSType MacIOSurface::GetPixelFormat() const {
|
||||
return MacIOSurfaceLib::IOSurfaceGetPixelFormat(mIOSurfacePtr);
|
||||
return ::IOSurfaceGetPixelFormat((IOSurfaceRef)mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void MacIOSurface::IncrementUseCount() {
|
||||
MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr);
|
||||
::IOSurfaceIncrementUseCount((IOSurfaceRef)mIOSurfacePtr);
|
||||
}
|
||||
|
||||
void MacIOSurface::DecrementUseCount() {
|
||||
MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr);
|
||||
::IOSurfaceDecrementUseCount((IOSurfaceRef)mIOSurfacePtr);
|
||||
}
|
||||
|
||||
#define READ_ONLY 0x1
|
||||
void MacIOSurface::Lock(bool aReadOnly) {
|
||||
MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0,
|
||||
nullptr);
|
||||
::IOSurfaceLock((IOSurfaceRef)mIOSurfacePtr,
|
||||
aReadOnly ? kIOSurfaceLockReadOnly : 0, nullptr);
|
||||
}
|
||||
|
||||
void MacIOSurface::Unlock(bool aReadOnly) {
|
||||
MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0,
|
||||
nullptr);
|
||||
::IOSurfaceUnlock((IOSurfaceRef)mIOSurfacePtr,
|
||||
aReadOnly ? kIOSurfaceLockReadOnly : 0, nullptr);
|
||||
}
|
||||
|
||||
using mozilla::gfx::IntSize;
|
||||
|
@ -499,9 +230,9 @@ CGLError MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, GLenum target,
|
|||
GLsizei width, GLsizei height,
|
||||
GLenum format, GLenum type,
|
||||
GLuint plane) const {
|
||||
return MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx, target, internalFormat,
|
||||
width, height, format, type,
|
||||
mIOSurfacePtr, plane);
|
||||
return ::CGLTexImageIOSurface2D(ctx, target, internalFormat, width, height,
|
||||
format, type, (IOSurfaceRef)mIOSurfacePtr,
|
||||
plane);
|
||||
}
|
||||
|
||||
CGLError MacIOSurface::CGLTexImageIOSurface2D(
|
||||
|
@ -574,54 +305,3 @@ CGLError MacIOSurface::CGLTexImageIOSurface2D(
|
|||
GetDevicePixelHeight(plane), format, type,
|
||||
plane);
|
||||
}
|
||||
|
||||
static CGColorSpaceRef CreateSystemColorSpace() {
|
||||
CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
|
||||
if (!cspace) {
|
||||
cspace = ::CGColorSpaceCreateDeviceRGB();
|
||||
}
|
||||
return cspace;
|
||||
}
|
||||
|
||||
CGContextRef MacIOSurface::CreateIOSurfaceContext() {
|
||||
CGColorSpaceRef cspace = CreateSystemColorSpace();
|
||||
CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(
|
||||
mIOSurfacePtr, GetDevicePixelWidth(), GetDevicePixelHeight(), 8, 32,
|
||||
cspace, 0x2002);
|
||||
::CGColorSpaceRelease(cspace);
|
||||
return ref;
|
||||
}
|
||||
|
||||
CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(
|
||||
CGContextRef aContext) {
|
||||
if (!MacIOSurfaceLib::isInit()) return nullptr;
|
||||
|
||||
return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext);
|
||||
}
|
||||
|
||||
already_AddRefed<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(
|
||||
CGContextRef aContext, double aContentsScaleFactor, bool aHasAlpha) {
|
||||
if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) return nullptr;
|
||||
|
||||
IOSurfacePtr surfaceRef =
|
||||
MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext);
|
||||
if (!surfaceRef) return nullptr;
|
||||
|
||||
RefPtr<MacIOSurface> ioSurface =
|
||||
new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
|
||||
return ioSurface.forget();
|
||||
}
|
||||
|
||||
CGContextType GetContextType(CGContextRef ref) {
|
||||
if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr)
|
||||
return CG_CONTEXT_TYPE_UNKNOWN;
|
||||
|
||||
unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref);
|
||||
if (type == CG_CONTEXT_TYPE_BITMAP) {
|
||||
return CG_CONTEXT_TYPE_BITMAP;
|
||||
} else if (type == CG_CONTEXT_TYPE_IOSURFACE) {
|
||||
return CG_CONTEXT_TYPE_IOSURFACE;
|
||||
} else {
|
||||
return CG_CONTEXT_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ class GLContext;
|
|||
struct _CGLContextObject;
|
||||
|
||||
typedef _CGLContextObject* CGLContextObj;
|
||||
typedef struct CGContext* CGContextRef;
|
||||
typedef struct CGImage* CGImageRef;
|
||||
typedef uint32_t IOSurfaceID;
|
||||
|
||||
# ifdef XP_IOS
|
||||
|
@ -32,36 +30,6 @@ typedef int CGLError;
|
|||
# endif
|
||||
|
||||
typedef CFTypeRef IOSurfacePtr;
|
||||
typedef IOSurfacePtr (*IOSurfaceCreateFunc)(CFDictionaryRef properties);
|
||||
typedef IOSurfacePtr (*IOSurfaceLookupFunc)(uint32_t io_surface_id);
|
||||
typedef IOSurfaceID (*IOSurfaceGetIDFunc)(IOSurfacePtr io_surface);
|
||||
typedef void (*IOSurfaceVoidFunc)(IOSurfacePtr io_surface);
|
||||
typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options,
|
||||
uint32_t* seed);
|
||||
typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface,
|
||||
uint32_t options, uint32_t* seed);
|
||||
typedef void* (*IOSurfaceGetBaseAddressFunc)(IOSurfacePtr io_surface);
|
||||
typedef void* (*IOSurfaceGetBaseAddressOfPlaneFunc)(IOSurfacePtr io_surface,
|
||||
size_t planeIndex);
|
||||
typedef size_t (*IOSurfaceSizeTFunc)(IOSurfacePtr io_surface);
|
||||
typedef size_t (*IOSurfaceSizePlaneTFunc)(IOSurfacePtr io_surface,
|
||||
size_t plane);
|
||||
typedef size_t (*IOSurfaceGetPropertyMaximumFunc)(CFStringRef property);
|
||||
typedef CGLError (*CGLTexImageIOSurface2DFunc)(
|
||||
CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width,
|
||||
GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface,
|
||||
GLuint plane);
|
||||
typedef CGContextRef (*IOSurfaceContextCreateFunc)(
|
||||
CFTypeRef io_surface, unsigned width, unsigned height,
|
||||
unsigned bitsPerComponent, unsigned bytes, CGColorSpaceRef colorSpace,
|
||||
CGBitmapInfo bitmapInfo);
|
||||
typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
|
||||
typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
|
||||
|
||||
typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
|
||||
CVPixelBufferRef pixelBuffer);
|
||||
|
||||
typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
|
||||
|
||||
# ifdef XP_MACOSX
|
||||
# import <OpenGL/OpenGL.h>
|
||||
|
@ -73,15 +41,6 @@ typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
|
|||
# include "mozilla/RefCounted.h"
|
||||
# include "mozilla/RefPtr.h"
|
||||
|
||||
enum CGContextType {
|
||||
CG_CONTEXT_TYPE_UNKNOWN = 0,
|
||||
// These are found by inspection, it's possible they could be changed
|
||||
CG_CONTEXT_TYPE_BITMAP = 4,
|
||||
CG_CONTEXT_TYPE_IOSURFACE = 8
|
||||
};
|
||||
|
||||
CGContextType GetContextType(CGContextRef ref);
|
||||
|
||||
class MacIOSurface final
|
||||
: public mozilla::external::AtomicRefCounted<MacIOSurface> {
|
||||
public:
|
||||
|
@ -151,13 +110,7 @@ class MacIOSurface final
|
|||
GLsizei height, GLenum format, GLenum type,
|
||||
GLuint plane) const;
|
||||
already_AddRefed<SourceSurface> GetAsSurface();
|
||||
CGContextRef CreateIOSurfaceContext();
|
||||
|
||||
// FIXME This doesn't really belong here
|
||||
static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
|
||||
static already_AddRefed<MacIOSurface> IOSurfaceContextGetSurface(
|
||||
CGContextRef aContext, double aContentsScaleFactor = 1.0,
|
||||
bool aHasAlpha = true);
|
||||
static size_t GetMaxWidth();
|
||||
static size_t GetMaxHeight();
|
||||
const void* GetIOSurfacePtr() { return mIOSurfacePtr; }
|
||||
|
@ -171,85 +124,5 @@ class MacIOSurface final
|
|||
mozilla::gfx::YUVColorSpace::UNKNOWN;
|
||||
};
|
||||
|
||||
class MacIOSurfaceLib {
|
||||
public:
|
||||
MacIOSurfaceLib() = delete;
|
||||
static void* sIOSurfaceFramework;
|
||||
static void* sOpenGLFramework;
|
||||
static void* sCoreGraphicsFramework;
|
||||
static void* sCoreVideoFramework;
|
||||
static bool isLoaded;
|
||||
static IOSurfaceCreateFunc sCreate;
|
||||
static IOSurfaceGetIDFunc sGetID;
|
||||
static IOSurfaceLookupFunc sLookup;
|
||||
static IOSurfaceGetBaseAddressFunc sGetBaseAddress;
|
||||
static IOSurfaceGetBaseAddressOfPlaneFunc sGetBaseAddressOfPlane;
|
||||
static IOSurfaceSizeTFunc sPlaneCount;
|
||||
static IOSurfaceLockFunc sLock;
|
||||
static IOSurfaceUnlockFunc sUnlock;
|
||||
static IOSurfaceVoidFunc sIncrementUseCount;
|
||||
static IOSurfaceVoidFunc sDecrementUseCount;
|
||||
static IOSurfaceSizePlaneTFunc sWidth;
|
||||
static IOSurfaceSizePlaneTFunc sHeight;
|
||||
static IOSurfaceSizePlaneTFunc sBytesPerRow;
|
||||
static IOSurfaceGetPropertyMaximumFunc sGetPropertyMaximum;
|
||||
static CGLTexImageIOSurface2DFunc sTexImage;
|
||||
static IOSurfaceContextCreateFunc sIOSurfaceContextCreate;
|
||||
static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage;
|
||||
static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface;
|
||||
static CVPixelBufferGetIOSurfaceFunc sCVPixelBufferGetIOSurface;
|
||||
static IOSurfacePixelFormatFunc sPixelFormat;
|
||||
static CFStringRef kPropWidth;
|
||||
static CFStringRef kPropHeight;
|
||||
static CFStringRef kPropBytesPerElem;
|
||||
static CFStringRef kPropBytesPerRow;
|
||||
static CFStringRef kPropIsGlobal;
|
||||
|
||||
static bool isInit();
|
||||
static CFStringRef GetIOConst(const char* symbole);
|
||||
static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
|
||||
static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
|
||||
static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
|
||||
static void* IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
|
||||
static void* IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t aPlaneIndex);
|
||||
static size_t IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr);
|
||||
static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr, size_t plane);
|
||||
static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane);
|
||||
static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr,
|
||||
size_t plane);
|
||||
static size_t IOSurfaceGetPropertyMaximum(CFStringRef property);
|
||||
static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, uint32_t options,
|
||||
uint32_t* seed);
|
||||
static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, uint32_t options,
|
||||
uint32_t* seed);
|
||||
static void IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr);
|
||||
static void IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr);
|
||||
static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, GLenum target,
|
||||
GLenum internalFormat, GLsizei width,
|
||||
GLsizei height, GLenum format,
|
||||
GLenum type, IOSurfacePtr ioSurface,
|
||||
GLuint plane);
|
||||
static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
|
||||
unsigned aWidth, unsigned aHeight,
|
||||
unsigned aBitsPerCompoent,
|
||||
unsigned aBytes,
|
||||
CGColorSpaceRef aColorSpace,
|
||||
CGBitmapInfo bitmapInfo);
|
||||
static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref);
|
||||
static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref);
|
||||
static IOSurfacePtr CVPixelBufferGetIOSurface(CVPixelBufferRef apixelBuffer);
|
||||
static OSType IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr);
|
||||
static unsigned int (*sCGContextGetTypePtr)(CGContextRef);
|
||||
static void LoadLibrary();
|
||||
static void CloseLibrary();
|
||||
|
||||
// Static deconstructor
|
||||
static class LibraryUnloader {
|
||||
public:
|
||||
~LibraryUnloader() { CloseLibrary(); }
|
||||
} sLibraryUnloader;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <AppKit/NSOpenGL.h>
|
||||
#import <OpenGL/CGLIOSurface.h>
|
||||
#include <dlfcn.h>
|
||||
#include "GLDefs.h"
|
||||
|
||||
|
@ -200,10 +201,10 @@ nsresult nsCARenderer::SetupRenderer(void* aCALayer, int aWidth, int aHeight,
|
|||
::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
|
||||
::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA,
|
||||
aWidth * intScaleFactor, aHeight * intScaleFactor,
|
||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
mIOSurface->mIOSurfacePtr, 0);
|
||||
::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA,
|
||||
aWidth * intScaleFactor, aHeight * intScaleFactor, GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV, (IOSurfaceRef)mIOSurface->mIOSurfacePtr,
|
||||
0);
|
||||
::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
|
||||
} else {
|
||||
::glGenTextures(1, &mFBOTexture);
|
||||
|
@ -332,10 +333,10 @@ void nsCARenderer::AttachIOSurface(MacIOSurface* aSurface) {
|
|||
CGLContextObj oldContext = ::CGLGetCurrentContext();
|
||||
::CGLSetCurrentContext(mOpenGLContext);
|
||||
::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
|
||||
MacIOSurfaceLib::CGLTexImageIOSurface2D(
|
||||
mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, mIOSurface->GetDevicePixelWidth(),
|
||||
mIOSurface->GetDevicePixelHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
mIOSurface->mIOSurfacePtr, 0);
|
||||
::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA,
|
||||
mIOSurface->GetDevicePixelWidth(), mIOSurface->GetDevicePixelHeight(),
|
||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
(IOSurfaceRef)mIOSurface->mIOSurfacePtr, 0);
|
||||
::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
|
||||
|
||||
// Rebind the FBO to make it live
|
||||
|
|
|
@ -22,11 +22,7 @@ nsresult SharedDIB::Create(uint32_t aSize) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool SharedDIB::IsValid() {
|
||||
if (!mShMem) return false;
|
||||
|
||||
return base::SharedMemory::IsHandleValid(mShMem->handle());
|
||||
}
|
||||
bool SharedDIB::IsValid() { return mShMem && mShMem->IsValid(); }
|
||||
|
||||
nsresult SharedDIB::Close() {
|
||||
delete mShMem;
|
||||
|
|
|
@ -100,8 +100,9 @@ nsresult SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER* aHdr) {
|
|||
|
||||
if (!mSharedHdc) return NS_ERROR_FAILURE;
|
||||
|
||||
mSharedBmp = ::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS,
|
||||
&mBitmapBits, mShMem->handle(), kHeaderBytes);
|
||||
mSharedBmp =
|
||||
::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS,
|
||||
&mBitmapBits, mShMem->GetHandle(), kHeaderBytes);
|
||||
if (!mSharedBmp) return NS_ERROR_FAILURE;
|
||||
|
||||
mOldObj = SelectObject(mSharedHdc, mSharedBmp);
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/file_util.h"
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "gfxQuartzSurface.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/MacIOSurface.h"
|
||||
|
||||
#include "gfxMacPlatformFontList.h"
|
||||
#include "gfxMacFont.h"
|
||||
|
@ -76,8 +75,6 @@ gfxPlatformMac::gfxPlatformMac() {
|
|||
|
||||
InitBackendPrefs(GetBackendPrefs());
|
||||
|
||||
MacIOSurfaceLib::LoadLibrary();
|
||||
|
||||
if (nsCocoaFeatures::OnHighSierraOrLater()) {
|
||||
mHasNativeColrFontSupport = true;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ in int aBlurDirection;
|
|||
struct BlurTask {
|
||||
RenderTaskCommonData common_data;
|
||||
float blur_radius;
|
||||
vec2 blur_region;
|
||||
};
|
||||
|
||||
BlurTask fetch_blur_task(int address) {
|
||||
|
@ -32,7 +33,8 @@ BlurTask fetch_blur_task(int address) {
|
|||
|
||||
BlurTask task = BlurTask(
|
||||
task_data.common_data,
|
||||
task_data.user_data.x
|
||||
task_data.user_data.x,
|
||||
task_data.user_data.yz
|
||||
);
|
||||
|
||||
return task;
|
||||
|
@ -72,7 +74,7 @@ void main(void) {
|
|||
}
|
||||
|
||||
vUvRect = vec4(src_rect.p0 + vec2(0.5),
|
||||
src_rect.p0 + src_rect.size - vec2(0.5));
|
||||
src_rect.p0 + blur_task.blur_region - vec2(0.5));
|
||||
vUvRect /= texture_size.xyxy;
|
||||
|
||||
vec2 pos = target_rect.p0 + target_rect.size * aPosition.xy;
|
||||
|
|
|
@ -2563,7 +2563,7 @@ impl PicturePrimitive {
|
|||
blur_std_deviation * scale_factors.0,
|
||||
blur_std_deviation * scale_factors.1
|
||||
);
|
||||
let device_rect = if self.options.inflate_if_required {
|
||||
let mut device_rect = if self.options.inflate_if_required {
|
||||
let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor;
|
||||
let inflation_factor = (inflation_factor * device_pixel_scale.0).ceil();
|
||||
|
||||
|
@ -2582,26 +2582,26 @@ impl PicturePrimitive {
|
|||
.intersection(&unclipped)
|
||||
.unwrap();
|
||||
|
||||
let mut device_rect = match device_rect.try_cast::<i32>() {
|
||||
match device_rect.try_cast::<i32>() {
|
||||
Some(rect) => rect,
|
||||
None => {
|
||||
return None
|
||||
}
|
||||
};
|
||||
|
||||
// Adjust the size to avoid introducing sampling errors during the down-scaling passes.
|
||||
// what would be even better is to rasterize the picture at the down-scaled size
|
||||
// directly.
|
||||
device_rect.size = RenderTask::adjusted_blur_source_size(
|
||||
device_rect.size,
|
||||
blur_std_deviation,
|
||||
);
|
||||
|
||||
device_rect
|
||||
}
|
||||
} else {
|
||||
clipped
|
||||
};
|
||||
|
||||
let original_size = device_rect.size;
|
||||
|
||||
// Adjust the size to avoid introducing sampling errors during the down-scaling passes.
|
||||
// what would be even better is to rasterize the picture at the down-scaled size
|
||||
// directly.
|
||||
device_rect.size = RenderTask::adjusted_blur_source_size(
|
||||
device_rect.size,
|
||||
blur_std_deviation,
|
||||
);
|
||||
|
||||
let uv_rect_kind = calculate_uv_rect_kind(
|
||||
&pic_rect,
|
||||
&transform,
|
||||
|
@ -2630,6 +2630,7 @@ impl PicturePrimitive {
|
|||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
None,
|
||||
original_size,
|
||||
);
|
||||
|
||||
Some((blur_render_task_id, picture_task_id))
|
||||
|
@ -2701,6 +2702,7 @@ impl PicturePrimitive {
|
|||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
Some(&mut blur_tasks),
|
||||
device_rect.size,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -559,6 +559,7 @@ pub struct BlurTask {
|
|||
pub blur_std_deviation: f32,
|
||||
pub target_kind: RenderTargetKind,
|
||||
pub uv_rect_handle: GpuCacheHandle,
|
||||
pub blur_region: DeviceIntSize,
|
||||
uv_rect_kind: UvRectKind,
|
||||
}
|
||||
|
||||
|
@ -975,6 +976,7 @@ impl RenderTask {
|
|||
RenderTargetKind::Alpha,
|
||||
ClearMode::Zero,
|
||||
None,
|
||||
cache_size,
|
||||
)
|
||||
}
|
||||
));
|
||||
|
@ -1086,6 +1088,7 @@ impl RenderTask {
|
|||
target_kind: RenderTargetKind,
|
||||
clear_mode: ClearMode,
|
||||
mut blur_cache: Option<&mut BlurTaskCache>,
|
||||
blur_region: DeviceIntSize,
|
||||
) -> RenderTaskId {
|
||||
// Adjust large std deviation value.
|
||||
let mut adjusted_blur_std_deviation = blur_std_deviation;
|
||||
|
@ -1137,6 +1140,8 @@ impl RenderTask {
|
|||
None => None,
|
||||
};
|
||||
|
||||
let blur_region = blur_region / (scale_factor as i32);
|
||||
|
||||
let blur_task_id = cached_task.unwrap_or_else(|| {
|
||||
let blur_task_v = RenderTask::with_dynamic_location(
|
||||
adjusted_blur_target_size,
|
||||
|
@ -1145,6 +1150,7 @@ impl RenderTask {
|
|||
blur_std_deviation: adjusted_blur_std_deviation.height,
|
||||
target_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
blur_region,
|
||||
uv_rect_kind,
|
||||
}),
|
||||
clear_mode,
|
||||
|
@ -1159,6 +1165,7 @@ impl RenderTask {
|
|||
blur_std_deviation: adjusted_blur_std_deviation.width,
|
||||
target_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
blur_region,
|
||||
uv_rect_kind,
|
||||
}),
|
||||
clear_mode,
|
||||
|
@ -1363,6 +1370,7 @@ impl RenderTask {
|
|||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
None,
|
||||
content_size,
|
||||
)
|
||||
}
|
||||
FilterPrimitiveKind::Opacity(ref opacity) => {
|
||||
|
@ -1432,6 +1440,7 @@ impl RenderTask {
|
|||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
None,
|
||||
content_size,
|
||||
);
|
||||
|
||||
let task = RenderTask::new_svg_filter_primitive(
|
||||
|
@ -1585,8 +1594,8 @@ impl RenderTask {
|
|||
RenderTaskKind::HorizontalBlur(ref task) => {
|
||||
[
|
||||
task.blur_std_deviation,
|
||||
0.0,
|
||||
0.0,
|
||||
task.blur_region.width as f32,
|
||||
task.blur_region.height as f32,
|
||||
]
|
||||
}
|
||||
RenderTaskKind::Readback(..) |
|
||||
|
|
|
@ -11,7 +11,6 @@ UNIFIED_SOURCES += [
|
|||
'src/base/at_exit.cc',
|
||||
'src/base/command_line.cc',
|
||||
'src/base/file_path.cc',
|
||||
'src/base/file_util.cc',
|
||||
'src/base/histogram.cc',
|
||||
'src/base/logging.cc',
|
||||
'src/base/message_loop.cc',
|
||||
|
@ -35,7 +34,6 @@ UNIFIED_SOURCES += [
|
|||
if os_win:
|
||||
SOURCES += [
|
||||
'src/base/condition_variable_win.cc',
|
||||
'src/base/file_util_win.cc',
|
||||
'src/base/lock_impl_win.cc',
|
||||
'src/base/message_pump_win.cc',
|
||||
'src/base/object_watcher.cc',
|
||||
|
@ -57,7 +55,6 @@ elif not CONFIG['MOZ_SYSTEM_LIBEVENT']:
|
|||
if os_posix:
|
||||
UNIFIED_SOURCES += [
|
||||
'src/base/condition_variable_posix.cc',
|
||||
'src/base/file_util_posix.cc',
|
||||
'src/base/lock_impl_posix.cc',
|
||||
'src/base/message_pump_libevent.cc',
|
||||
'src/base/platform_thread_posix.cc',
|
||||
|
@ -74,7 +71,6 @@ if os_posix:
|
|||
if os_macosx:
|
||||
UNIFIED_SOURCES += [
|
||||
'src/base/chrome_application_mac.mm',
|
||||
'src/base/file_util_mac.mm',
|
||||
'src/base/mac_util.mm',
|
||||
'src/base/message_pump_mac.mm',
|
||||
'src/base/process_util_mac.mm',
|
||||
|
|
|
@ -1,234 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/file_util.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
# include <io.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#if defined(ANDROID) || defined(OS_POSIX)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "base/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
|
||||
#include "base/string_piece.h"
|
||||
#include "base/sys_string_conversions.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace file_util {
|
||||
|
||||
bool EndsWithSeparator(const FilePath& path) {
|
||||
FilePath::StringType value = path.value();
|
||||
if (value.empty()) return false;
|
||||
|
||||
return FilePath::IsSeparator(value[value.size() - 1]);
|
||||
}
|
||||
|
||||
void TrimTrailingSeparator(std::wstring* dir) {
|
||||
while (dir->length() > 1 && EndsWithSeparator(dir))
|
||||
dir->resize(dir->length() - 1);
|
||||
}
|
||||
|
||||
FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
|
||||
FilePath::StringType file_name = path.BaseName().value();
|
||||
const FilePath::StringType::size_type last_dot =
|
||||
file_name.rfind(kExtensionSeparator);
|
||||
return FilePath::StringType(last_dot == FilePath::StringType::npos
|
||||
? FILE_PATH_LITERAL("")
|
||||
: file_name,
|
||||
last_dot + 1);
|
||||
}
|
||||
|
||||
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
|
||||
FilePath::StringType& value =
|
||||
const_cast<FilePath::StringType&>(path->value());
|
||||
|
||||
const FilePath::StringType::size_type last_dot =
|
||||
value.rfind(kExtensionSeparator);
|
||||
const FilePath::StringType::size_type last_separator =
|
||||
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
|
||||
|
||||
if (last_dot == FilePath::StringType::npos ||
|
||||
(last_separator != std::wstring::npos && last_dot < last_separator)) {
|
||||
// The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
|
||||
// We should just append the suffix to the entire path.
|
||||
value.append(suffix);
|
||||
return;
|
||||
}
|
||||
|
||||
value.insert(last_dot, suffix);
|
||||
}
|
||||
|
||||
void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
|
||||
FilePath::StringType clean_extension;
|
||||
// If the new extension is "" or ".", then we will just remove the current
|
||||
// extension.
|
||||
if (!extension.empty() &&
|
||||
extension != FilePath::StringType(&kExtensionSeparator, 1)) {
|
||||
if (extension[0] != kExtensionSeparator)
|
||||
clean_extension.append(&kExtensionSeparator, 1);
|
||||
clean_extension.append(extension);
|
||||
}
|
||||
|
||||
FilePath::StringType& value =
|
||||
const_cast<FilePath::StringType&>(path->value());
|
||||
const FilePath::StringType::size_type last_dot =
|
||||
value.rfind(kExtensionSeparator);
|
||||
const FilePath::StringType::size_type last_separator =
|
||||
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
|
||||
|
||||
// Erase the current extension, if any.
|
||||
if ((last_dot > last_separator ||
|
||||
last_separator == FilePath::StringType::npos) &&
|
||||
last_dot != FilePath::StringType::npos)
|
||||
value.erase(last_dot);
|
||||
|
||||
value.append(clean_extension);
|
||||
}
|
||||
|
||||
FILE* CreateAndOpenTemporaryFile(FilePath* path) {
|
||||
FilePath directory;
|
||||
if (!GetTempDir(&directory)) return NULL;
|
||||
|
||||
return CreateAndOpenTemporaryFileInDir(directory, path);
|
||||
}
|
||||
|
||||
bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
|
||||
FileInfo info;
|
||||
if (!GetFileInfo(file_path, &info)) return false;
|
||||
*file_size = info.size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CloseFile(FILE* file) {
|
||||
if (file == NULL) return true;
|
||||
return fclose(file) == 0;
|
||||
}
|
||||
|
||||
// Deprecated functions ----------------------------------------------------
|
||||
|
||||
bool AbsolutePath(std::wstring* path_str) {
|
||||
FilePath path(FilePath::FromWStringHack(*path_str));
|
||||
if (!AbsolutePath(&path)) return false;
|
||||
*path_str = path.ToWStringHack();
|
||||
return true;
|
||||
}
|
||||
void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
|
||||
if (!path) {
|
||||
NOTREACHED();
|
||||
return; // Don't crash in this function in release builds.
|
||||
}
|
||||
|
||||
if (!EndsWithSeparator(path)) path->push_back(FilePath::kSeparators[0]);
|
||||
path->append(new_ending);
|
||||
}
|
||||
bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
|
||||
return CopyFile(FilePath::FromWStringHack(from_path),
|
||||
FilePath::FromWStringHack(to_path));
|
||||
}
|
||||
bool CreateDirectory(const std::wstring& full_path) {
|
||||
return CreateDirectory(FilePath::FromWStringHack(full_path));
|
||||
}
|
||||
bool CreateNewTempDirectory(const std::wstring& prefix,
|
||||
std::wstring* new_temp_path) {
|
||||
#if defined(OS_WIN)
|
||||
FilePath::StringType dir_prefix(prefix);
|
||||
#elif defined(OS_POSIX)
|
||||
FilePath::StringType dir_prefix = WideToUTF8(prefix);
|
||||
#endif
|
||||
FilePath temp_path;
|
||||
if (!CreateNewTempDirectory(dir_prefix, &temp_path)) return false;
|
||||
*new_temp_path = temp_path.ToWStringHack();
|
||||
return true;
|
||||
}
|
||||
bool CreateTemporaryFileName(std::wstring* temp_file) {
|
||||
FilePath temp_file_path;
|
||||
if (!CreateTemporaryFileName(&temp_file_path)) return false;
|
||||
*temp_file = temp_file_path.ToWStringHack();
|
||||
return true;
|
||||
}
|
||||
bool Delete(const std::wstring& path) {
|
||||
return Delete(FilePath::FromWStringHack(path));
|
||||
}
|
||||
bool DirectoryExists(const std::wstring& path) {
|
||||
return DirectoryExists(FilePath::FromWStringHack(path));
|
||||
}
|
||||
bool EndsWithSeparator(std::wstring* path) {
|
||||
return EndsWithSeparator(FilePath::FromWStringHack(*path));
|
||||
}
|
||||
bool EndsWithSeparator(const std::wstring& path) {
|
||||
return EndsWithSeparator(FilePath::FromWStringHack(path));
|
||||
}
|
||||
bool GetCurrentDirectory(std::wstring* path_str) {
|
||||
FilePath path;
|
||||
if (!GetCurrentDirectory(&path)) return false;
|
||||
*path_str = path.ToWStringHack();
|
||||
return true;
|
||||
}
|
||||
std::wstring GetFileExtensionFromPath(const std::wstring& path) {
|
||||
FilePath::StringType extension =
|
||||
GetFileExtensionFromPath(FilePath::FromWStringHack(path));
|
||||
#if defined(OS_WIN)
|
||||
return extension;
|
||||
#elif defined(OS_POSIX)
|
||||
return UTF8ToWide(extension);
|
||||
#endif
|
||||
}
|
||||
bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
|
||||
return GetFileInfo(FilePath::FromWStringHack(file_path), results);
|
||||
}
|
||||
std::wstring GetFilenameFromPath(const std::wstring& path) {
|
||||
if (path.empty() || EndsWithSeparator(path)) return std::wstring();
|
||||
|
||||
return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
|
||||
}
|
||||
bool GetFileSize(const std::wstring& file_path, int64_t* file_size) {
|
||||
return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
|
||||
}
|
||||
bool GetTempDir(std::wstring* path_str) {
|
||||
FilePath path;
|
||||
if (!GetTempDir(&path)) return false;
|
||||
*path_str = path.ToWStringHack();
|
||||
return true;
|
||||
}
|
||||
FILE* OpenFile(const std::wstring& filename, const char* mode) {
|
||||
return OpenFile(FilePath::FromWStringHack(filename), mode);
|
||||
}
|
||||
bool PathExists(const std::wstring& path) {
|
||||
return PathExists(FilePath::FromWStringHack(path));
|
||||
}
|
||||
bool PathIsWritable(const std::wstring& path) {
|
||||
return PathIsWritable(FilePath::FromWStringHack(path));
|
||||
}
|
||||
int ReadFile(const std::wstring& filename, char* data, int size) {
|
||||
return ReadFile(FilePath::FromWStringHack(filename), data, size);
|
||||
}
|
||||
bool SetCurrentDirectory(const std::wstring& directory) {
|
||||
return SetCurrentDirectory(FilePath::FromWStringHack(directory));
|
||||
}
|
||||
void UpOneDirectory(std::wstring* dir) {
|
||||
FilePath path = FilePath::FromWStringHack(*dir);
|
||||
FilePath directory = path.DirName();
|
||||
// If there is no separator, we will get back kCurrentDirectory.
|
||||
// In this case don't change |dir|.
|
||||
if (directory.value() != FilePath::kCurrentDirectory)
|
||||
*dir = directory.ToWStringHack();
|
||||
}
|
||||
int WriteFile(const std::wstring& filename, const char* data, int size) {
|
||||
return WriteFile(FilePath::FromWStringHack(filename), data, size);
|
||||
}
|
||||
} // namespace file_util
|
|
@ -1,229 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This file contains utility functions for dealing with the local
|
||||
// filesystem.
|
||||
|
||||
#ifndef BASE_FILE_UTIL_H_
|
||||
#define BASE_FILE_UTIL_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
# include <windows.h>
|
||||
#elif defined(ANDROID)
|
||||
# include <sys/stat.h>
|
||||
#elif defined(OS_POSIX)
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/file_path.h"
|
||||
|
||||
namespace file_util {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions that operate purely on a path string w/o touching the filesystem:
|
||||
|
||||
// Returns true if the given path ends with a path separator character.
|
||||
bool EndsWithSeparator(const FilePath& path);
|
||||
// These two versions are both deprecated. TODO(estade): remove them.
|
||||
bool EndsWithSeparator(std::wstring* path);
|
||||
bool EndsWithSeparator(const std::wstring& path);
|
||||
|
||||
// Modifies a string by trimming all trailing separators from the end.
|
||||
// Deprecated. FilePath does this automatically, and if it's constructed from a
|
||||
// path with a trailing separator, StripTrailingSeparators() may be used.
|
||||
void TrimTrailingSeparator(std::wstring* dir);
|
||||
|
||||
// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not
|
||||
// refer to a file.
|
||||
// If 'dir' is a root directory, return without change.
|
||||
// Deprecated. Use FilePath::DirName instead.
|
||||
void UpOneDirectory(std::wstring* dir);
|
||||
|
||||
// Returns the filename portion of 'path', without any leading \'s or /'s.
|
||||
// Deprecated. Use FilePath::BaseName instead.
|
||||
std::wstring GetFilenameFromPath(const std::wstring& path);
|
||||
|
||||
// Deprecated compatibility function. Use FilePath::Extension.
|
||||
FilePath::StringType GetFileExtensionFromPath(const FilePath& path);
|
||||
// Deprecated temporary compatibility function.
|
||||
std::wstring GetFileExtensionFromPath(const std::wstring& path);
|
||||
|
||||
// Appends new_ending to path, adding a separator between the two if necessary.
|
||||
void AppendToPath(std::wstring* path, const std::wstring& new_ending);
|
||||
|
||||
// Convert provided relative path into an absolute path. Returns false on
|
||||
// error. On POSIX, this function fails if the path does not exist.
|
||||
bool AbsolutePath(FilePath* path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool AbsolutePath(std::wstring* path);
|
||||
|
||||
// Deprecated compatibility function. Use FilePath::InsertBeforeExtension.
|
||||
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix);
|
||||
|
||||
// Deprecated compatibility function. Use FilePath::ReplaceExtension.
|
||||
void ReplaceExtension(FilePath* file_name,
|
||||
const FilePath::StringType& extension);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Deprecated temporary compatibility functions.
|
||||
void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix);
|
||||
void ReplaceExtension(std::wstring* file_name, const std::wstring& extension);
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions that involve filesystem access or modification:
|
||||
|
||||
// Deletes the given path, whether it's a file or a directory.
|
||||
// If it's a directory, it's perfectly happy to delete all of the
|
||||
// directory's contents.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool Delete(const FilePath& path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool Delete(const std::wstring& path);
|
||||
|
||||
// Copies a single file. Use CopyDirectory to copy directories.
|
||||
bool CopyFile(const FilePath& from_path, const FilePath& to_path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool CopyFile(const std::wstring& from_path, const std::wstring& to_path);
|
||||
|
||||
// Returns true if the given path exists on the local filesystem,
|
||||
// false otherwise.
|
||||
bool PathExists(const FilePath& path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool PathExists(const std::wstring& path);
|
||||
|
||||
// Returns true if the given path is writable by the user, false otherwise.
|
||||
bool PathIsWritable(const FilePath& path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool PathIsWritable(const std::wstring& path);
|
||||
|
||||
// Returns true if the given path exists and is a directory, false otherwise.
|
||||
bool DirectoryExists(const FilePath& path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool DirectoryExists(const std::wstring& path);
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
|
||||
// in |buffer|. This function is protected against EINTR and partial reads.
|
||||
// Returns true iff |bytes| bytes have been successfuly read from |fd|.
|
||||
bool ReadFromFD(int fd, char* buffer, size_t bytes);
|
||||
#endif // defined(OS_POSIX)
|
||||
|
||||
// Get the temporary directory provided by the system.
|
||||
bool GetTempDir(FilePath* path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool GetTempDir(std::wstring* path);
|
||||
// Get a temporary directory for shared memory files.
|
||||
// Only useful on POSIX; redirects to GetTempDir() on Windows.
|
||||
bool GetShmemTempDir(FilePath* path);
|
||||
|
||||
// Creates a temporary file. The full path is placed in |path|, and the
|
||||
// function returns true if was successful in creating the file. The file will
|
||||
// be empty and all handles closed after this function returns.
|
||||
// TODO(erikkay): rename this function and track down all of the callers.
|
||||
// (Clarification of erik's comment: the intent is to rename the BlahFileName()
|
||||
// calls into BlahFile(), since they create temp files (not temp filenames).)
|
||||
bool CreateTemporaryFileName(FilePath* path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool CreateTemporaryFileName(std::wstring* temp_file);
|
||||
|
||||
// Create and open a temporary file. File is opened for read/write.
|
||||
// The full path is placed in |path|, and the function returns true if
|
||||
// was successful in creating and opening the file.
|
||||
FILE* CreateAndOpenTemporaryFile(FilePath* path);
|
||||
// Like above but for shmem files. Only useful for POSIX.
|
||||
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path);
|
||||
|
||||
// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
|
||||
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path);
|
||||
|
||||
// Same as CreateTemporaryFileName but the file is created in |dir|.
|
||||
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
|
||||
std::wstring* temp_file);
|
||||
|
||||
// Create a new directory under TempPath. If prefix is provided, the new
|
||||
// directory name is in the format of prefixyyyy.
|
||||
// NOTE: prefix is ignored in the POSIX implementation.
|
||||
// TODO(erikkay): is this OK?
|
||||
// If success, return true and output the full path of the directory created.
|
||||
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
|
||||
FilePath* new_temp_path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool CreateNewTempDirectory(const std::wstring& prefix,
|
||||
std::wstring* new_temp_path);
|
||||
|
||||
// Creates a directory, as well as creating any parent directories, if they
|
||||
// don't exist. Returns 'true' on successful creation, or if the directory
|
||||
// already exists.
|
||||
bool CreateDirectory(const FilePath& full_path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool CreateDirectory(const std::wstring& full_path);
|
||||
|
||||
// Returns the file size. Returns true on success.
|
||||
bool GetFileSize(const FilePath& file_path, int64_t* file_size);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool GetFileSize(const std::wstring& file_path, int64_t* file_size);
|
||||
|
||||
// Used to hold information about a given file path. See GetFileInfo below.
|
||||
struct FileInfo {
|
||||
// The size of the file in bytes. Undefined when is_directory is true.
|
||||
int64_t size;
|
||||
|
||||
// True if the file corresponds to a directory.
|
||||
bool is_directory;
|
||||
|
||||
// Add additional fields here as needed.
|
||||
};
|
||||
|
||||
// Returns information about the given file path.
|
||||
bool GetFileInfo(const FilePath& file_path, FileInfo* info);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool GetFileInfo(const std::wstring& file_path, FileInfo* info);
|
||||
|
||||
// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
|
||||
FILE* OpenFile(const FilePath& filename, const char* mode);
|
||||
// Deprecated temporary compatibility functions.
|
||||
FILE* OpenFile(const std::string& filename, const char* mode);
|
||||
FILE* OpenFile(const std::wstring& filename, const char* mode);
|
||||
|
||||
// Closes file opened by OpenFile. Returns true on success.
|
||||
bool CloseFile(FILE* file);
|
||||
|
||||
// Reads the given number of bytes from the file into the buffer. Returns
|
||||
// the number of read bytes, or -1 on error.
|
||||
int ReadFile(const FilePath& filename, char* data, int size);
|
||||
// Deprecated temporary compatibility function.
|
||||
int ReadFile(const std::wstring& filename, char* data, int size);
|
||||
|
||||
// Writes the given buffer into the file, overwriting any data that was
|
||||
// previously there. Returns the number of bytes written, or -1 on error.
|
||||
int WriteFile(const FilePath& filename, const char* data, int size);
|
||||
// Deprecated temporary compatibility function.
|
||||
int WriteFile(const std::wstring& filename, const char* data, int size);
|
||||
|
||||
// Gets the current working directory for the process.
|
||||
bool GetCurrentDirectory(FilePath* path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool GetCurrentDirectory(std::wstring* path);
|
||||
|
||||
// Sets the current working directory for the process.
|
||||
bool SetCurrentDirectory(const FilePath& path);
|
||||
// Deprecated temporary compatibility function.
|
||||
bool SetCurrentDirectory(const std::wstring& current_directory);
|
||||
|
||||
} // namespace file_util
|
||||
|
||||
#endif // BASE_FILE_UTIL_H_
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/file_util.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <copyfile.h>
|
||||
|
||||
#include "base/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/scoped_nsautorelease_pool.h"
|
||||
|
||||
namespace file_util {
|
||||
|
||||
bool GetTempDir(FilePath* path) {
|
||||
base::ScopedNSAutoreleasePool autorelease_pool;
|
||||
NSString* tmp = NSTemporaryDirectory();
|
||||
if (tmp == nil) return false;
|
||||
*path = FilePath([tmp fileSystemRepresentation]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetShmemTempDir(FilePath* path) { return GetTempDir(path); }
|
||||
|
||||
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
|
||||
return (copyfile(from_path.value().c_str(), to_path.value().c_str(), NULL, COPYFILE_ALL) == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -1,316 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/file_util.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#define _DARWIN_USE_64_BIT_INODE // Use 64-bit inode data structures
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/time.h"
|
||||
|
||||
namespace file_util {
|
||||
|
||||
#if defined(GOOGLE_CHROME_BUILD)
|
||||
static const char* kTempFileName = "com.google.chrome.XXXXXX";
|
||||
#else
|
||||
static const char* kTempFileName = "org.chromium.XXXXXX";
|
||||
#endif
|
||||
|
||||
bool AbsolutePath(FilePath* path) {
|
||||
char full_path[PATH_MAX];
|
||||
if (realpath(path->value().c_str(), full_path) == NULL) return false;
|
||||
*path = FilePath(full_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
|
||||
// which works both with and without the recursive flag. I'm not sure we need
|
||||
// that functionality. If not, remove from file_util_win.cc, otherwise add it
|
||||
// here.
|
||||
bool Delete(const FilePath& path) {
|
||||
const char* path_str = path.value().c_str();
|
||||
struct stat file_info;
|
||||
int test = stat(path_str, &file_info);
|
||||
if (test != 0) {
|
||||
// The Windows version defines this condition as success.
|
||||
bool ret = (errno == ENOENT || errno == ENOTDIR);
|
||||
return ret;
|
||||
}
|
||||
if (!S_ISDIR(file_info.st_mode)) return (unlink(path_str) == 0);
|
||||
|
||||
return (rmdir(path_str) == 0);
|
||||
}
|
||||
|
||||
bool PathExists(const FilePath& path) {
|
||||
struct stat file_info;
|
||||
return (stat(path.value().c_str(), &file_info) == 0);
|
||||
}
|
||||
|
||||
bool PathIsWritable(const FilePath& path) {
|
||||
FilePath test_path(path);
|
||||
struct stat file_info;
|
||||
if (stat(test_path.value().c_str(), &file_info) != 0) {
|
||||
// If the path doesn't exist, test the parent dir.
|
||||
test_path = test_path.DirName();
|
||||
// If the parent dir doesn't exist, then return false (the path is not
|
||||
// directly writable).
|
||||
if (stat(test_path.value().c_str(), &file_info) != 0) return false;
|
||||
}
|
||||
if (S_IWOTH & file_info.st_mode) return true;
|
||||
if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
|
||||
return true;
|
||||
if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectoryExists(const FilePath& path) {
|
||||
struct stat file_info;
|
||||
if (stat(path.value().c_str(), &file_info) == 0)
|
||||
return S_ISDIR(file_info.st_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadFromFD(int fd, char* buffer, size_t bytes) {
|
||||
size_t total_read = 0;
|
||||
while (total_read < bytes) {
|
||||
ssize_t bytes_read =
|
||||
HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
|
||||
if (bytes_read <= 0) break;
|
||||
total_read += bytes_read;
|
||||
}
|
||||
return total_read == bytes;
|
||||
}
|
||||
|
||||
// Creates and opens a temporary file in |directory|, returning the
|
||||
// file descriptor. |path| is set to the temporary file path.
|
||||
// Note TODO(erikkay) comment in header for BlahFileName() calls; the
|
||||
// intent is to rename these files BlahFile() (since they create
|
||||
// files, not filenames). This function does NOT unlink() the file.
|
||||
int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
|
||||
*path = directory.Append(kTempFileName);
|
||||
const std::string& tmpdir_string = path->value();
|
||||
// this should be OK since mkstemp just replaces characters in place
|
||||
char* buffer = const_cast<char*>(tmpdir_string.c_str());
|
||||
|
||||
return mkstemp(buffer);
|
||||
}
|
||||
|
||||
bool CreateTemporaryFileName(FilePath* path) {
|
||||
FilePath directory;
|
||||
if (!GetTempDir(&directory)) return false;
|
||||
int fd = CreateAndOpenFdForTemporaryFile(directory, path);
|
||||
if (fd < 0) return false;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
|
||||
FilePath directory;
|
||||
if (!GetShmemTempDir(&directory)) return NULL;
|
||||
|
||||
return CreateAndOpenTemporaryFileInDir(directory, path);
|
||||
}
|
||||
|
||||
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
|
||||
int fd = CreateAndOpenFdForTemporaryFile(dir, path);
|
||||
if (fd < 0) return NULL;
|
||||
|
||||
return fdopen(fd, "a+");
|
||||
}
|
||||
|
||||
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
|
||||
std::wstring* temp_file) {
|
||||
// Not implemented yet.
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
|
||||
FilePath* new_temp_path) {
|
||||
FilePath tmpdir;
|
||||
if (!GetTempDir(&tmpdir)) return false;
|
||||
tmpdir = tmpdir.Append(kTempFileName);
|
||||
std::string tmpdir_string = tmpdir.value();
|
||||
#ifdef ANDROID
|
||||
char* dtemp = NULL;
|
||||
#else
|
||||
// this should be OK since mkdtemp just replaces characters in place
|
||||
char* buffer = const_cast<char*>(tmpdir_string.c_str());
|
||||
char* dtemp = mkdtemp(buffer);
|
||||
#endif
|
||||
if (!dtemp) return false;
|
||||
*new_temp_path = FilePath(dtemp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateDirectory(const FilePath& full_path) {
|
||||
std::vector<FilePath> subpaths;
|
||||
|
||||
// Collect a list of all parent directories.
|
||||
FilePath last_path = full_path;
|
||||
subpaths.push_back(full_path);
|
||||
for (FilePath path = full_path.DirName(); path.value() != last_path.value();
|
||||
path = path.DirName()) {
|
||||
subpaths.push_back(path);
|
||||
last_path = path;
|
||||
}
|
||||
|
||||
// Iterate through the parents and create the missing ones.
|
||||
for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
|
||||
i != subpaths.rend(); ++i) {
|
||||
if (!DirectoryExists(*i)) {
|
||||
if (mkdir(i->value().c_str(), 0777) != 0) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
|
||||
struct stat file_info;
|
||||
if (stat(file_path.value().c_str(), &file_info) != 0) return false;
|
||||
results->is_directory = S_ISDIR(file_info.st_mode);
|
||||
results->size = file_info.st_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE* OpenFile(const std::string& filename, const char* mode) {
|
||||
return OpenFile(FilePath(filename), mode);
|
||||
}
|
||||
|
||||
FILE* OpenFile(const FilePath& filename, const char* mode) {
|
||||
return fopen(filename.value().c_str(), mode);
|
||||
}
|
||||
|
||||
int ReadFile(const FilePath& filename, char* data, int size) {
|
||||
int fd = open(filename.value().c_str(), O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
int ret_value = HANDLE_EINTR(read(fd, data, size));
|
||||
IGNORE_EINTR(close(fd));
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
int WriteFile(const FilePath& filename, const char* data, int size) {
|
||||
int fd = creat(filename.value().c_str(), 0666);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
// Allow for partial writes
|
||||
ssize_t bytes_written_total = 0;
|
||||
do {
|
||||
ssize_t bytes_written_partial = HANDLE_EINTR(
|
||||
write(fd, data + bytes_written_total, size - bytes_written_total));
|
||||
if (bytes_written_partial < 0) {
|
||||
IGNORE_EINTR(close(fd));
|
||||
return -1;
|
||||
}
|
||||
bytes_written_total += bytes_written_partial;
|
||||
} while (bytes_written_total < size);
|
||||
|
||||
IGNORE_EINTR(close(fd));
|
||||
return bytes_written_total;
|
||||
}
|
||||
|
||||
// Gets the current working directory for the process.
|
||||
bool GetCurrentDirectory(FilePath* dir) {
|
||||
char system_buffer[PATH_MAX] = "";
|
||||
if (!getcwd(system_buffer, sizeof(system_buffer))) {
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
*dir = FilePath(system_buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets the current working directory for the process.
|
||||
bool SetCurrentDirectory(const FilePath& path) {
|
||||
int ret = chdir(path.value().c_str());
|
||||
return !ret;
|
||||
}
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
bool GetTempDir(FilePath* path) {
|
||||
const char* tmp = getenv("TMPDIR");
|
||||
if (tmp)
|
||||
*path = FilePath(tmp);
|
||||
else
|
||||
*path = FilePath("/tmp");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetShmemTempDir(FilePath* path) {
|
||||
# if defined(OS_LINUX) && !defined(ANDROID)
|
||||
*path = FilePath("/dev/shm");
|
||||
return true;
|
||||
# else
|
||||
return GetTempDir(path);
|
||||
# endif
|
||||
}
|
||||
|
||||
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
|
||||
int infile = open(from_path.value().c_str(), O_RDONLY);
|
||||
if (infile < 0) return false;
|
||||
|
||||
int outfile = creat(to_path.value().c_str(), 0666);
|
||||
if (outfile < 0) {
|
||||
close(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t kBufferSize = 32768;
|
||||
std::vector<char> buffer(kBufferSize);
|
||||
bool result = true;
|
||||
|
||||
while (result) {
|
||||
ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
|
||||
if (bytes_read < 0) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
if (bytes_read == 0) break;
|
||||
// Allow for partial writes
|
||||
ssize_t bytes_written_per_read = 0;
|
||||
do {
|
||||
ssize_t bytes_written_partial =
|
||||
HANDLE_EINTR(write(outfile, &buffer[bytes_written_per_read],
|
||||
bytes_read - bytes_written_per_read));
|
||||
if (bytes_written_partial < 0) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
bytes_written_per_read += bytes_written_partial;
|
||||
} while (bytes_written_per_read < bytes_read);
|
||||
}
|
||||
|
||||
if (IGNORE_EINTR(close(infile)) < 0) result = false;
|
||||
if (IGNORE_EINTR(close(outfile)) < 0) result = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(OS_MACOSX)
|
||||
|
||||
} // namespace file_util
|
|
@ -1,330 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/file_util.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
|
||||
#include "base/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/scoped_handle.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/time.h"
|
||||
#include "base/win_util.h"
|
||||
|
||||
namespace file_util {
|
||||
|
||||
bool AbsolutePath(FilePath* path) {
|
||||
wchar_t file_path_buf[MAX_PATH];
|
||||
if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) return false;
|
||||
*path = FilePath(file_path_buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Delete(const FilePath& path) {
|
||||
if (path.value().length() >= MAX_PATH) return false;
|
||||
|
||||
// Use DeleteFile; it should be faster. DeleteFile
|
||||
// fails if passed a directory though, which is why we fall through on
|
||||
// failure to the SHFileOperation.
|
||||
if (DeleteFile(path.value().c_str()) != 0) return true;
|
||||
|
||||
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
|
||||
// so we have to use wcscpy because wcscpy_s writes non-NULLs
|
||||
// into the rest of the buffer.
|
||||
wchar_t double_terminated_path[MAX_PATH + 1] = {0};
|
||||
#pragma warning(suppress : 4996) // don't complain about wcscpy deprecation
|
||||
wcscpy(double_terminated_path, path.value().c_str());
|
||||
|
||||
SHFILEOPSTRUCT file_operation = {0};
|
||||
file_operation.wFunc = FO_DELETE;
|
||||
file_operation.pFrom = double_terminated_path;
|
||||
file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
|
||||
file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
|
||||
int err = SHFileOperation(&file_operation);
|
||||
// Some versions of Windows return ERROR_FILE_NOT_FOUND when
|
||||
// deleting an empty directory.
|
||||
return (err == 0 || err == ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
|
||||
// NOTE: I suspect we could support longer paths, but that would involve
|
||||
// analyzing all our usage of files.
|
||||
if (from_path.value().length() >= MAX_PATH ||
|
||||
to_path.value().length() >= MAX_PATH) {
|
||||
return false;
|
||||
}
|
||||
return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
|
||||
false) != 0);
|
||||
}
|
||||
|
||||
bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
|
||||
bool recursive) {
|
||||
// NOTE: I suspect we could support longer paths, but that would involve
|
||||
// analyzing all our usage of files.
|
||||
if (from_path.value().length() >= MAX_PATH ||
|
||||
to_path.value().length() >= MAX_PATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
|
||||
// so we have to use wcscpy because wcscpy_s writes non-NULLs
|
||||
// into the rest of the buffer.
|
||||
wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
|
||||
wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
|
||||
#pragma warning(suppress : 4996) // don't complain about wcscpy deprecation
|
||||
wcscpy(double_terminated_path_from, from_path.value().c_str());
|
||||
#pragma warning(suppress : 4996) // don't complain about wcscpy deprecation
|
||||
wcscpy(double_terminated_path_to, to_path.value().c_str());
|
||||
|
||||
SHFILEOPSTRUCT file_operation = {0};
|
||||
file_operation.wFunc = FO_COPY;
|
||||
file_operation.pFrom = double_terminated_path_from;
|
||||
file_operation.pTo = double_terminated_path_to;
|
||||
file_operation.fFlags =
|
||||
FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
|
||||
if (!recursive) file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
|
||||
|
||||
return (SHFileOperation(&file_operation) == 0);
|
||||
}
|
||||
|
||||
bool PathExists(const FilePath& path) {
|
||||
return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
|
||||
}
|
||||
|
||||
bool PathIsWritable(const FilePath& path) {
|
||||
HANDLE dir =
|
||||
CreateFile(path.value().c_str(), FILE_ADD_FILE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
if (dir == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
CloseHandle(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DirectoryExists(const FilePath& path) {
|
||||
DWORD fileattr = GetFileAttributes(path.value().c_str());
|
||||
if (fileattr != INVALID_FILE_ATTRIBUTES)
|
||||
return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetTempDir(FilePath* path) {
|
||||
wchar_t temp_path[MAX_PATH + 1];
|
||||
DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
|
||||
if (path_len >= MAX_PATH || path_len <= 0) return false;
|
||||
// TODO(evanm): the old behavior of this function was to always strip the
|
||||
// trailing slash. We duplicate this here, but it shouldn't be necessary
|
||||
// when everyone is using the appropriate FilePath APIs.
|
||||
std::wstring path_str(temp_path);
|
||||
TrimTrailingSeparator(&path_str);
|
||||
*path = FilePath(path_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetShmemTempDir(FilePath* path) { return GetTempDir(path); }
|
||||
|
||||
bool CreateTemporaryFileName(FilePath* path) {
|
||||
std::wstring temp_path, temp_file;
|
||||
|
||||
if (!GetTempDir(&temp_path)) return false;
|
||||
|
||||
if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) {
|
||||
*path = FilePath(temp_file);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
|
||||
return CreateAndOpenTemporaryFile(path);
|
||||
}
|
||||
|
||||
// On POSIX we have semantics to create and open a temporary file
|
||||
// atomically.
|
||||
// TODO(jrg): is there equivalent call to use on Windows instead of
|
||||
// going 2-step?
|
||||
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
|
||||
std::wstring wstring_path;
|
||||
if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) {
|
||||
return NULL;
|
||||
}
|
||||
*path = FilePath(wstring_path);
|
||||
// Open file in binary mode, to avoid problems with fwrite. On Windows
|
||||
// it replaces \n's with \r\n's, which may surprise you.
|
||||
// Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
|
||||
return OpenFile(*path, "wb+");
|
||||
}
|
||||
|
||||
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
|
||||
std::wstring* temp_file) {
|
||||
wchar_t temp_name[MAX_PATH + 1];
|
||||
|
||||
if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) return false; // fail!
|
||||
|
||||
DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
|
||||
if (path_len > MAX_PATH + 1 || path_len == 0) return false; // fail!
|
||||
|
||||
temp_file->assign(temp_name, path_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
|
||||
FilePath* new_temp_path) {
|
||||
FilePath system_temp_dir;
|
||||
if (!GetTempDir(&system_temp_dir)) return false;
|
||||
|
||||
FilePath path_to_create;
|
||||
srand(static_cast<uint32_t>(time(NULL)));
|
||||
|
||||
int count = 0;
|
||||
while (count < 50) {
|
||||
// Try create a new temporary directory with random generated name. If
|
||||
// the one exists, keep trying another path name until we reach some limit.
|
||||
path_to_create = system_temp_dir;
|
||||
std::wstring new_dir_name;
|
||||
new_dir_name.assign(prefix);
|
||||
new_dir_name.append(IntToWString(rand() % kint16max));
|
||||
path_to_create = path_to_create.Append(new_dir_name);
|
||||
|
||||
if (::CreateDirectory(path_to_create.value().c_str(), NULL)) break;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 50) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*new_temp_path = path_to_create;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateDirectory(const FilePath& full_path) {
|
||||
if (DirectoryExists(full_path)) return true;
|
||||
int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL);
|
||||
return err == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(),
|
||||
GetFileExInfoStandard, &attr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ULARGE_INTEGER size;
|
||||
size.HighPart = attr.nFileSizeHigh;
|
||||
size.LowPart = attr.nFileSizeLow;
|
||||
results->size = size.QuadPart;
|
||||
|
||||
results->is_directory =
|
||||
(attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE* OpenFile(const FilePath& filename, const char* mode) {
|
||||
std::wstring w_mode = ASCIIToWide(std::string(mode));
|
||||
FILE* file;
|
||||
if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
FILE* OpenFile(const std::string& filename, const char* mode) {
|
||||
FILE* file;
|
||||
if (fopen_s(&file, filename.c_str(), mode) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
int ReadFile(const FilePath& filename, char* data, int size) {
|
||||
ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL));
|
||||
if (file == INVALID_HANDLE_VALUE) return -1;
|
||||
|
||||
int ret_value;
|
||||
DWORD read;
|
||||
if (::ReadFile(file, data, size, &read, NULL) && read == size) {
|
||||
ret_value = static_cast<int>(read);
|
||||
} else {
|
||||
ret_value = -1;
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
int WriteFile(const FilePath& filename, const char* data, int size) {
|
||||
ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS, 0, NULL));
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
CHROMIUM_LOG(WARNING) << "CreateFile failed for path " << filename.value()
|
||||
<< " error code=" << GetLastError()
|
||||
<< " error text=" << win_util::FormatLastWin32Error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD written;
|
||||
BOOL result = ::WriteFile(file, data, size, &written, NULL);
|
||||
if (result && written == size) return static_cast<int>(written);
|
||||
|
||||
if (!result) {
|
||||
// WriteFile failed.
|
||||
CHROMIUM_LOG(WARNING) << "writing file " << filename.value()
|
||||
<< " failed, error code=" << GetLastError()
|
||||
<< " description="
|
||||
<< win_util::FormatLastWin32Error();
|
||||
} else {
|
||||
// Didn't write all the bytes.
|
||||
CHROMIUM_LOG(WARNING) << "wrote" << written << " bytes to "
|
||||
<< filename.value() << " expected " << size;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Gets the current working directory for the process.
|
||||
bool GetCurrentDirectory(FilePath* dir) {
|
||||
wchar_t system_buffer[MAX_PATH];
|
||||
system_buffer[0] = 0;
|
||||
DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
|
||||
if (len == 0 || len > MAX_PATH) return false;
|
||||
// TODO(evanm): the old behavior of this function was to always strip the
|
||||
// trailing slash. We duplicate this here, but it shouldn't be necessary
|
||||
// when everyone is using the appropriate FilePath APIs.
|
||||
std::wstring dir_str(system_buffer);
|
||||
file_util::TrimTrailingSeparator(&dir_str);
|
||||
*dir = FilePath(dir_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets the current working directory for the process.
|
||||
bool SetCurrentDirectory(const FilePath& directory) {
|
||||
BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
// Deprecated functions ----------------------------------------------------
|
||||
|
||||
void InsertBeforeExtension(std::wstring* path_str, const std::wstring& suffix) {
|
||||
FilePath path(*path_str);
|
||||
InsertBeforeExtension(&path, suffix);
|
||||
path_str->assign(path.value());
|
||||
}
|
||||
void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) {
|
||||
FilePath path(*file_name);
|
||||
ReplaceExtension(&path, extension);
|
||||
file_name->assign(path.value());
|
||||
}
|
||||
} // namespace file_util
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/process.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
@ -57,12 +59,21 @@ class SharedMemory {
|
|||
// invalid value; NULL for a HANDLE and -1 for a file descriptor)
|
||||
static bool IsHandleValid(const SharedMemoryHandle& handle);
|
||||
|
||||
// IsHandleValid applied to this object's handle.
|
||||
bool IsValid() const;
|
||||
|
||||
// Return invalid handle (see comment above for exact definition).
|
||||
static SharedMemoryHandle NULLHandle();
|
||||
|
||||
// Creates a shared memory segment.
|
||||
// Returns true on success, false on failure.
|
||||
bool Create(size_t size);
|
||||
bool Create(size_t size) { return CreateInternal(size, false); }
|
||||
|
||||
// Creates a shared memory segment that supports the Freeze()
|
||||
// method; see below. (Warning: creating freezeable shared memory
|
||||
// within a sandboxed process isn't possible on some platforms.)
|
||||
// Returns true on success, false on failure.
|
||||
bool CreateFreezeable(size_t size) { return CreateInternal(size, true); }
|
||||
|
||||
// Maps the shared memory into the caller's address space.
|
||||
// Returns true on success, false otherwise. The memory address
|
||||
|
@ -89,10 +100,29 @@ class SharedMemory {
|
|||
// Mapped via Map(). Returns NULL if it is not mapped.
|
||||
void* memory() const { return memory_; }
|
||||
|
||||
// Get access to the underlying OS handle for this segment.
|
||||
// Use of this handle for anything other than an opaque
|
||||
// identifier is not portable.
|
||||
SharedMemoryHandle handle() const;
|
||||
// Extracts the underlying file handle; similar to
|
||||
// GiveToProcess(GetCurrentProcId(), ...) but returns a RAII type.
|
||||
// Like GiveToProcess, this unmaps the memory as a side-effect.
|
||||
mozilla::UniqueFileHandle TakeHandle();
|
||||
|
||||
#ifdef OS_WIN
|
||||
// Used only in gfx/ipc/SharedDIBWin.cpp; should be removable once
|
||||
// NPAPI goes away.
|
||||
HANDLE GetHandle() {
|
||||
freezeable_ = false;
|
||||
return mapped_file_;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make the shared memory object read-only, such that it cannot be
|
||||
// written even if it's sent to an untrusted process. If it was
|
||||
// mapped in this process, it will be unmapped. The object must
|
||||
// have been created with CreateFreezeable(), and must not have
|
||||
// already been shared to another process.
|
||||
//
|
||||
// (See bug 1479960 comment #0 for OS-specific implementation
|
||||
// details.)
|
||||
MOZ_MUST_USE bool Freeze();
|
||||
|
||||
// Closes the open shared memory segment.
|
||||
// It is safe to call Close repeatedly.
|
||||
|
@ -140,6 +170,8 @@ class SharedMemory {
|
|||
bool ShareToProcessCommon(ProcessId target_pid,
|
||||
SharedMemoryHandle* new_handle, bool close_self);
|
||||
|
||||
bool CreateInternal(size_t size, bool freezeable);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// If true indicates this came from an external source so needs extra checks
|
||||
// before being mapped.
|
||||
|
@ -147,9 +179,12 @@ class SharedMemory {
|
|||
HANDLE mapped_file_;
|
||||
#elif defined(OS_POSIX)
|
||||
int mapped_file_;
|
||||
int frozen_file_;
|
||||
size_t mapped_size_;
|
||||
#endif
|
||||
void* memory_;
|
||||
bool read_only_;
|
||||
bool freezeable_;
|
||||
size_t max_size_;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(SharedMemory);
|
||||
|
|
|
@ -20,12 +20,19 @@
|
|||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "prenv.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
SharedMemory::SharedMemory()
|
||||
: mapped_file_(-1), memory_(NULL), read_only_(false), max_size_(0) {}
|
||||
: mapped_file_(-1),
|
||||
frozen_file_(-1),
|
||||
mapped_size_(0),
|
||||
memory_(nullptr),
|
||||
read_only_(false),
|
||||
freezeable_(false),
|
||||
max_size_(0) {}
|
||||
|
||||
SharedMemory::SharedMemory(SharedMemory&& other) {
|
||||
if (this == &other) {
|
||||
|
@ -33,11 +40,16 @@ SharedMemory::SharedMemory(SharedMemory&& other) {
|
|||
}
|
||||
|
||||
mapped_file_ = other.mapped_file_;
|
||||
mapped_size_ = other.mapped_size_;
|
||||
frozen_file_ = other.frozen_file_;
|
||||
memory_ = other.memory_;
|
||||
read_only_ = other.read_only_;
|
||||
freezeable_ = other.freezeable_;
|
||||
max_size_ = other.max_size_;
|
||||
|
||||
other.mapped_file_ = -1;
|
||||
other.mapped_size_ = 0;
|
||||
other.frozen_file_ = -1;
|
||||
other.memory_ = nullptr;
|
||||
}
|
||||
|
||||
|
@ -45,7 +57,9 @@ SharedMemory::~SharedMemory() { Close(); }
|
|||
|
||||
bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
|
||||
DCHECK(mapped_file_ == -1);
|
||||
DCHECK(frozen_file_ == -1);
|
||||
|
||||
freezeable_ = false;
|
||||
mapped_file_ = handle.fd;
|
||||
read_only_ = read_only;
|
||||
return true;
|
||||
|
@ -56,12 +70,97 @@ bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
|
|||
return handle.fd >= 0;
|
||||
}
|
||||
|
||||
bool SharedMemory::IsValid() const { return mapped_file_ >= 0; }
|
||||
|
||||
// static
|
||||
SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); }
|
||||
|
||||
// Workaround for CVE-2018-4435 (https://crbug.com/project-zero/1671);
|
||||
// can be removed when minimum OS version is at least 10.12.
|
||||
#ifdef OS_MACOSX
|
||||
static const char* GetTmpDir() {
|
||||
static const char* const kTmpDir = [] {
|
||||
const char* tmpdir = PR_GetEnv("TMPDIR");
|
||||
if (tmpdir) {
|
||||
return tmpdir;
|
||||
}
|
||||
return "/tmp";
|
||||
}();
|
||||
return kTmpDir;
|
||||
}
|
||||
|
||||
static int FakeShmOpen(const char* name, int oflag, int mode) {
|
||||
CHECK(name[0] == '/');
|
||||
std::string path(GetTmpDir());
|
||||
path += name;
|
||||
return open(path.c_str(), oflag | O_CLOEXEC | O_NOCTTY, mode);
|
||||
}
|
||||
|
||||
static int FakeShmUnlink(const char* name) {
|
||||
CHECK(name[0] == '/');
|
||||
std::string path(GetTmpDir());
|
||||
path += name;
|
||||
return unlink(path.c_str());
|
||||
}
|
||||
|
||||
static bool IsShmOpenSecure() {
|
||||
static const bool kIsSecure = [] {
|
||||
mozilla::UniqueFileHandle rwfd, rofd;
|
||||
std::string name;
|
||||
CHECK(SharedMemory::AppendPosixShmPrefix(&name, getpid()));
|
||||
name += "sectest";
|
||||
// The prefix includes the pid and this will be called at most
|
||||
// once per process, so no need for a counter.
|
||||
rwfd.reset(
|
||||
HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)));
|
||||
// An adversary could steal the name. Handle this semi-gracefully.
|
||||
DCHECK(rwfd);
|
||||
if (!rwfd) {
|
||||
return false;
|
||||
}
|
||||
rofd.reset(shm_open(name.c_str(), O_RDONLY, 0400));
|
||||
CHECK(rofd);
|
||||
CHECK(shm_unlink(name.c_str()) == 0);
|
||||
CHECK(ftruncate(rwfd.get(), 1) == 0);
|
||||
rwfd = nullptr;
|
||||
void* map = mmap(nullptr, 1, PROT_READ, MAP_SHARED, rofd.get(), 0);
|
||||
CHECK(map != MAP_FAILED);
|
||||
bool ok = mprotect(map, 1, PROT_READ | PROT_WRITE) != 0;
|
||||
munmap(map, 1);
|
||||
return ok;
|
||||
}();
|
||||
return kIsSecure;
|
||||
}
|
||||
|
||||
static int SafeShmOpen(bool freezeable, const char* name, int oflag, int mode) {
|
||||
if (!freezeable || IsShmOpenSecure()) {
|
||||
return shm_open(name, oflag, mode);
|
||||
} else {
|
||||
return FakeShmOpen(name, oflag, mode);
|
||||
}
|
||||
}
|
||||
|
||||
static int SafeShmUnlink(bool freezeable, const char* name) {
|
||||
if (!freezeable || IsShmOpenSecure()) {
|
||||
return shm_unlink(name);
|
||||
} else {
|
||||
return FakeShmUnlink(name);
|
||||
}
|
||||
}
|
||||
|
||||
#elif !defined(ANDROID)
|
||||
static int SafeShmOpen(bool freezeable, const char* name, int oflag, int mode) {
|
||||
return shm_open(name, oflag, mode);
|
||||
}
|
||||
|
||||
static int SafeShmUnlink(bool freezeable, const char* name) {
|
||||
return shm_unlink(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
// static
|
||||
bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
|
||||
#if defined(ANDROID) || defined(SHM_ANON)
|
||||
#if defined(ANDROID)
|
||||
return false;
|
||||
#else
|
||||
*str += '/';
|
||||
|
@ -87,35 +186,32 @@ bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
|
|||
// enough for this.
|
||||
StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
|
||||
return true;
|
||||
#endif // !ANDROID && !SHM_ANON
|
||||
#endif // !ANDROID
|
||||
}
|
||||
|
||||
bool SharedMemory::Create(size_t size) {
|
||||
bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
|
||||
read_only_ = false;
|
||||
|
||||
DCHECK(size > 0);
|
||||
DCHECK(mapped_file_ == -1);
|
||||
DCHECK(frozen_file_ == -1);
|
||||
|
||||
int fd;
|
||||
mozilla::UniqueFileHandle fd;
|
||||
mozilla::UniqueFileHandle frozen_fd;
|
||||
bool needs_truncate = true;
|
||||
|
||||
#ifdef ANDROID
|
||||
// Android has its own shared memory facility:
|
||||
fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
|
||||
if (fd < 0) {
|
||||
fd.reset(open("/" ASHMEM_NAME_DEF, O_RDWR, 0600));
|
||||
if (!fd) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
|
||||
if (ioctl(fd.get(), ASHMEM_SET_SIZE, size) != 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
needs_truncate = false;
|
||||
#elif defined(SHM_ANON)
|
||||
// FreeBSD (or any other Unix that might decide to implement this
|
||||
// nice, simple API):
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#else
|
||||
// Generic Unix: shm_open + shm_unlink
|
||||
do {
|
||||
|
@ -126,69 +222,104 @@ bool SharedMemory::Create(size_t size) {
|
|||
CHECK(AppendPosixShmPrefix(&name, getpid()));
|
||||
StringAppendF(&name, "%zu", sNameCounter++);
|
||||
// O_EXCL means the names being predictable shouldn't be a problem.
|
||||
fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
|
||||
if (fd >= 0) {
|
||||
if (shm_unlink(name.c_str()) != 0) {
|
||||
fd.reset(HANDLE_EINTR(SafeShmOpen(freezeable, name.c_str(),
|
||||
O_RDWR | O_CREAT | O_EXCL, 0600)));
|
||||
if (fd) {
|
||||
if (freezeable) {
|
||||
frozen_fd.reset(HANDLE_EINTR(
|
||||
SafeShmOpen(freezeable, name.c_str(), O_RDONLY, 0400)));
|
||||
if (!frozen_fd) {
|
||||
int open_err = errno;
|
||||
SafeShmUnlink(freezeable, name.c_str());
|
||||
DLOG(FATAL) << "failed to re-open freezeable shm: "
|
||||
<< strerror(open_err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (SafeShmUnlink(freezeable, name.c_str()) != 0) {
|
||||
// This shouldn't happen, but if it does: assume the file is
|
||||
// in fact leaked, and bail out now while it's still 0-length.
|
||||
DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (fd < 0 && errno == EEXIST);
|
||||
} while (!fd && errno == EEXIST);
|
||||
#endif
|
||||
|
||||
if (fd < 0) {
|
||||
if (!fd) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (needs_truncate) {
|
||||
if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
|
||||
if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))) != 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mapped_file_ = fd;
|
||||
mapped_file_ = fd.release();
|
||||
frozen_file_ = frozen_fd.release();
|
||||
max_size_ = size;
|
||||
freezeable_ = freezeable;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::Freeze() {
|
||||
DCHECK(!read_only_);
|
||||
CHECK(freezeable_);
|
||||
Unmap();
|
||||
|
||||
#ifdef ANDROID
|
||||
if (ioctl(mapped_file_, ASHMEM_SET_PROT_MASK, PROT_READ) != 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to freeze shm: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
DCHECK(frozen_file_ >= 0);
|
||||
DCHECK(mapped_file_ >= 0);
|
||||
close(mapped_file_);
|
||||
mapped_file_ = frozen_file_;
|
||||
frozen_file_ = -1;
|
||||
#endif
|
||||
|
||||
read_only_ = true;
|
||||
freezeable_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::Map(size_t bytes, void* fixed_address) {
|
||||
if (mapped_file_ == -1) return false;
|
||||
DCHECK(!memory_);
|
||||
|
||||
// Don't use MAP_FIXED when a fixed_address was specified, since that can
|
||||
// replace pages that are alread mapped at that address.
|
||||
memory_ =
|
||||
void* mem =
|
||||
mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
|
||||
MAP_SHARED, mapped_file_, 0);
|
||||
|
||||
bool mmap_succeeded = memory_ != MAP_FAILED;
|
||||
|
||||
DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
|
||||
|
||||
if (mmap_succeeded) {
|
||||
if (fixed_address && memory_ != fixed_address) {
|
||||
bool munmap_succeeded = munmap(memory_, bytes) == 0;
|
||||
DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
|
||||
memory_ = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
max_size_ = bytes;
|
||||
if (mem == MAP_FAILED) {
|
||||
CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
return mmap_succeeded;
|
||||
if (fixed_address && mem != fixed_address) {
|
||||
bool munmap_succeeded = munmap(mem, bytes) == 0;
|
||||
DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
memory_ = mem;
|
||||
mapped_size_ = bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::Unmap() {
|
||||
if (memory_ == NULL) return false;
|
||||
|
||||
munmap(memory_, max_size_);
|
||||
munmap(memory_, mapped_size_);
|
||||
memory_ = NULL;
|
||||
max_size_ = 0;
|
||||
mapped_size_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -202,6 +333,7 @@ void* SharedMemory::FindFreeAddressSpace(size_t size) {
|
|||
bool SharedMemory::ShareToProcessCommon(ProcessId processId,
|
||||
SharedMemoryHandle* new_handle,
|
||||
bool close_self) {
|
||||
freezeable_ = false;
|
||||
const int new_fd = dup(mapped_file_);
|
||||
DCHECK(new_fd >= -1);
|
||||
new_handle->fd = new_fd;
|
||||
|
@ -221,10 +353,20 @@ void SharedMemory::Close(bool unmap_view) {
|
|||
close(mapped_file_);
|
||||
mapped_file_ = -1;
|
||||
}
|
||||
if (frozen_file_ >= 0) {
|
||||
CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen";
|
||||
close(frozen_file_);
|
||||
frozen_file_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
SharedMemoryHandle SharedMemory::handle() const {
|
||||
return FileDescriptor(mapped_file_, false);
|
||||
mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
|
||||
mozilla::UniqueFileHandle fh(mapped_file_);
|
||||
mapped_file_ = -1;
|
||||
// Now that the main fd is removed, reset everything else: close the
|
||||
// frozen fd if present and unmap the memory if mapped.
|
||||
Close();
|
||||
return fh;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#include "base/win_util.h"
|
||||
#include "base/string_util.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
#include "mozilla/RandomNum.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace {
|
||||
// NtQuerySection is an internal (but believed to be stable) API and the
|
||||
|
@ -59,6 +63,7 @@ SharedMemory::SharedMemory()
|
|||
mapped_file_(NULL),
|
||||
memory_(NULL),
|
||||
read_only_(false),
|
||||
freezeable_(false),
|
||||
max_size_(0) {}
|
||||
|
||||
SharedMemory::SharedMemory(SharedMemory&& other) {
|
||||
|
@ -70,6 +75,7 @@ SharedMemory::SharedMemory(SharedMemory&& other) {
|
|||
memory_ = other.memory_;
|
||||
read_only_ = other.read_only_;
|
||||
max_size_ = other.max_size_;
|
||||
freezeable_ = other.freezeable_;
|
||||
external_section_ = other.external_section_;
|
||||
|
||||
other.mapped_file_ = nullptr;
|
||||
|
@ -85,6 +91,7 @@ bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
|
|||
DCHECK(mapped_file_ == NULL);
|
||||
|
||||
external_section_ = true;
|
||||
freezeable_ = false; // just in case
|
||||
mapped_file_ = handle;
|
||||
read_only_ = read_only;
|
||||
return true;
|
||||
|
@ -95,17 +102,65 @@ bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
|
|||
return handle != NULL;
|
||||
}
|
||||
|
||||
bool SharedMemory::IsValid() const { return mapped_file_ != NULL; }
|
||||
|
||||
// static
|
||||
SharedMemoryHandle SharedMemory::NULLHandle() { return NULL; }
|
||||
|
||||
bool SharedMemory::Create(size_t size) {
|
||||
bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
|
||||
DCHECK(mapped_file_ == NULL);
|
||||
read_only_ = false;
|
||||
mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
|
||||
0, static_cast<DWORD>(size), NULL);
|
||||
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
ACL dacl;
|
||||
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = &sd;
|
||||
sa.bInheritHandle = FALSE;
|
||||
|
||||
if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
|
||||
NS_WARN_IF(
|
||||
!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) ||
|
||||
NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoStringN<sizeof("MozSharedMem_") + 16 * 4> name;
|
||||
if (!mozilla::IsWin8Point1OrLater()) {
|
||||
name.AssignLiteral("MozSharedMem_");
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
mozilla::Maybe<uint64_t> randomNum = mozilla::RandomUint64();
|
||||
if (NS_WARN_IF(randomNum.isNothing())) {
|
||||
return false;
|
||||
}
|
||||
name.AppendPrintf("%016llx", *randomNum);
|
||||
}
|
||||
}
|
||||
|
||||
mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
|
||||
0, static_cast<DWORD>(size),
|
||||
name.IsEmpty() ? nullptr : name.get());
|
||||
if (!mapped_file_) return false;
|
||||
|
||||
max_size_ = size;
|
||||
freezeable_ = freezeable;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::Freeze() {
|
||||
DCHECK(!read_only_);
|
||||
CHECK(freezeable_);
|
||||
Unmap();
|
||||
|
||||
if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_, GetCurrentProcess(),
|
||||
&mapped_file_, GENERIC_READ | FILE_MAP_READ, false,
|
||||
DUPLICATE_CLOSE_SOURCE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read_only_ = true;
|
||||
freezeable_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -146,6 +201,7 @@ void* SharedMemory::FindFreeAddressSpace(size_t size) {
|
|||
bool SharedMemory::ShareToProcessCommon(ProcessId processId,
|
||||
SharedMemoryHandle* new_handle,
|
||||
bool close_self) {
|
||||
freezeable_ = false;
|
||||
*new_handle = 0;
|
||||
DWORD access = FILE_MAP_READ | SECTION_QUERY;
|
||||
DWORD options = 0;
|
||||
|
@ -184,6 +240,11 @@ void SharedMemory::Close(bool unmap_view) {
|
|||
}
|
||||
}
|
||||
|
||||
SharedMemoryHandle SharedMemory::handle() const { return mapped_file_; }
|
||||
mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
|
||||
mozilla::UniqueFileHandle fh(mapped_file_);
|
||||
mapped_file_ = NULL;
|
||||
Unmap();
|
||||
return fh;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
|
@ -27,17 +27,12 @@ class SharedPreferenceSerializer final {
|
|||
|
||||
bool SerializeToSharedMemory();
|
||||
|
||||
base::SharedMemoryHandle GetSharedMemoryHandle() const {
|
||||
return mShm.handle();
|
||||
}
|
||||
|
||||
const FileDescriptor::UniquePlatformHandle& GetPrefMapHandle() const {
|
||||
return mPrefMapHandle;
|
||||
}
|
||||
|
||||
nsACString::size_type GetPrefLength() const { return mPrefs.Length(); }
|
||||
|
||||
size_t GetPrefMapSize() const { return mPrefMapSize; }
|
||||
size_t GetPrefsLength() const { return mPrefsLength; }
|
||||
|
||||
const UniqueFileHandle& GetPrefsHandle() const { return mPrefsHandle; }
|
||||
|
||||
const UniqueFileHandle& GetPrefMapHandle() const { return mPrefMapHandle; }
|
||||
|
||||
void AddSharedPrefCmdLineArgs(GeckoChildProcessHost& procHost,
|
||||
std::vector<std::string>& aExtraOpts) const;
|
||||
|
@ -45,9 +40,9 @@ class SharedPreferenceSerializer final {
|
|||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(SharedPreferenceSerializer);
|
||||
size_t mPrefMapSize;
|
||||
FileDescriptor::UniquePlatformHandle mPrefMapHandle;
|
||||
base::SharedMemory mShm;
|
||||
nsAutoCStringN<1024> mPrefs;
|
||||
size_t mPrefsLength;
|
||||
UniqueFileHandle mPrefMapHandle;
|
||||
UniqueFileHandle mPrefsHandle;
|
||||
};
|
||||
|
||||
class SharedPreferenceDeserializer final {
|
||||
|
|
|
@ -24,32 +24,36 @@ SharedPreferenceSerializer::~SharedPreferenceSerializer() {
|
|||
SharedPreferenceSerializer::SharedPreferenceSerializer(
|
||||
SharedPreferenceSerializer&& aOther)
|
||||
: mPrefMapSize(aOther.mPrefMapSize),
|
||||
mPrefsLength(aOther.mPrefsLength),
|
||||
mPrefMapHandle(std::move(aOther.mPrefMapHandle)),
|
||||
mShm(std::move(aOther.mShm)),
|
||||
mPrefs(std::move(aOther.mPrefs)) {
|
||||
mPrefsHandle(std::move(aOther.mPrefsHandle)) {
|
||||
MOZ_COUNT_CTOR(SharedPreferenceSerializer);
|
||||
}
|
||||
|
||||
bool SharedPreferenceSerializer::SerializeToSharedMemory() {
|
||||
mPrefMapHandle =
|
||||
Preferences::EnsureSnapshot(&mPrefMapSize).ClonePlatformHandle();
|
||||
Preferences::EnsureSnapshot(&mPrefMapSize).TakePlatformHandle();
|
||||
|
||||
// Serialize the early prefs.
|
||||
Preferences::SerializePreferences(mPrefs);
|
||||
nsAutoCStringN<1024> prefs;
|
||||
Preferences::SerializePreferences(prefs);
|
||||
mPrefsLength = prefs.Length();
|
||||
|
||||
base::SharedMemory shm;
|
||||
// Set up the shared memory.
|
||||
if (!mShm.Create(mPrefs.Length())) {
|
||||
if (!shm.Create(prefs.Length())) {
|
||||
NS_ERROR("failed to create shared memory in the parent");
|
||||
return false;
|
||||
}
|
||||
if (!mShm.Map(mPrefs.Length())) {
|
||||
if (!shm.Map(prefs.Length())) {
|
||||
NS_ERROR("failed to map shared memory in the parent");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the serialized prefs into the shared memory.
|
||||
memcpy(static_cast<char*>(mShm.memory()), mPrefs.get(), mPrefs.Length());
|
||||
memcpy(static_cast<char*>(shm.memory()), prefs.get(), mPrefsLength);
|
||||
|
||||
mPrefsHandle = shm.TakeHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -65,11 +69,10 @@ void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
|
|||
#if defined(XP_WIN)
|
||||
// Record the handle as to-be-shared, and pass it via a command flag. This
|
||||
// works because Windows handles are system-wide.
|
||||
HANDLE prefsHandle = GetSharedMemoryHandle();
|
||||
procHost.AddHandleToShare(prefsHandle);
|
||||
procHost.AddHandleToShare(GetPrefsHandle().get());
|
||||
procHost.AddHandleToShare(GetPrefMapHandle().get());
|
||||
aExtraOpts.push_back("-prefsHandle");
|
||||
aExtraOpts.push_back(formatPtrArg(prefsHandle).get());
|
||||
aExtraOpts.push_back(formatPtrArg(GetPrefsHandle().get()).get());
|
||||
aExtraOpts.push_back("-prefMapHandle");
|
||||
aExtraOpts.push_back(formatPtrArg(GetPrefMapHandle().get()).get());
|
||||
#else
|
||||
|
@ -80,13 +83,13 @@ void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
|
|||
// Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
|
||||
// and the fixed fd isn't used. However, we still need to mark it for
|
||||
// remapping so it doesn't get closed in the child.
|
||||
procHost.AddFdToRemap(GetSharedMemoryHandle().fd, kPrefsFileDescriptor);
|
||||
procHost.AddFdToRemap(GetPrefsHandle().get(), kPrefsFileDescriptor);
|
||||
procHost.AddFdToRemap(GetPrefMapHandle().get(), kPrefMapFileDescriptor);
|
||||
#endif
|
||||
|
||||
// Pass the lengths via command line flags.
|
||||
aExtraOpts.push_back("-prefsLen");
|
||||
aExtraOpts.push_back(formatPtrArg(GetPrefLength()).get());
|
||||
aExtraOpts.push_back(formatPtrArg(GetPrefsLength()).get());
|
||||
aExtraOpts.push_back("-prefMapSize");
|
||||
aExtraOpts.push_back(formatPtrArg(GetPrefMapSize()).get());
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ class SharedMemory {
|
|||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedMemory)
|
||||
|
||||
static void SystemProtect(char* aAddr, size_t aSize, int aRights);
|
||||
static MOZ_MUST_USE bool SystemProtectFallible(char* aAddr, size_t aSize,
|
||||
int aRights);
|
||||
static size_t SystemPageSize();
|
||||
static size_t PageAlignedSize(size_t aSize);
|
||||
|
||||
|
|
|
@ -13,12 +13,19 @@ namespace mozilla {
|
|||
namespace ipc {
|
||||
|
||||
void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
|
||||
if (!SystemProtectFallible(aAddr, aSize, aRights)) {
|
||||
MOZ_CRASH("can't mprotect()");
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
|
||||
int aRights) {
|
||||
int flags = 0;
|
||||
if (aRights & RightsRead) flags |= PROT_READ;
|
||||
if (aRights & RightsWrite) flags |= PROT_WRITE;
|
||||
if (RightsNone == aRights) flags = PROT_NONE;
|
||||
|
||||
if (0 < mprotect(aAddr, aSize, flags)) MOZ_CRASH("can't mprotect()");
|
||||
return 0 == mprotect(aAddr, aSize, flags);
|
||||
}
|
||||
|
||||
size_t SharedMemory::SystemPageSize() { return sysconf(_SC_PAGESIZE); }
|
||||
|
|
|
@ -12,6 +12,13 @@ namespace mozilla {
|
|||
namespace ipc {
|
||||
|
||||
void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
|
||||
if (!SystemProtectFallible(aAddr, aSize, aRights)) {
|
||||
MOZ_CRASH("can't VirtualProtect()");
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
|
||||
int aRights) {
|
||||
DWORD flags;
|
||||
if ((aRights & RightsRead) && (aRights & RightsWrite))
|
||||
flags = PAGE_READWRITE;
|
||||
|
@ -21,8 +28,7 @@ void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
|
|||
flags = PAGE_NOACCESS;
|
||||
|
||||
DWORD oldflags;
|
||||
if (!VirtualProtect(aAddr, aSize, flags, &oldflags))
|
||||
MOZ_CRASH("can't VirtualProtect()");
|
||||
return VirtualProtect(aAddr, aSize, flags, &oldflags);
|
||||
}
|
||||
|
||||
size_t SharedMemory::SystemPageSize() {
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "base/shared_memory.h"
|
||||
|
||||
#include "base/process_util.h"
|
||||
#include "mozilla/ipc/SharedMemory.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Try to map a frozen shm for writing. Threat model: the process is
|
||||
// compromised and then receives a frozen handle.
|
||||
TEST(IPCSharedMemory, FreezeAndMapRW)
|
||||
{
|
||||
base::SharedMemory shm;
|
||||
|
||||
// Create and initialize
|
||||
ASSERT_TRUE(shm.CreateFreezeable(1));
|
||||
ASSERT_TRUE(shm.Map(1));
|
||||
auto mem = reinterpret_cast<char*>(shm.memory());
|
||||
ASSERT_TRUE(mem);
|
||||
*mem = 'A';
|
||||
|
||||
// Freeze
|
||||
ASSERT_TRUE(shm.Freeze());
|
||||
ASSERT_FALSE(shm.memory());
|
||||
|
||||
// Re-create as writeable
|
||||
auto handle = base::SharedMemory::NULLHandle();
|
||||
ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
|
||||
ASSERT_TRUE(shm.IsHandleValid(handle));
|
||||
ASSERT_FALSE(shm.IsValid());
|
||||
ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ false));
|
||||
ASSERT_TRUE(shm.IsValid());
|
||||
|
||||
// This should fail
|
||||
EXPECT_FALSE(shm.Map(1));
|
||||
}
|
||||
|
||||
// Try to restore write permissions to a frozen mapping. Threat
|
||||
// model: the process has mapped frozen shm normally and then is
|
||||
// compromised, or as for FreezeAndMapRW (see also the
|
||||
// proof-of-concept at https://crbug.com/project-zero/1671 ).
|
||||
TEST(IPCSharedMemory, FreezeAndReprotect)
|
||||
{
|
||||
base::SharedMemory shm;
|
||||
|
||||
// Create and initialize
|
||||
ASSERT_TRUE(shm.CreateFreezeable(1));
|
||||
ASSERT_TRUE(shm.Map(1));
|
||||
auto mem = reinterpret_cast<char*>(shm.memory());
|
||||
ASSERT_TRUE(mem);
|
||||
*mem = 'A';
|
||||
|
||||
// Freeze
|
||||
ASSERT_TRUE(shm.Freeze());
|
||||
ASSERT_FALSE(shm.memory());
|
||||
|
||||
// Re-map
|
||||
ASSERT_TRUE(shm.Map(1));
|
||||
mem = reinterpret_cast<char*>(shm.memory());
|
||||
ASSERT_EQ(*mem, 'A');
|
||||
|
||||
// Try to alter protection; should fail
|
||||
EXPECT_FALSE(ipc::SharedMemory::SystemProtectFallible(
|
||||
mem, 1, ipc::SharedMemory::RightsReadWrite));
|
||||
}
|
||||
|
||||
#ifndef XP_WIN
|
||||
// This essentially tests whether FreezeAndReprotect would have failed
|
||||
// without the freeze. It doesn't work on Windows: VirtualProtect
|
||||
// can't exceed the permissions set in MapViewOfFile regardless of the
|
||||
// security status of the original handle.
|
||||
TEST(IPCSharedMemory, Reprotect)
|
||||
{
|
||||
base::SharedMemory shm;
|
||||
|
||||
// Create and initialize
|
||||
ASSERT_TRUE(shm.CreateFreezeable(1));
|
||||
ASSERT_TRUE(shm.Map(1));
|
||||
auto mem = reinterpret_cast<char*>(shm.memory());
|
||||
ASSERT_TRUE(mem);
|
||||
*mem = 'A';
|
||||
|
||||
// Re-create as read-only
|
||||
auto handle = base::SharedMemory::NULLHandle();
|
||||
ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
|
||||
ASSERT_TRUE(shm.IsHandleValid(handle));
|
||||
ASSERT_FALSE(shm.IsValid());
|
||||
ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ true));
|
||||
ASSERT_TRUE(shm.IsValid());
|
||||
|
||||
// Re-map
|
||||
ASSERT_TRUE(shm.Map(1));
|
||||
mem = reinterpret_cast<char*>(shm.memory());
|
||||
ASSERT_EQ(*mem, 'A');
|
||||
|
||||
// Try to alter protection; should succeed, because not frozen
|
||||
EXPECT_TRUE(ipc::SharedMemory::SystemProtectFallible(
|
||||
mem, 1, ipc::SharedMemory::RightsReadWrite));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,15 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
Library('ipctest')
|
||||
|
||||
SOURCES += [
|
||||
'TestSharedMemory.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'app',
|
||||
'chromium',
|
||||
'glue',
|
||||
'ipdl',
|
||||
|
@ -17,7 +18,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
|
|||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DIRS += ['mscom']
|
||||
|
||||
DIRS += ['app']
|
||||
TEST_DIRS += [
|
||||
'gtest',
|
||||
]
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "IPC")
|
||||
|
|
|
@ -256,6 +256,7 @@ Result CallScriptMethod(HandleDebuggerScript obj,
|
|||
return (script->*ifJSScript)();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(obj->getReferent().is<LazyScript*>());
|
||||
LazyScript* lazyScript = obj->getReferent().as<LazyScript*>();
|
||||
return (lazyScript->*ifLazyScript)();
|
||||
}
|
||||
|
@ -265,8 +266,7 @@ bool DebuggerScript::getIsGeneratorFunction(JSContext* cx, unsigned argc,
|
|||
Value* vp) {
|
||||
THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isGeneratorFunction)",
|
||||
args, obj);
|
||||
args.rval().setBoolean(
|
||||
CallScriptMethod(obj, &JSScript::isGenerator, &LazyScript::isGenerator));
|
||||
args.rval().setBoolean(obj->getReferentScript()->isGenerator());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -275,8 +275,7 @@ bool DebuggerScript::getIsAsyncFunction(JSContext* cx, unsigned argc,
|
|||
Value* vp) {
|
||||
THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isAsyncFunction)",
|
||||
args, obj);
|
||||
args.rval().setBoolean(
|
||||
CallScriptMethod(obj, &JSScript::isAsync, &LazyScript::isAsync));
|
||||
args.rval().setBoolean(obj->getReferentScript()->isAsync());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1149,7 +1149,7 @@ XDRResult js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
script->column_ = column;
|
||||
script->immutableFlags_ = immutableFlags;
|
||||
|
||||
if (script->hasFlag(ImmutableFlags::ArgsHasVarBinding)) {
|
||||
if (script->argumentsHasVarBinding()) {
|
||||
// Call setArgumentsHasVarBinding to initialize the
|
||||
// NeedsArgsAnalysis flag.
|
||||
script->setArgumentsHasVarBinding();
|
||||
|
@ -4931,7 +4931,7 @@ Scope* JSScript::innermostScope(jsbytecode* pc) {
|
|||
}
|
||||
|
||||
void JSScript::setArgumentsHasVarBinding() {
|
||||
setFlag(ImmutableFlags::ArgsHasVarBinding);
|
||||
setFlag(ImmutableFlags::ArgumentsHasVarBinding);
|
||||
setFlag(MutableFlags::NeedsArgsAnalysis);
|
||||
}
|
||||
|
||||
|
|
|
@ -1337,6 +1337,9 @@ class ScriptSourceObject : public NativeObject {
|
|||
};
|
||||
};
|
||||
|
||||
enum class GeneratorKind : bool { NotGenerator, Generator };
|
||||
enum class FunctionAsyncKind : bool { SyncFunction, AsyncFunction };
|
||||
|
||||
// This class contains fields and accessors that are common to both lazy and
|
||||
// non-lazy interpreted scripts. This must be located at offset +0 of any
|
||||
// derived classes in order for the 'jitCodeRaw' mechanism to work with the
|
||||
|
@ -1384,7 +1387,7 @@ class BaseScript : public gc::TenuredCell {
|
|||
uint32_t column_ = 0; // Count of Code Points
|
||||
|
||||
// See ImmutableFlags / MutableFlags below for definitions. These are stored
|
||||
// as uint32_t instead of bitfields to make it more predicatable to access
|
||||
// as uint32_t instead of bitfields to make it more predictable to access
|
||||
// from JIT code.
|
||||
uint32_t immutableFlags_ = 0;
|
||||
uint32_t mutableFlags_ = 0;
|
||||
|
@ -1408,6 +1411,9 @@ class BaseScript : public gc::TenuredCell {
|
|||
// initialized. These flags should likely be preserved when serializing
|
||||
// (XDR) or copying (CopyScript) this script. This is only public for the
|
||||
// JITs.
|
||||
//
|
||||
// Specific accessors for flag values are defined with
|
||||
// IMMUTABLE_FLAG_* macros below.
|
||||
enum class ImmutableFlags : uint32_t {
|
||||
// No need for result value of last expression statement.
|
||||
NoScriptRval = 1 << 0,
|
||||
|
@ -1470,7 +1476,7 @@ class BaseScript : public gc::TenuredCell {
|
|||
HasRest = 1 << 20,
|
||||
|
||||
// See comments below.
|
||||
ArgsHasVarBinding = 1 << 21,
|
||||
ArgumentsHasVarBinding = 1 << 21,
|
||||
|
||||
// Script came from eval().
|
||||
IsForEval = 1 << 22,
|
||||
|
@ -1492,6 +1498,9 @@ class BaseScript : public gc::TenuredCell {
|
|||
|
||||
// Mutable flags typically store information about runtime or deoptimization
|
||||
// behavior of this script. This is only public for the JITs.
|
||||
//
|
||||
// Specific accessors for flag values are defined with
|
||||
// MUTABLE_FLAG_* macros below.
|
||||
enum class MutableFlags : uint32_t {
|
||||
// Number of times the |warmUpCount| was forcibly discarded. The counter is
|
||||
// reset when a script is successfully jit-compiled.
|
||||
|
@ -1611,6 +1620,133 @@ class BaseScript : public gc::TenuredCell {
|
|||
|
||||
void traceChildren(JSTracer* trc);
|
||||
|
||||
// Specific flag accessors
|
||||
|
||||
#define FLAG_GETTER(enumName, enumEntry, lowerName) \
|
||||
public: \
|
||||
bool lowerName() const { return hasFlag(enumName::enumEntry); }
|
||||
|
||||
#define FLAG_GETTER_SETTER(enumName, enumEntry, setterLevel, lowerName, name) \
|
||||
setterLevel: \
|
||||
void set##name() { setFlag(enumName::enumEntry); } \
|
||||
void set##name(bool b) { setFlag(enumName::enumEntry, b); } \
|
||||
void clear##name() { clearFlag(enumName::enumEntry); } \
|
||||
\
|
||||
public: \
|
||||
bool lowerName() const { return hasFlag(enumName::enumEntry); }
|
||||
|
||||
#define IMMUTABLE_FLAG_GETTER(lowerName, name) \
|
||||
FLAG_GETTER(ImmutableFlags, name, lowerName)
|
||||
#define IMMUTABLE_FLAG_GETTER_SETTER(lowerName, name) \
|
||||
FLAG_GETTER_SETTER(ImmutableFlags, name, protected, lowerName, name)
|
||||
#define IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(lowerName, name) \
|
||||
FLAG_GETTER_SETTER(ImmutableFlags, name, public, lowerName, name)
|
||||
#define IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC(enumName, lowerName, name) \
|
||||
FLAG_GETTER_SETTER(ImmutableFlags, enumName, public, lowerName, name)
|
||||
#define MUTABLE_FLAG_GETTER(lowerName, name) \
|
||||
FLAG_GETTER(MutableFlags, name, lowerName)
|
||||
#define MUTABLE_FLAG_GETTER_SETTER(lowerName, name) \
|
||||
FLAG_GETTER_SETTER(MutableFlags, name, public, lowerName, name)
|
||||
|
||||
IMMUTABLE_FLAG_GETTER(noScriptRval, NoScriptRval)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(strict, Strict)
|
||||
IMMUTABLE_FLAG_GETTER(hasNonSyntacticScope, HasNonSyntacticScope)
|
||||
IMMUTABLE_FLAG_GETTER(selfHosted, SelfHosted)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(bindingsAccessedDynamically,
|
||||
BindingsAccessedDynamically)
|
||||
IMMUTABLE_FLAG_GETTER(funHasExtensibleScope, FunHasExtensibleScope)
|
||||
IMMUTABLE_FLAG_GETTER(hasCallSiteObj, HasCallSiteObj)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER(functionHasThisBinding, FunctionHasThisBinding)
|
||||
// Alternate shorter name used throughout the codebase
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC(FunctionHasThisBinding,
|
||||
hasThisBinding, HasThisBinding)
|
||||
// FunctionHasExtraBodyVarScope: custom logic below.
|
||||
IMMUTABLE_FLAG_GETTER_SETTER(hasMappedArgsObj, HasMappedArgsObj)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER(hasInnerFunctions, HasInnerFunctions)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(needsHomeObject, NeedsHomeObject)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(isDerivedClassConstructor,
|
||||
IsDerivedClassConstructor)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(isDefaultClassConstructor,
|
||||
IsDefaultClassConstructor)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(treatAsRunOnce, TreatAsRunOnce)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER(isLikelyConstructorWrapper,
|
||||
IsLikelyConstructorWrapper)
|
||||
// alternate name used throughout the codebase
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC(IsLikelyConstructorWrapper,
|
||||
likelyConstructorWrapper,
|
||||
LikelyConstructorWrapper)
|
||||
IMMUTABLE_FLAG_GETTER(isGenerator, IsGenerator)
|
||||
IMMUTABLE_FLAG_GETTER(isAsync, IsAsync)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(hasRest, HasRest)
|
||||
// See ContextFlags::funArgumentsHasLocalBinding comment.
|
||||
// N.B.: no setter -- custom logic in JSScript.
|
||||
IMMUTABLE_FLAG_GETTER(argumentsHasVarBinding, ArgumentsHasVarBinding)
|
||||
// IsForEval: custom logic below.
|
||||
// IsModule: custom logic below.
|
||||
IMMUTABLE_FLAG_GETTER_SETTER(needsFunctionEnvironmentObjects,
|
||||
NeedsFunctionEnvironmentObjects)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(shouldDeclareArguments,
|
||||
ShouldDeclareArguments)
|
||||
IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(hasDirectEval, HasDirectEval)
|
||||
|
||||
MUTABLE_FLAG_GETTER_SETTER(warnedAboutUndefinedProp, WarnedAboutUndefinedProp)
|
||||
MUTABLE_FLAG_GETTER_SETTER(hasRunOnce, HasRunOnce)
|
||||
MUTABLE_FLAG_GETTER_SETTER(hasBeenCloned, HasBeenCloned)
|
||||
MUTABLE_FLAG_GETTER_SETTER(trackRecordReplayProgress,
|
||||
TrackRecordReplayProgress)
|
||||
// N.B.: no setter -- custom logic in JSScript.
|
||||
MUTABLE_FLAG_GETTER(hasScriptCounts, HasScriptCounts)
|
||||
// Access the flag for whether this script has a DebugScript in its realm's
|
||||
// map. This should only be used by the DebugScript class.
|
||||
MUTABLE_FLAG_GETTER_SETTER(hasDebugScript, HasDebugScript)
|
||||
MUTABLE_FLAG_GETTER_SETTER(doNotRelazify, DoNotRelazify)
|
||||
MUTABLE_FLAG_GETTER_SETTER(failedBoundsCheck, FailedBoundsCheck)
|
||||
MUTABLE_FLAG_GETTER_SETTER(failedShapeGuard, FailedShapeGuard)
|
||||
MUTABLE_FLAG_GETTER_SETTER(hadFrequentBailouts, HadFrequentBailouts)
|
||||
MUTABLE_FLAG_GETTER_SETTER(hadOverflowBailout, HadOverflowBailout)
|
||||
MUTABLE_FLAG_GETTER_SETTER(uninlineable, Uninlineable)
|
||||
MUTABLE_FLAG_GETTER_SETTER(invalidatedIdempotentCache,
|
||||
InvalidatedIdempotentCache)
|
||||
MUTABLE_FLAG_GETTER_SETTER(failedLexicalCheck, FailedLexicalCheck)
|
||||
MUTABLE_FLAG_GETTER_SETTER(needsArgsAnalysis, NeedsArgsAnalysis)
|
||||
// NeedsArgsObj: custom logic below.
|
||||
MUTABLE_FLAG_GETTER_SETTER(hideScriptFromDebugger, HideScriptFromDebugger)
|
||||
MUTABLE_FLAG_GETTER_SETTER(spewEnabled, SpewEnabled)
|
||||
|
||||
#undef IMMUTABLE_FLAG_GETTER
|
||||
#undef IMMUTABLE_FLAG_GETTER_SETTER
|
||||
#undef IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC
|
||||
#undef IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC
|
||||
#undef MUTABLE_FLAG_GETTER
|
||||
#undef MUTABLE_FLAG_GETTER_SETTER
|
||||
#undef FLAG_GETTER
|
||||
#undef FLAG_GETTER_SETTER
|
||||
|
||||
GeneratorKind generatorKind() const {
|
||||
return isGenerator() ? GeneratorKind::Generator
|
||||
: GeneratorKind::NotGenerator;
|
||||
}
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A script only gets its generator kind set as part of initialization,
|
||||
// so it can only transition from NotGenerator.
|
||||
MOZ_ASSERT(!isGenerator());
|
||||
if (kind == GeneratorKind::Generator) {
|
||||
setFlag(ImmutableFlags::IsGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
FunctionAsyncKind asyncKind() const {
|
||||
return isAsync() ? FunctionAsyncKind::AsyncFunction
|
||||
: FunctionAsyncKind::SyncFunction;
|
||||
}
|
||||
|
||||
void setAsyncKind(FunctionAsyncKind kind) {
|
||||
if (kind == FunctionAsyncKind::AsyncFunction) {
|
||||
setFlag(ImmutableFlags::IsAsync);
|
||||
}
|
||||
}
|
||||
|
||||
// JIT accessors
|
||||
static constexpr size_t offsetOfJitCodeRaw() {
|
||||
return offsetof(BaseScript, jitCodeRaw_);
|
||||
|
@ -1623,9 +1759,6 @@ class BaseScript : public gc::TenuredCell {
|
|||
}
|
||||
};
|
||||
|
||||
enum class GeneratorKind : bool { NotGenerator, Generator };
|
||||
enum class FunctionAsyncKind : bool { SyncFunction, AsyncFunction };
|
||||
|
||||
struct FieldInitializers {
|
||||
#ifdef DEBUG
|
||||
bool valid;
|
||||
|
@ -2390,37 +2523,6 @@ class JSScript : public js::BaseScript {
|
|||
|
||||
size_t funLength() const { return immutableScriptData()->funLength; }
|
||||
|
||||
bool noScriptRval() const { return hasFlag(ImmutableFlags::NoScriptRval); }
|
||||
|
||||
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
|
||||
|
||||
bool hasNonSyntacticScope() const {
|
||||
return hasFlag(ImmutableFlags::HasNonSyntacticScope);
|
||||
}
|
||||
|
||||
bool selfHosted() const { return hasFlag(ImmutableFlags::SelfHosted); }
|
||||
bool bindingsAccessedDynamically() const {
|
||||
return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||
}
|
||||
bool funHasExtensibleScope() const {
|
||||
return hasFlag(ImmutableFlags::FunHasExtensibleScope);
|
||||
}
|
||||
bool hasDirectEval() const { return hasFlag(ImmutableFlags::HasDirectEval); }
|
||||
|
||||
bool hasCallSiteObj() const {
|
||||
return hasFlag(ImmutableFlags::HasCallSiteObj);
|
||||
}
|
||||
|
||||
bool treatAsRunOnce() const {
|
||||
return hasFlag(ImmutableFlags::TreatAsRunOnce);
|
||||
}
|
||||
bool hasRunOnce() const { return hasFlag(MutableFlags::HasRunOnce); }
|
||||
bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
|
||||
|
||||
void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
|
||||
void setHasRunOnce() { setFlag(MutableFlags::HasRunOnce); }
|
||||
void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
|
||||
|
||||
void cacheForEval() {
|
||||
MOZ_ASSERT(isForEval());
|
||||
// IsEvalCacheCandidate will make sure that there's nothing in this
|
||||
|
@ -2429,65 +2531,8 @@ class JSScript : public js::BaseScript {
|
|||
clearFlag(MutableFlags::HasRunOnce);
|
||||
}
|
||||
|
||||
bool isLikelyConstructorWrapper() const {
|
||||
return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||
}
|
||||
void setLikelyConstructorWrapper() {
|
||||
setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||
}
|
||||
|
||||
bool failedBoundsCheck() const {
|
||||
return hasFlag(MutableFlags::FailedBoundsCheck);
|
||||
}
|
||||
bool failedShapeGuard() const {
|
||||
return hasFlag(MutableFlags::FailedShapeGuard);
|
||||
}
|
||||
bool hadFrequentBailouts() const {
|
||||
return hasFlag(MutableFlags::HadFrequentBailouts);
|
||||
}
|
||||
bool hadOverflowBailout() const {
|
||||
return hasFlag(MutableFlags::HadOverflowBailout);
|
||||
}
|
||||
bool uninlineable() const { return hasFlag(MutableFlags::Uninlineable); }
|
||||
bool invalidatedIdempotentCache() const {
|
||||
return hasFlag(MutableFlags::InvalidatedIdempotentCache);
|
||||
}
|
||||
bool failedLexicalCheck() const {
|
||||
return hasFlag(MutableFlags::FailedLexicalCheck);
|
||||
}
|
||||
bool isDefaultClassConstructor() const {
|
||||
return hasFlag(ImmutableFlags::IsDefaultClassConstructor);
|
||||
}
|
||||
|
||||
void setFailedBoundsCheck() { setFlag(MutableFlags::FailedBoundsCheck); }
|
||||
void setFailedShapeGuard() { setFlag(MutableFlags::FailedShapeGuard); }
|
||||
void setHadFrequentBailouts() { setFlag(MutableFlags::HadFrequentBailouts); }
|
||||
void setHadOverflowBailout() { setFlag(MutableFlags::HadOverflowBailout); }
|
||||
void setUninlineable() { setFlag(MutableFlags::Uninlineable); }
|
||||
void setInvalidatedIdempotentCache() {
|
||||
setFlag(MutableFlags::InvalidatedIdempotentCache);
|
||||
}
|
||||
void setFailedLexicalCheck() { setFlag(MutableFlags::FailedLexicalCheck); }
|
||||
void setIsDefaultClassConstructor() {
|
||||
setFlag(ImmutableFlags::IsDefaultClassConstructor);
|
||||
}
|
||||
|
||||
bool hasScriptCounts() const {
|
||||
return hasFlag(MutableFlags::HasScriptCounts);
|
||||
}
|
||||
bool hasScriptName();
|
||||
|
||||
bool warnedAboutUndefinedProp() const {
|
||||
return hasFlag(MutableFlags::WarnedAboutUndefinedProp);
|
||||
}
|
||||
void setWarnedAboutUndefinedProp() {
|
||||
setFlag(MutableFlags::WarnedAboutUndefinedProp);
|
||||
}
|
||||
|
||||
/* See ContextFlags::funArgumentsHasLocalBinding comment. */
|
||||
bool argumentsHasVarBinding() const {
|
||||
return hasFlag(ImmutableFlags::ArgsHasVarBinding);
|
||||
}
|
||||
void setArgumentsHasVarBinding();
|
||||
bool argumentsAliasesFormals() const {
|
||||
return argumentsHasVarBinding() && hasMappedArgsObj();
|
||||
|
@ -2497,35 +2542,11 @@ class JSScript : public js::BaseScript {
|
|||
return isGenerator() ? js::GeneratorKind::Generator
|
||||
: js::GeneratorKind::NotGenerator;
|
||||
}
|
||||
bool isGenerator() const { return hasFlag(ImmutableFlags::IsGenerator); }
|
||||
|
||||
js::FunctionAsyncKind asyncKind() const {
|
||||
return isAsync() ? js::FunctionAsyncKind::AsyncFunction
|
||||
: js::FunctionAsyncKind::SyncFunction;
|
||||
}
|
||||
bool isAsync() const { return hasFlag(ImmutableFlags::IsAsync); }
|
||||
|
||||
bool hasRest() const { return hasFlag(ImmutableFlags::HasRest); }
|
||||
|
||||
bool hideScriptFromDebugger() const {
|
||||
return hasFlag(MutableFlags::HideScriptFromDebugger);
|
||||
}
|
||||
void clearHideScriptFromDebugger() {
|
||||
clearFlag(MutableFlags::HideScriptFromDebugger);
|
||||
}
|
||||
|
||||
bool spewEnabled() const { return hasFlag(MutableFlags::SpewEnabled); }
|
||||
void setSpewEnabled(bool enabled) {
|
||||
setFlag(MutableFlags::SpewEnabled, enabled);
|
||||
}
|
||||
|
||||
bool needsHomeObject() const {
|
||||
return hasFlag(ImmutableFlags::NeedsHomeObject);
|
||||
}
|
||||
|
||||
bool isDerivedClassConstructor() const {
|
||||
return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||
}
|
||||
|
||||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
|
@ -2549,14 +2570,6 @@ class JSScript : public js::BaseScript {
|
|||
static void argumentsOptimizationFailed(JSContext* cx,
|
||||
js::HandleScript script);
|
||||
|
||||
bool hasMappedArgsObj() const {
|
||||
return hasFlag(ImmutableFlags::HasMappedArgsObj);
|
||||
}
|
||||
|
||||
bool functionHasThisBinding() const {
|
||||
return hasFlag(ImmutableFlags::FunctionHasThisBinding);
|
||||
}
|
||||
|
||||
void setFieldInitializers(js::FieldInitializers fieldInitializers) {
|
||||
MOZ_ASSERT(data_);
|
||||
data_->setFieldInitializers(fieldInitializers);
|
||||
|
@ -2579,13 +2592,6 @@ class JSScript : public js::BaseScript {
|
|||
return needsArgsObj() && hasMappedArgsObj();
|
||||
}
|
||||
|
||||
bool hasDoNotRelazify() const { return hasFlag(MutableFlags::DoNotRelazify); }
|
||||
void setDoNotRelazify(bool b) { setFlag(MutableFlags::DoNotRelazify, b); }
|
||||
|
||||
bool hasInnerFunctions() const {
|
||||
return hasFlag(ImmutableFlags::HasInnerFunctions);
|
||||
}
|
||||
|
||||
static constexpr size_t offsetOfScriptData() {
|
||||
return offsetof(JSScript, scriptData_);
|
||||
}
|
||||
|
@ -2648,7 +2654,7 @@ class JSScript : public js::BaseScript {
|
|||
bool isRelazifiableIgnoringJitCode() const {
|
||||
return (selfHosted() || lazyScript) && !hasInnerFunctions() &&
|
||||
!isGenerator() && !isAsync() && !isDefaultClassConstructor() &&
|
||||
!hasDoNotRelazify() && !hasCallSiteObj();
|
||||
!doNotRelazify() && !hasCallSiteObj();
|
||||
}
|
||||
bool isRelazifiable() const {
|
||||
MOZ_ASSERT_IF(hasBaselineScript() || hasIonScript(), jitScript_);
|
||||
|
@ -2769,10 +2775,6 @@ class JSScript : public js::BaseScript {
|
|||
return getScope(index);
|
||||
}
|
||||
|
||||
bool needsFunctionEnvironmentObjects() const {
|
||||
return hasFlag(ImmutableFlags::NeedsFunctionEnvironmentObjects);
|
||||
}
|
||||
|
||||
bool functionHasExtraBodyVarScope() const {
|
||||
bool res = hasFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
|
||||
MOZ_ASSERT_IF(res, functionHasParameterExprs());
|
||||
|
@ -3039,11 +3041,6 @@ class JSScript : public js::BaseScript {
|
|||
// invariants of debuggee compartments, scripts, and frames.
|
||||
inline bool isDebuggee() const;
|
||||
|
||||
// Access the flag for whether this script has a DebugScript in its realm's
|
||||
// map. This should only be used by the DebugScript class.
|
||||
bool hasDebugScript() const { return hasFlag(MutableFlags::HasDebugScript); }
|
||||
void setHasDebugScript(bool b) { setFlag(MutableFlags::HasDebugScript, b); }
|
||||
|
||||
void finalize(JSFreeOp* fop);
|
||||
|
||||
static const JS::TraceKind TraceKind = JS::TraceKind::Script;
|
||||
|
@ -3080,10 +3077,6 @@ class JSScript : public js::BaseScript {
|
|||
void holdScript(JS::HandleFunction fun);
|
||||
void dropScript();
|
||||
};
|
||||
|
||||
bool trackRecordReplayProgress() const {
|
||||
return hasFlag(MutableFlags::TrackRecordReplayProgress);
|
||||
}
|
||||
};
|
||||
|
||||
/* If this fails, add/remove padding within JSScript. */
|
||||
|
@ -3336,39 +3329,6 @@ class LazyScript : public BaseScript {
|
|||
return lazyData_ ? lazyData_->innerFunctions().size() : 0;
|
||||
}
|
||||
|
||||
GeneratorKind generatorKind() const {
|
||||
return hasFlag(ImmutableFlags::IsGenerator) ? GeneratorKind::Generator
|
||||
: GeneratorKind::NotGenerator;
|
||||
}
|
||||
|
||||
bool isGenerator() const {
|
||||
return generatorKind() == GeneratorKind::Generator;
|
||||
}
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A script only gets its generator kind set as part of initialization,
|
||||
// so it can only transition from NotGenerator.
|
||||
MOZ_ASSERT(!isGenerator());
|
||||
if (kind == GeneratorKind::Generator) {
|
||||
setFlag(ImmutableFlags::IsGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
bool isAsync() const { return hasFlag(ImmutableFlags::IsAsync); }
|
||||
FunctionAsyncKind asyncKind() const {
|
||||
return isAsync() ? FunctionAsyncKind::AsyncFunction
|
||||
: FunctionAsyncKind::SyncFunction;
|
||||
}
|
||||
|
||||
void setAsyncKind(FunctionAsyncKind kind) {
|
||||
if (kind == FunctionAsyncKind::AsyncFunction) {
|
||||
setFlag(ImmutableFlags::IsAsync);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasRest() const { return hasFlag(ImmutableFlags::HasRest); }
|
||||
void setHasRest() { setFlag(ImmutableFlags::HasRest); }
|
||||
|
||||
frontend::ParseGoal parseGoal() const {
|
||||
if (hasFlag(ImmutableFlags::IsModule)) {
|
||||
return frontend::ParseGoal::Module;
|
||||
|
@ -3378,58 +3338,6 @@ class LazyScript : public BaseScript {
|
|||
|
||||
bool isBinAST() const { return scriptSource()->hasBinASTSource(); }
|
||||
|
||||
bool strict() const { return hasFlag(ImmutableFlags::Strict); }
|
||||
void setStrict() { setFlag(ImmutableFlags::Strict); }
|
||||
|
||||
bool bindingsAccessedDynamically() const {
|
||||
return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||
}
|
||||
void setBindingsAccessedDynamically() {
|
||||
setFlag(ImmutableFlags::BindingsAccessedDynamically);
|
||||
}
|
||||
|
||||
bool hasDirectEval() const { return hasFlag(ImmutableFlags::HasDirectEval); }
|
||||
void setHasDirectEval() { setFlag(ImmutableFlags::HasDirectEval); }
|
||||
|
||||
bool isLikelyConstructorWrapper() const {
|
||||
return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||
}
|
||||
void setLikelyConstructorWrapper() {
|
||||
setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
|
||||
}
|
||||
|
||||
bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
|
||||
void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
|
||||
|
||||
bool treatAsRunOnce() const {
|
||||
return hasFlag(ImmutableFlags::TreatAsRunOnce);
|
||||
}
|
||||
void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
|
||||
|
||||
bool isDerivedClassConstructor() const {
|
||||
return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||
}
|
||||
void setIsDerivedClassConstructor() {
|
||||
setFlag(ImmutableFlags::IsDerivedClassConstructor);
|
||||
}
|
||||
|
||||
bool needsHomeObject() const {
|
||||
return hasFlag(ImmutableFlags::NeedsHomeObject);
|
||||
}
|
||||
void setNeedsHomeObject() { setFlag(ImmutableFlags::NeedsHomeObject); }
|
||||
|
||||
bool shouldDeclareArguments() const {
|
||||
return hasFlag(ImmutableFlags::ShouldDeclareArguments);
|
||||
}
|
||||
void setShouldDeclareArguments() {
|
||||
setFlag(ImmutableFlags::ShouldDeclareArguments);
|
||||
}
|
||||
|
||||
bool hasThisBinding() const {
|
||||
return hasFlag(ImmutableFlags::FunctionHasThisBinding);
|
||||
}
|
||||
void setHasThisBinding() { setFlag(ImmutableFlags::FunctionHasThisBinding); }
|
||||
|
||||
void setFieldInitializers(FieldInitializers fieldInitializers) {
|
||||
MOZ_ASSERT(lazyData_);
|
||||
lazyData_->fieldInitializers_ = fieldInitializers;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче