Merge m-c to b2ginbound, a=merge

This commit is contained in:
Wes Kocher 2015-10-29 17:17:52 -07:00
commit ac2e661b3b
203 changed files with 3383 additions and 1104 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Merge day clobber
Merge day clobber

View File

@ -8,7 +8,7 @@ var tabs = require('sdk/tabs');
var { notify } = require('sdk/notifications');
var { ActionButton, ToggleButton } = require('sdk/ui');
var icon = 'chrome://mozapps/skin/extensions/extensionGeneric.png';
var icon = 'chrome://mozapps/skin/extensions/extensionGeneric.svg';
exports.icon = icon;
// your basic action button

View File

@ -1523,8 +1523,13 @@ pref("ui.key.menuAccessKeyFocuses", true);
pref("media.eme.enabled", true);
pref("media.eme.apiVisible", true);
// If decoding-via-gmp is turned on for <video>, default to using
// Adobe's GMP for decoding.
// Decode using Gecko Media Plugins in <video>, if a system decoder is not
// availble and the preferred GMP is available.
pref("media.gmp.decoder.enabled", true);
// If decoding-via-GMP is turned on for <video>, use Adobe's GMP for decoding,
// if it's available. Note: We won't fallback to another GMP if Adobe's is not
// installed.
pref("media.gmp.decoder.aac", 2);
pref("media.gmp.decoder.h264", 2);

View File

@ -732,7 +732,7 @@
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
<image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webNotsNotificationAnchor.label;"/>
aria-label="&urlbar.webNotsNotificationAnchor2.label;"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"

View File

@ -1544,15 +1544,6 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
</implementation>
<handlers>
<handler event="select"><![CDATA[
// When the user selects one of matches, stop the search to avoid
// changing the underlying result unexpectedly.
if (!this._ignoreNextSelect && this.selectedIndex >= 0) {
let controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
controller.stopSearch();
}
]]></handler>
<handler event="mousedown"><![CDATA[
// Required to make the xul:label.text-link elements in the search
// suggestions notification work correctly when clicked on Linux.

View File

@ -244,7 +244,7 @@
class="pref-item" align="top">
<image class="pref-icon" type="desktop-notification"/>
<vbox>
<label class="pref-title" value="&desktop-notification.label;"/>
<label class="pref-title" value="&desktop-notification2.label;"/>
<hbox align="center">
<menulist id="desktop-notification-menulist"
class="pref-menulist"

View File

@ -13,6 +13,10 @@ by NSS/PSM via netError.xhtml. -->
<!ENTITY certerror.pagetitle "Untrusted Connection">
<!ENTITY certerror.longpagetitle "This Connection is Untrusted">
<!-- These are going to be used for the updated design in Bug 1207107 -->
<!ENTITY certerror.pagetitle1 "Insecure Connection">
<!ENTITY certerror.longpagetitle1 "Your connection is not secure">
<!-- Localization note (certerror.introPara1) - The string "#1" will
be replaced at runtime with the name of the server to which the user
was trying to connect. -->
@ -22,6 +26,15 @@ securely to <b>#1</b>, but we can't confirm that your connection is secure.">
sites will present trusted identification to prove that you are
going to the right place. However, this site's identity can't be verified.">
<!-- These are going to be used for the udpated design in Bug 1207107 -->
<!-- Localization note (certerror.introPara) - The text content of the span tag
will be replaced at runtime with the name of the server to which the user
was trying to connect. -->
<!ENTITY certerror.introPara "The owner of <span class='hostname'/> has configured their website improperly. To protect your information from being stolen, &brandShortName; has not connected to this website.">
<!ENTITY certerror.returnToPreviousPage.label "Go Back">
<!ENTITY certerror.learnMore "Learn more…">
<!ENTITY certerror.advanced.label "Advanced">
<!ENTITY certerror.whatShouldIDo.heading "What Should I Do?">
<!ENTITY certerror.whatShouldIDo.content "If you usually connect to
this site without problems, this error could mean that someone is

View File

@ -201,7 +201,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY urlbar.passwordNotificationAnchor.label "Check if you want to save your password">
<!ENTITY urlbar.webappsNotificationAnchor.label "View the app install message">
<!ENTITY urlbar.pluginsNotificationAnchor.label "Manage plugin usage on this page">
<!ENTITY urlbar.webNotsNotificationAnchor.label "Change whether the site can show you notifications">
<!ENTITY urlbar.webNotsNotificationAnchor2.label "Change whether the site can receive notifications">
<!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.label "Manage sharing your camera and/or microphone with the site">
<!ENTITY urlbar.webRTCSharingDevicesNotificationAnchor.label "You are sharing your camera and/or microphone with the site">

View File

@ -0,0 +1,116 @@
# 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/.
# LOCALIZATION NOTE These strings are used inside the Memory Tools
# which is available from the Web Developer sub-menu -> 'Memory'.
# The correct localization of this file might be to keep it in
# English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.
# LOCALIZATION NOTE (memory.label):
# This string is displayed in the title of the tab when the memory tool is
# displayed inside the developer tools window and in the Developer Tools Menu.
memory.label=Memory
# LOCALIZATION NOTE (memory.panelLabel):
# This is used as the label for the toolbox panel.
memory.panelLabel=Memory Panel
# LOCALIZATION NOTE (memory.tooltip):
# This string is displayed in the tooltip of the tab when the memory tool is
# displayed inside the developer tools window.
memory.tooltip=Memory
# LOCALIZATION NOTE (aggregate.mb): The label annotating the number of bytes (in megabytes)
# in a snapshot. %S represents the value, rounded to 2 decimal points.
aggregate.mb=%S MB
# LOCALIZATION NOTE (snapshot-title.loading): The title for a snapshot before
# it has a creation time to display.
snapshot-title.loading=Processing…
# LOCALIZATION NOTE (checkbox.invertTree): The label describing the boolean
# checkbox whether or not to invert the tree.
checkbox.invertTree=Invert tree
# LOCALIZATION NOTE (checkbox.recordAllocationStacks): The label describing the boolean
# checkbox whether or not to record allocation stacks.
checkbox.recordAllocationStacks=Record allocation stacks
# LOCALIZATION NOTE (toolbar.breakdownBy): The label describing the select menu
# options of the breakdown options.
toolbar.breakdownBy=Group by:
# LOCALIZATION NOTE (take-snapshot): The label describing the button that initiates
# taking a snapshot, either as the main label, or a tooltip.
take-snapshot=Take snapshot
# LOCALIZATION NOTE (viewsourceindebugger): The label for the tooltip when hovering over
# a link in the heap tree to jump to the debugger view.
# %S represents the URL to match in the debugger.
viewsourceindebugger=View source in Debugger → %S
# LOCALIZATION NOTE (tree-item.nostack): The label describing the row in the heap tree
# that represents a row broken down by allocation stack when no stack was available.
tree-item.nostack=(no stack available)
# LOCALIZATION NOTE (tree-item.root): The label describing the row in the heap tree
# that represents the root of the tree when inverted.
tree-item.root=(root)
# LOCALIZATION NOTE (snapshot.state.saving.full): The label describing the snapshot
# state SAVING, used in the main heap view.
snapshot.state.saving.full=Saving snapshot…
# LOCALIZATION NOTE (snapshot.state.reading.full): The label describing the snapshot
# state READING, and SAVED, due to these states being combined visually, used
# in the main heap view.
snapshot.state.reading.full=Reading snapshot…
# LOCALIZATION NOTE (snapshot.state.saving-census.full): The label describing the snapshot
# state SAVING, used in the main heap view.
snapshot.state.saving-census.full=Saving census…
# LOCALIZATION NOTE (snapshot.state.error.full): The label describing the snapshot
# state ERROR, used in the main heap view.
snapshot.state.error.full=There was an error processing this snapshot.
# LOCALIZATION NOTE (snapshot.state.saving): The label describing the snapshot
# state SAVING, used in the snapshot list view
snapshot.state.saving=Saving snapshot…
# LOCALIZATION NOTE (snapshot.state.reading): The label describing the snapshot
# state READING, and SAVED, due to these states being combined visually, used
# in the snapshot list view.
snapshot.state.reading=Reading snapshot…
# LOCALIZATION NOTE (snapshot.state.saving-census): The label describing the snapshot
# state SAVING, used in snapshot list view.
snapshot.state.saving-census=Saving census…
# LOCALIZATION NOTE (snapshot.state.error): The label describing the snapshot
# state ERROR, used in the snapshot list view.
snapshot.state.error=Error
# LOCALIZATION NOTE (heapview.noAllocationStacks): The message displayed to
# users when selecting a breakdown by "allocation stack" but no allocation
# stacks were recorded in the heap snapshot.
heapview.noAllocationStacks=No allocation stacks found. Record allocation stacks before taking a heap snapshot.
# LOCALIZATION NOTE (heapview.field.bytes): The name of the column in the heap view for bytes.
heapview.field.bytes=Bytes
# LOCALIZATION NOTE (heapview.field.count): The name of the column in the heap view for count.
heapview.field.count=Count
# LOCALIZATION NOTE (heapview.field.totalbytes): The name of the column in the heap view for total bytes.
heapview.field.totalbytes=Total Bytes
# LOCALIZATION NOTE (heapview.field.totalcount): The name of the column in the heap view for total count.
heapview.field.totalcount=Total Count
# LOCALIZATION NOTE (heapview.field.name): The name of the column in the heap view for name.
heapview.field.name=Name

View File

@ -41,7 +41,7 @@
<!ENTITY popup.label "Open Pop-up Windows">
<!ENTITY desktop-notification.label "Show Notifications">
<!ENTITY desktop-notification2.label "Receive Notifications">
<!ENTITY camera.label "Use the Camera">
<!ENTITY microphone.label "Use the Microphone">

View File

@ -8,7 +8,7 @@ block = Block
alwaysAsk = Always Ask
permission.cookie.label = Set Cookies
permission.desktop-notification.label = Show Notifications
permission.desktop-notification2.label = Receive Notifications
permission.image.label = Load Images
permission.camera.label = Use the Camera
permission.microphone.label = Use the Microphone

View File

@ -65,6 +65,7 @@
locale/browser/devtools/promisedebugger.properties (%chrome/browser/devtools/promisedebugger.properties)
locale/browser/devtools/performance.dtd (%chrome/browser/devtools/performance.dtd)
locale/browser/devtools/performance.properties (%chrome/browser/devtools/performance.properties)
locale/browser/devtools/memory.properties (%chrome/browser/devtools/memory.properties)
locale/browser/devtools/graphs.properties (%chrome/browser/devtools/graphs.properties)
locale/browser/devtools/layoutview.dtd (%chrome/browser/devtools/layoutview.dtd)
locale/browser/devtools/responsiveUI.properties (%chrome/browser/devtools/responsiveUI.properties)

View File

@ -100,7 +100,8 @@ this.SitePermissions = {
* used in a UI for managing permissions.
*/
getPermissionLabel: function (aPermissionID) {
return gStringBundle.GetStringFromName("permission." + aPermissionID + ".label");
let labelID = gPermissionObject[aPermissionID].labelID || aPermissionID;
return gStringBundle.GetStringFromName("permission." + labelID + ".label");
},
/* Returns the localized label for the given permission state, to be used in
@ -136,6 +137,10 @@ var gPermissionObject = {
* Defaults to UNKNOWN, indicating that the user will be asked each time
* a page asks for that permissions.
*
* - labelID
* Use the given ID instead of the permission name for looking up strings.
* e.g. "desktop-notification2" to use permission.desktop-notification2.label
*
* - states
* Array of permission states to be exposed to the user.
* Defaults to ALLOW, BLOCK and the default state (see getDefault).
@ -162,7 +167,8 @@ var gPermissionObject = {
},
"desktop-notification": {
exactHostMatch: true
exactHostMatch: true,
labelID: "desktop-notification2",
},
"camera": {},

View File

@ -101,7 +101,7 @@
list-style-image: url(chrome://global/skin/icons/question-64.png);
}
.pref-icon[type="install"] {
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.svg);
}
.pref-icon[type="popup"] {
list-style-image: url(chrome://global/skin/icons/question-64.png);

View File

@ -111,7 +111,7 @@
list-style-image: url(chrome://global/skin/icons/question-64.png);
}
.pref-icon[type="install"] {
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.svg);
}
.pref-icon[type="popup"] {
list-style-image: url(chrome://global/skin/icons/question-64.png);

View File

@ -105,7 +105,7 @@
list-style-image: url(chrome://global/skin/icons/question-64.png);
}
.pref-icon[type="install"] {
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.svg);
}
.pref-icon[type="popup"] {
list-style-image: url(chrome://global/skin/icons/question-64.png);

View File

@ -224,7 +224,33 @@ if test "$GNU_CXX"; then
elif test "$ac_cv_cxx0x_headers_bug" = "yes"; then
AC_MSG_ERROR([Your toolchain does not support C++0x/C++11 mode properly. Please upgrade your toolchain])
fi
AC_CACHE_CHECK([whether 64-bits std::atomic requires -latomic],
ac_cv_needs_atomic,
AC_TRY_LINK(
[#include <cstdint>
#include <atomic>],
[ std::atomic<uint64_t> foo; foo = 1; ],
ac_cv_needs_atomic=no,
_SAVE_LIBS="$LIBS"
LIBS="$LIBS -latomic"
AC_TRY_LINK(
[#include <cstdint>
#include <atomic>],
[ std::atomic<uint64_t> foo; foo = 1; ],
ac_cv_needs_atomic=yes,
ac_cv_needs_atomic="do not know; assuming no")
LIBS="$_SAVE_LIBS"
)
)
if test "$ac_cv_needs_atomic" = yes; then
MOZ_NEEDS_LIBATOMIC=1
else
MOZ_NEEDS_LIBATOMIC=
fi
AC_SUBST(MOZ_NEEDS_LIBATOMIC)
fi
if test -n "$CROSS_COMPILE"; then
dnl When cross compile, we have no variable telling us what the host compiler is. Figure it out.
cat > conftest.C <<EOF

View File

@ -15,7 +15,7 @@ loader.lazyRequireGetter(this, "Services");
loader.lazyImporter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.png";
const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
const Strings = Services.strings.createBundle(
"chrome://browser/locale/devtools/aboutdebugging.properties");

View File

@ -179,7 +179,7 @@ var AnimationsPanel = {
onTimelineDataChanged: function(e, data) {
this.timelineData = data;
let {isMoving, isUserDrag, time} = data;
let {isMoving, isPaused, isUserDrag, time} = data;
this.playTimelineButtonEl.classList.toggle("paused", !isMoving);

View File

@ -684,13 +684,22 @@ AnimationsTimeline.prototype = {
this.animations.every(({state}) => state.currentTime === 0);
},
hasInfiniteAnimations: function() {
return this.animations.some(({state}) => !state.iterationCount);
},
startAnimatingScrubber: function(time) {
let x = TimeScale.startTimeToDistance(time, this.timeHeaderEl.offsetWidth);
this.scrubberEl.style.left = x + "px";
if (time < TimeScale.minStartTime ||
time > TimeScale.maxEndTime ||
!this.isAtLeastOneAnimationPlaying()) {
// Only stop the scrubber if it's out of bounds or all animations have been
// paused, but not if at least an animation is infinite.
let isOutOfBounds = time < TimeScale.minStartTime ||
time > TimeScale.maxEndTime;
let isAllPaused = !this.isAtLeastOneAnimationPlaying();
let hasInfinite = this.hasInfiniteAnimations();
if (isAllPaused || (isOutOfBounds && !hasInfinite)) {
this.stopAnimatingScrubber();
this.emit("timeline-data-changed", {
isPaused: !this.isAtLeastOneAnimationPlaying(),

View File

@ -8,11 +8,17 @@
// button can be clicked. Check that when it is, the current animations
// displayed in the timeline get their playstates changed accordingly, and check
// that the scrubber resumes/stops moving.
// Also checks that the button goes to the right state when the scrubber has
// reached the end of the timeline: continues to be in playing mode for infinite
// animations, goes to paused mode otherwise.
// And test that clicking the button once the scrubber has reached the end of
// the timeline does the right thing.
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let {panel, inspector} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let btn = panel.playTimelineButtonEl;
ok(btn, "The play/pause button exists");
@ -32,4 +38,57 @@ add_task(function*() {
ok(!btn.classList.contains("paused"),
"The play/pause button is in its playing state again");
yield assertScrubberMoving(panel, true);
// Some animations on the test page are infinite, so the scrubber won't stop
// at the end of the timeline, and the button should remain in play mode.
info("Select an infinite animation, reload the page and wait for the " +
"animation to complete");
yield selectNode(".multi", inspector);
yield reloadTab(inspector);
yield waitForOutOfBoundScrubber(timeline);
ok(!btn.classList.contains("paused"),
"The button is in its playing state still, animations are infinite.");
yield assertScrubberMoving(panel, true);
info("Click on the button after the scrubber has moved out of bounds");
yield clickTimelinePlayPauseButton(panel);
ok(btn.classList.contains("paused"),
"The button can be paused after the scrubber has moved out of bounds");
yield assertScrubberMoving(panel, false);
// For a finite animation though, once the scrubber reaches the end of the
// timeline, it should go back to paused mode.
info("Select a finite animation, reload the page and wait for the " +
"animation to complete");
yield selectNode(".negative-delay", inspector);
yield reloadTab(inspector);
yield waitForOutOfBoundScrubber(timeline);
ok(btn.classList.contains("paused"),
"The button is in paused state once finite animations are done");
yield assertScrubberMoving(panel, false);
info("Click again on the button to play the animation from the start again");
yield clickTimelinePlayPauseButton(panel);
ok(!btn.classList.contains("paused"),
"Clicking the button once finite animations are done should restart them");
yield assertScrubberMoving(panel, true);
});
function waitForOutOfBoundScrubber({win, scrubberEl}) {
return new Promise(resolve => {
function check() {
let pos = scrubberEl.getBoxQuads()[0].bounds.right;
let width = win.document.documentElement.offsetWidth;
if (pos >= width) {
setTimeout(resolve, 50);
} else {
setTimeout(check, 50);
}
}
check();
});
}

View File

@ -22,10 +22,7 @@ add_task(function*() {
"The toggle button now is in its paused state");
info("Reloading the page");
let onNewRoot = inspector.once("new-root");
yield reloadTab();
yield onNewRoot;
yield inspector.once("inspector-updated");
yield reloadTab(inspector);
ok(!panel.toggleAllButtonEl.classList.contains("paused"),
"The toggle button is back in its running state");

View File

@ -85,9 +85,14 @@ function addTab(url) {
/**
* Reload the current tab location.
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
*/
function reloadTab() {
return executeInContent("devtools:test:reload", {}, {}, false);
function* reloadTab(inspector) {
let onNewRoot = inspector.once("new-root");
yield executeInContent("devtools:test:reload", {}, {}, false);
yield onNewRoot;
yield inspector.once("inspector-updated");
}
/**

View File

@ -38,6 +38,7 @@ const performanceProps = "chrome://browser/locale/devtools/performance.propertie
const netMonitorProps = "chrome://browser/locale/devtools/netmonitor.properties";
const storageProps = "chrome://browser/locale/devtools/storage.properties";
const scratchpadProps = "chrome://browser/locale/devtools/scratchpad.properties";
const memoryProps = "chrome://browser/locale/devtools/memory.properties";
loader.lazyGetter(this, "toolboxStrings", () => Services.strings.createBundle(toolboxProps));
loader.lazyGetter(this, "performanceStrings",() => Services.strings.createBundle(performanceProps));
@ -51,6 +52,7 @@ loader.lazyGetter(this, "inspectorStrings", () => Services.strings.createBundle(
loader.lazyGetter(this, "netMonitorStrings", () => Services.strings.createBundle(netMonitorProps));
loader.lazyGetter(this, "storageStrings", () => Services.strings.createBundle(storageProps));
loader.lazyGetter(this, "scratchpadStrings", () => Services.strings.createBundle(scratchpadProps));
loader.lazyGetter(this, "memoryStrings", () => Services.strings.createBundle(memoryProps));
var Tools = {};
exports.Tools = Tools;
@ -283,9 +285,9 @@ Tools.memory = {
highlightedicon: "chrome://devtools/skin/themes/images/tool-memory-active.svg",
url: "chrome://devtools/content/memory/memory.xhtml",
visibilityswitch: "devtools.memory.enabled",
label: "Memory",
panelLabel: "Memory Panel",
tooltip: "Memory (keyboardshortcut)",
label: l10n("memory.label", memoryStrings),
panelLabel: l10n("memory.panelLabel", memoryStrings),
tooltip: l10n("memory.tooltip", memoryStrings),
isTargetSupported: function (target) {
return target.getTrait("heapSnapshots");

View File

@ -75,16 +75,19 @@ const readSnapshot = exports.readSnapshot = function readSnapshot (heapWorker, s
assert(snapshot.state === states.SAVED,
`Should only read a snapshot once. Found snapshot in state ${snapshot.state}`);
let creationTime;
dispatch({ type: actions.READ_SNAPSHOT_START, snapshot });
try {
yield heapWorker.readHeapSnapshot(snapshot.path);
creationTime = yield heapWorker.getCreationTime(snapshot.path);
} catch (error) {
reportException("readSnapshot", error);
dispatch({ type: actions.SNAPSHOT_ERROR, snapshot, error });
return;
}
dispatch({ type: actions.READ_SNAPSHOT_END, snapshot });
dispatch({ type: actions.READ_SNAPSHOT_END, snapshot, creationTime });
};
};

View File

@ -22,10 +22,7 @@ const App = createClass({
propTypes: appModel,
getDefaultProps() {
return {
breakdown: breakdowns.coarseType.breakdown,
inverted: false,
};
return {};
},
childContextTypes: {
@ -95,10 +92,7 @@ const App = createClass({
* and passed to components.
*/
function mapStateToProps (state) {
return {
allocations: state.allocations,
snapshots: state.snapshots
};
return state;
}
module.exports = connect(mapStateToProps)(App);

View File

@ -3,6 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils");
const { URL } = require("sdk/url");
const Frame = module.exports = createClass({
@ -18,13 +19,14 @@ const Frame = module.exports = createClass({
let url = new URL(frame.source);
let spec = url.toString();
let func = frame.functionDisplayFrame || "";
let func = frame.functionDisplayName || "";
let tooltip = `${func} (${spec}:${frame.line}:${frame.column})`;
let viewTooltip = L10N.getFormatStr("viewsourceindebugger", `${spec}:${frame.line}:${frame.column}`);
let onClick = () => toolbox.viewSourceInDebugger(spec, frame.line);
let fields = [
dom.span({ className: "frame-link-function-display-name" }, func),
dom.a({ className: "frame-link-filename", onClick }, url.fileName),
dom.a({ className: "frame-link-filename", onClick, title: viewTooltip }, url.fileName),
dom.span({ className: "frame-link-colon" }, ":"),
dom.span({ className: "frame-link-line" }, frame.line),
dom.span({ className: "frame-link-colon" }, ":"),

View File

@ -6,10 +6,9 @@ const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/cl
const { safeErrorString } = require("devtools/shared/DevToolsUtils");
const Tree = createFactory(require("./tree"));
const TreeItem = createFactory(require("./tree-item"));
const { getSnapshotStatusTextFull } = require("../utils");
const { getSnapshotStatusTextFull, L10N } = require("../utils");
const { snapshotState: states } = require("../constants");
const { snapshot: snapshotModel } = require("../models");
const TAKE_SNAPSHOT_TEXT = "Take snapshot";
// If HEAP_TREE_ROW_HEIGHT changes, be sure to change `var(--heap-tree-row-height)`
// in `devtools/client/themes/memory.css`
const HEAP_TREE_ROW_HEIGHT = 14;
@ -83,12 +82,12 @@ const Heap = module.exports = createClass({
// but React hates that evidently
"data-standalone": true,
"data-text-only": true,
}, TAKE_SNAPSHOT_TEXT)];
}, L10N.getStr("take-snapshot"))];
break;
case states.ERROR:
content = [
dom.span({ className: "snapshot-status error" }, statusText),
dom.pre({}, safeErrorString(snapshot.error || new Error("blahblah"))),
dom.pre({}, safeErrorString(snapshot.error))
];
break;
case states.SAVING:
@ -99,16 +98,25 @@ const Heap = module.exports = createClass({
content = [dom.span({ className: "snapshot-status devtools-throbber" }, statusText)];
break;
case states.SAVED_CENSUS:
content = [
content = [];
if (snapshot.breakdown.by === "allocationStack"
&& census.children.length === 1
&& census.children[0].name === "noStack") {
content.push(dom.div({ className: "error no-allocation-stacks" },
L10N.getStr("heapview.noAllocationStacks")));
}
content.push(
dom.div({ className: "header" },
dom.span({ className: "heap-tree-item-bytes" }, "Bytes"),
dom.span({ className: "heap-tree-item-count" }, "Count"),
dom.span({ className: "heap-tree-item-total-bytes" }, "Total Bytes"),
dom.span({ className: "heap-tree-item-total-count" }, "Total Count"),
dom.span({ className: "heap-tree-item-name" }, "Name")
dom.span({ className: "heap-tree-item-bytes" }, L10N.getStr("heapview.field.bytes")),
dom.span({ className: "heap-tree-item-count" }, L10N.getStr("heapview.field.count")),
dom.span({ className: "heap-tree-item-total-bytes" }, L10N.getStr("heapview.field.totalbytes")),
dom.span({ className: "heap-tree-item-total-count" }, L10N.getStr("heapview.field.totalcount")),
dom.span({ className: "heap-tree-item-name" }, L10N.getStr("heapview.field.name"))
),
Tree(createTreeProperties(snapshot.census, toolbox))
];
);
break;
}
let pane = dom.div({ className: "heap-view-panel", "data-state": state }, ...content);

View File

@ -3,7 +3,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const { getSnapshotStatusText } = require("../utils");
const { L10N, getSnapshotTitle, getSnapshotTotals, getSnapshotStatusText } = require("../utils");
const { snapshotState: states } = require("../constants");
const { snapshot: snapshotModel } = require("../models");
const SnapshotListItem = module.exports = createClass({
@ -16,17 +17,29 @@ const SnapshotListItem = module.exports = createClass({
},
render() {
let { index, item, onClick } = this.props;
let className = `snapshot-list-item ${item.selected ? " selected" : ""}`;
let statusText = getSnapshotStatusText(item);
let { index, item: snapshot, onClick } = this.props;
let className = `snapshot-list-item ${snapshot.selected ? " selected" : ""}`;
let statusText = getSnapshotStatusText(snapshot);
let title = getSnapshotTitle(snapshot);
let details;
if (snapshot.state === states.SAVED_CENSUS) {
let { bytes } = getSnapshotTotals(snapshot);
let formatBytes = L10N.getFormatStr("aggregate.mb", L10N.numberWithDecimals(bytes / 1000000, 2));
details = dom.span({ className: "snapshot-totals" },
dom.span({ className: "total-bytes" }, formatBytes)
);
} else {
details = dom.span({ className: "snapshot-state" }, statusText);
}
return (
dom.li({ className, onClick },
dom.span({
className: `snapshot-title ${statusText ? " devtools-throbber" : ""}`
}, `Snapshot #${index}`),
statusText ? dom.span({ className: "snapshot-state" }, statusText) : void 0
}, title),
details
)
);
}

View File

@ -3,7 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils");
const models = require("../models");
const Toolbar = module.exports = createClass({
@ -34,11 +34,16 @@ const Toolbar = module.exports = createClass({
return (
dom.div({ className: "devtools-toolbar" },
dom.button({ className: `take-snapshot devtools-button`, onClick: onTakeSnapshotClick }),
dom.button({
className: `take-snapshot devtools-button`,
onClick: onTakeSnapshotClick,
title: L10N.getStr("take-snapshot")
}),
dom.label({},
"Breakdown by ",
L10N.getStr("toolbar.breakdownBy"),
dom.select({
id: "select-breakdown",
className: `select-breakdown`,
onChange: e => onBreakdownChange(e.target.value),
}, ...breakdowns.map(({ name, displayName }) => dom.option({ key: name, value: name }, displayName)))
@ -46,12 +51,12 @@ const Toolbar = module.exports = createClass({
dom.label({},
dom.input({
id: "invert-tree-checkbox",
type: "checkbox",
checked: inverted,
onChange: onToggleInverted,
}),
// TODO bug 1214799
"Invert tree"
L10N.getStr("checkbox.invertTree")
),
dom.label({},
@ -61,8 +66,7 @@ const Toolbar = module.exports = createClass({
disabled: allocations.togglingInProgress,
onChange: onToggleRecordAllocationStacks,
}),
// TODO bug 1214799
"Record allocation stacks"
L10N.getStr("checkbox.recordAllocationStacks")
)
)
);

View File

@ -4,6 +4,7 @@
const { isSavedFrame } = require("devtools/shared/DevToolsUtils");
const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils");
const FrameView = createFactory(require("./frame"));
const INDENT = 10;
@ -33,8 +34,9 @@ const TreeItem = module.exports = createClass({
},
toLabel(name, toolbox) {
return isSavedFrame(name)
? FrameView({ frame: name, toolbox })
: String(name);
return isSavedFrame(name) ? FrameView({ frame: name, toolbox }) :
name === "noStack" ? L10N.getStr("tree-item.nostack") :
name === null ? L10N.getStr("tree-item.root") :
String(name);
},
});

View File

@ -36,11 +36,14 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
inverted: PropTypes.bool,
// If an error was thrown while processing this snapshot, the `Error` instance is attached here.
error: PropTypes.object,
// The creation time of the snapshot; required after the snapshot has been read.
creationTime: PropTypes.number,
// State the snapshot is in
// @see ./constants.js
state: function (snapshot, propName) {
let current = snapshot.state;
let shouldHavePath = [states.SAVED, states.READ, states.SAVING_CENSUS, states.SAVED_CENSUS];
let shouldHaveCreationTime = [states.READ, states.SAVING_CENSUS, states.SAVED_CENSUS];
let shouldHaveCensus = [states.SAVED_CENSUS];
if (!stateKeys.includes(current)) {
@ -52,6 +55,9 @@ let snapshotModel = exports.snapshot = PropTypes.shape({
if (shouldHaveCensus.includes(current) && (!snapshot.census || !snapshot.breakdown)) {
throw new Error(`Snapshots in state ${current} must have a census and breakdown.`);
}
if (shouldHaveCreationTime.includes(current) && !snapshot.creationTime) {
throw new Error(`Snapshots in state ${current} must have a creation time.`);
}
},
});

View File

@ -6,5 +6,9 @@
const { actions } = require("../constants");
module.exports = function (inverted = false, action) {
return action.type === actions.TOGGLE_INVERTED ? !inverted : inverted;
if (action.type === actions.TOGGLE_INVERTED) {
return !inverted;
} else {
return inverted;
}
};

View File

@ -37,6 +37,7 @@ handlers[actions.READ_SNAPSHOT_START] = function (snapshots, action) {
handlers[actions.READ_SNAPSHOT_END] = function (snapshots, action) {
let snapshot = getSnapshot(snapshots, action.snapshot);
snapshot.state = states.READ;
snapshot.creationTime = action.creationTime;
return [...snapshots];
};

View File

@ -8,6 +8,7 @@ support-files =
[browser_memory_allocationStackBreakdown_01.js]
[browser_memory-breakdowns-01.js]
skip-if = debug # bug 1219554
[browser_memory_no_allocation_stacks.js]
[browser_memory-simple-01.js]
skip-if = debug # bug 1219554
[browser_memory_transferHeapSnapshot_e10s_01.js]

View File

@ -10,7 +10,7 @@ const { breakdowns } = require("devtools/client/memory/constants");
const { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const { toggleInverted } = require("devtools/client/memory/actions/inverted");
const { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
@ -18,9 +18,12 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
dispatch(toggleInverted());
yield dispatch(toggleInvertedAndRefresh(heapWorker));
ok(getState().inverted, true);
ok(doc.getElementById("invert-tree-checkbox").checked,
"invert-tree-checkbox should be checked");
dispatch(breakdownActions.setBreakdown(breakdowns.allocationStack.breakdown));
is(getState().breakdown.by, "allocationStack");
@ -33,7 +36,8 @@ this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
const doc = panel.panelWin.document;
ok(doc.querySelector(".frame-link-function-display-name"),
"Should have rendered some allocation stack tree items");
const names = [...doc.querySelectorAll(".frame-link-function-display-name")];
ok(names.length, "Should have rendered some allocation stack tree items");
ok(names.some(e => !!e.textContent.trim()),
"And at least some of them should have functionDisplayNames");
});

View File

@ -0,0 +1,35 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Sanity test that we can show allocation stack breakdowns in the tree.
"use strict";
const { breakdowns } = require("devtools/client/memory/constants");
const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
const breakdownActions = require("devtools/client/memory/actions/breakdown");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_steady_allocation.html";
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const { getState, dispatch } = panel.panelWin.gStore;
const doc = panel.panelWin.document;
ok(!getState().allocations.recording,
"Should not be recording allocagtions");
yield dispatch(takeSnapshotAndCensus(front, heapWorker));
yield dispatch(breakdownActions.setBreakdownAndRefresh(heapWorker,
breakdowns.allocationStack.breakdown));
is(getState().breakdown.by, "allocationStack",
"Should be using allocation stack breakdown");
ok(!getState().allocations.recording,
"Should still not be recording allocagtions");
ok(doc.querySelector(".no-allocation-stacks"),
"Because we did not record allocations, the no-allocation-stack warning should be visible");
});

View File

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that we use the correct snapshot aggregate value
* in `utils.getSnapshotTotals(snapshot)`
*/
let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants");
let { getSnapshotTotals, breakdownEquals } = require("devtools/client/memory/utils");
let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted");
let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
function run_test() {
run_next_test();
}
add_task(function *() {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
let { getState, dispatch } = store;
dispatch(takeSnapshotAndCensus(front, heapWorker));
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(!getState().snapshots[0].inverted, "Snapshot is not inverted");
ok(isBreakdownType(getState().snapshots[0].census, "coarseType"),
"Snapshot using `coarseType` breakdown");
let census = getState().snapshots[0].census;
let result = aggregate(census);
let totalBytes = result.bytes;
let totalCount = result.count;
ok(totalBytes > 0, "counted up bytes in the census");
ok(totalCount > 0, "counted up count in the census");
result = getSnapshotTotals(getState().snapshots[0])
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes");
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count");
dispatch(toggleInvertedAndRefresh(heapWorker));
yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]);
yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
ok(getState().snapshots[0].inverted, "Snapshot is inverted");
result = getSnapshotTotals(getState().snapshots[0])
equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes when inverted");
equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count when inverted");
});
function aggregate (census) {
let totalBytes = census.bytes;
let totalCount = census.count;
for (let child of (census.children || [])) {
let { bytes, count } = aggregate(child);
totalBytes += bytes
totalCount += count;
}
return { bytes: totalBytes, count: totalCount };
}

View File

@ -17,3 +17,4 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-take-snapshot.js]
[test_action-take-snapshot-and-census.js]
[test_utils.js]
[test_utils-get-snapshot-totals.js]

View File

@ -2,16 +2,36 @@
* 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/. */
const { Cu } = require("chrome");
Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
const STRINGS_URI = "chrome://browser/locale/devtools/memory.properties"
const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI);
const { assert } = require("devtools/shared/DevToolsUtils");
const { Preferences } = require("resource://gre/modules/Preferences.jsm");
const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns";
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { snapshotState: states, breakdowns } = require("./constants");
const FULL_ERROR_TEXT = "There was an error processing this snapshot.";
const ERROR_SNAPSHOT_TEXT = "⚠ Error!";
const SAVING_SNAPSHOT_TEXT = "Saving snapshot...";
const READING_SNAPSHOT_TEXT = "Reading snapshot...";
const SAVING_CENSUS_TEXT = "Taking heap census...";
/**
* Takes a snapshot object and returns the
* localized form of its timestamp to be used as a title.
*
* @param {Snapshot} snapshot
* @return {String}
*/
exports.getSnapshotTitle = function (snapshot) {
if (!snapshot.creationTime) {
return L10N.getStr("snapshot-title.loading");
}
let date = new Date(snapshot.creationTime / 1000);
return date.toLocaleTimeString(void 0, {
year: "2-digit",
month: "2-digit",
day: "2-digit",
hour12: false
});
};
/**
* Returns an array of objects with the unique key `name`
@ -98,17 +118,16 @@ exports.getSnapshotStatusText = function (snapshot) {
switch (snapshot.state) {
case states.ERROR:
return ERROR_SNAPSHOT_TEXT;
return L10N.getStr("snapshot.state.error");
case states.SAVING:
return SAVING_SNAPSHOT_TEXT;
return L10N.getStr("snapshot.state.saving");
case states.SAVED:
case states.READING:
return READING_SNAPSHOT_TEXT;
return L10N.getStr("snapshot.state.reading");
case states.SAVING_CENSUS:
return SAVING_CENSUS_TEXT;
// If it's read, it shouldn't have any label, as we could've cleared the
// census cache by changing the breakdown, and we should lazily
// go to SAVING_CENSUS. If it's SAVED_CENSUS, we have no status to display.
return L10N.getStr("snapshot.state.saving-census");
// Both READ and SAVED_CENSUS state do not have any message
// to show as other content will be displayed.
case states.READ:
case states.SAVED_CENSUS:
return "";
@ -130,9 +149,20 @@ exports.getSnapshotStatusTextFull = function (snapshot) {
`Snapshot must have expected state, found ${(snapshot || {}).state}.`);
switch (snapshot.state) {
case states.ERROR:
return FULL_ERROR_TEXT;
return L10N.getStr("snapshot.state.error.full");
case states.SAVING:
return L10N.getStr("snapshot.state.saving.full");
case states.SAVED:
case states.READING:
return L10N.getStr("snapshot.state.reading.full");
case states.SAVING_CENSUS:
return L10N.getStr("snapshot.state.saving-census.full");
// Both READ and SAVED_CENSUS state do not have any full message
// to show as other content will be displayed.
case states.READ:
case states.SAVED_CENSUS:
return "";
}
return exports.getSnapshotStatusText(snapshot);
}
/**
@ -207,3 +237,33 @@ exports.breakdownEquals = function (obj1, obj2) {
return false;
};
/**
* Takes a snapshot and returns the total bytes and
* total count that this snapshot represents.
*
* @param {Snapshot} snapshot
* @return {Object}
*/
exports.getSnapshotTotals = function (snapshot) {
let bytes, count;
let census = snapshot.census;
if (snapshot.inverted) {
while (census) {
bytes = census.totalBytes;
count = census.totalCount;
census = census.children && census.children[0];
}
} else {
bytes = census.totalBytes;
count = census.totalCount;
}
return {
bytes: bytes || 0,
count: count || 0,
};
};

View File

@ -129,16 +129,27 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
color: var(--theme-selection-color);
}
.snapshot-list-item {
position: relative;
}
.snapshot-list-item span {
display: block;
}
.snapshot-list-item .snapshot-state {
.snapshot-list-item .snapshot-state, .snapshot-list-item .snapshot-totals {
font-size: 90%;
color: var(--theme-body-color-alt);
position: absolute;
}
.snapshot-list-item.selected .snapshot-state {
.snapshot-list-item .snapshot-state {
top: 38px;
}
.snapshot-list-item .snapshot-totals {
top: 38px;
}
.snapshot-list-item .total-bytes {
float: left;
}
.snapshot-list-item.selected .snapshot-state, .snapshot-list-item.selected .snapshot-totals {
/* Text inside a selected item should not be custom colored. */
color: inherit !important;
}
@ -317,3 +328,15 @@ html, .theme-body, #app, #memory-tool, #memory-tool-container {
margin-left: 5px;
color: var(--theme-content-color2);
}
.frame-link-function-display-name {
margin-right: 5px;
}
.no-allocation-stacks {
border-color: var(--theme-splitter-color);
border-style: solid;
border-width: 0px 0px 1px 0px;
text-align: center;
padding: 5px;
}

View File

@ -128,4 +128,18 @@ HeapAnalysesClient.prototype.takeCensusDiff = function (firstSnapshotFilePath,
censusOptions,
requestOptions
});
}
};
/**
* Request the creation time given a snapshot file path. Returns `null`
* if snapshot does not exist.
*
* @param {String} snapshotFilePath
* The path to the snapshot.
* @return {Number?}
* The unix timestamp of the creation time of the snapshot, or null if
* snapshot does not exist.
*/
HeapAnalysesClient.prototype.getCreationTime = function (snapshotFilePath) {
return this._worker.performTask("getCreationTime", snapshotFilePath);
};

View File

@ -78,3 +78,11 @@ workerHelper.createTask(self, "takeCensusDiff", request => {
return delta;
}
});
/**
* @see HeapAnalysesClient.prototype.getCreationTime
*/
workerHelper.createTask(self, "getCreationTime", (snapshotFilePath) => {
let snapshot = snapshots[snapshotFilePath];
return snapshot ? snapshot.creationTime : null;
});

View File

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that the HeapAnalyses{Client,Worker} can get a HeapSnapshot's
// creation time.
function waitForTenMilliseconds() {
const start = Date.now();
while (Date.now() - start < 10) ;
}
function run_test() {
run_next_test();
}
const BREAKDOWN = {
by: "internalType",
then: { by: "count", count: true, bytes: true }
};
add_task(function* () {
const client = new HeapAnalysesClient();
const start = Date.now() * 1000;
// Because Date.now() is less precise than the snapshot's time stamp, give it
// a little bit of head room.
waitForTenMilliseconds();
const snapshotFilePath = saveNewHeapSnapshot();
waitForTenMilliseconds();
const end = Date.now() * 1000;
yield client.readHeapSnapshot(snapshotFilePath);
ok(true, "Should have read the heap snapshot");
let time = yield client.getCreationTime("/not/a/real/path", {
breakdown: BREAKDOWN
});
equal(time, null, "getCreationTime returns `null` when snapshot does not exist");
time = yield client.getCreationTime(snapshotFilePath, {
breakdown: BREAKDOWN
});
ok(time >= start, "creation time occurred after start");
ok(time <= end, "creation time occurred before end");
client.destroy();
});

View File

@ -23,6 +23,7 @@ support-files =
[test_census-tree-node-05.js]
[test_census-tree-node-06.js]
[test_census-tree-node-07.js]
[test_HeapAnalyses_getCreationTime_01.js]
[test_HeapAnalyses_readHeapSnapshot_01.js]
[test_HeapAnalyses_takeCensusDiff_01.js]
[test_HeapAnalyses_takeCensusDiff_02.js]

View File

@ -517,7 +517,6 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
}
++gMouseOrKeyboardEventCounter;
nsCOMPtr<nsINode> node = do_QueryInterface(aTargetContent);
if (node &&
(aEvent->mMessage == eKeyUp || aEvent->mMessage == eMouseUp ||
@ -599,6 +598,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
StopTrackingDragGesture();
sNormalLMouseEventInProcess = false;
// then fall through...
MOZ_FALLTHROUGH;
case WidgetMouseEvent::eRightButton:
case WidgetMouseEvent::eMiddleButton:
SetClickCount(aPresContext, mouseEvent, aStatus);
@ -652,6 +652,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
aEvent->mMessage = eVoidEvent;
break;
}
MOZ_FALLTHROUGH;
case eMouseMove:
case ePointerDown:
case ePointerMove: {
@ -712,6 +713,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
}
}
// then fall through...
MOZ_FALLTHROUGH;
case eBeforeKeyDown:
case eKeyDown:
case eAfterKeyDown:
@ -1199,7 +1201,7 @@ EventStateManager::IsRemoteTarget(nsIContent* target) {
return false;
}
bool
static bool
CrossProcessSafeEvent(const WidgetEvent& aEvent)
{
switch (aEvent.mClass) {
@ -1235,7 +1237,7 @@ CrossProcessSafeEvent(const WidgetEvent& aEvent)
case eDrop:
return true;
default:
break;
return false;
}
default:
return false;
@ -1371,7 +1373,6 @@ EventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
}
} // CreateClickHoldTimer
//
// KillClickHoldTimer
//
@ -1386,7 +1387,6 @@ EventStateManager::KillClickHoldTimer()
}
}
//
// sClickHoldCallback
//
@ -1404,7 +1404,6 @@ EventStateManager::sClickHoldCallback(nsITimer* aTimer, void* aESM)
} // sAutoHideCallback
//
// FireContextClick
//
@ -1529,7 +1528,6 @@ EventStateManager::FireContextClick()
} // FireContextClick
//
// BeginTrackingDragGesture
//
@ -3048,8 +3046,9 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
if(WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
GenerateMouseEnterExit(mouseEvent);
}
// This break was commented specially
// break;
// After firing the pointercancel event, a user agent must also fire a
// pointerout event followed by a pointerleave event.
MOZ_FALLTHROUGH;
}
case ePointerUp: {
WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
@ -4203,8 +4202,8 @@ EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
// Update the last known refPoint with the current refPoint.
sLastRefPoint = aMouseEvent->refPoint;
}
MOZ_FALLTHROUGH;
case ePointerMove:
case ePointerDown:
{
@ -5862,4 +5861,3 @@ AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher()
}
} // namespace mozilla

View File

@ -195,6 +195,7 @@
#include "mozilla/widget/PuppetBidiKeyboard.h"
#include "mozilla/RemoteSpellCheckEngineChild.h"
#include "GMPServiceChild.h"
#include "GMPDecoderModule.h"
#include "gfxPlatform.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
@ -1463,6 +1464,13 @@ ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId)
return true;
}
bool
ContentChild::RecvNotifyGMPsChanged()
{
GMPDecoderModule::UpdateUsableCodecs();
return true;
}
PCrashReporterChild*
ContentChild::AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
const uint32_t& processType)

View File

@ -287,6 +287,8 @@ public:
const nsString& aSessionId) override;
virtual bool RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override;
virtual bool RecvNotifyGMPsChanged() override;
virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;
virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;

View File

@ -693,6 +693,7 @@ static const char* sObserverTopics[] = {
"profiler-subprocess-gather",
"profiler-subprocess",
#endif
"gmp-changed",
};
#ifdef MOZ_NUWA_PROCESS
@ -3297,6 +3298,9 @@ ContentParent::Observe(nsISupports* aSubject,
}
}
#endif
else if (!strcmp(aTopic, "gmp-changed")) {
unused << SendNotifyGMPsChanged();
}
return NS_OK;
}

View File

@ -677,6 +677,11 @@ child:
*/
async NotifyPresentationReceiverCleanUp(nsString aSessionId);
/**
* Notify the child that the Gecko Media Plugins installed changed.
*/
async NotifyGMPsChanged();
parent:
/**
* Tell the content process some attributes of itself. This is

View File

@ -1600,7 +1600,8 @@ MediaDecoderOwner*
MediaDecoder::GetOwner()
{
MOZ_ASSERT(NS_IsMainThread());
return mOwner;
// mOwner is valid until shutdown.
return !mShuttingDown ? mOwner : nullptr;
}
void

View File

@ -1878,6 +1878,15 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
break;
case dom::MediaSourceEnum::Browser:
// If no window id is passed in then default to the caller's window.
// Functional defaults are helpful in tests, but also a natural outcome
// of the constraints API's limited semantics for requiring input.
if (!vc.mBrowserWindow.WasPassed()) {
nsPIDOMWindow *outer = aWindow->GetOuterWindow();
vc.mBrowserWindow.Construct(outer->WindowID());
}
// | Fall through
// V
case dom::MediaSourceEnum::Screen:
case dom::MediaSourceEnum::Application:
case dom::MediaSourceEnum::Window:

View File

@ -42,6 +42,7 @@
#include "nsPrintfCString.h"
#endif
#include "nsIXULRuntime.h"
#include "GMPDecoderModule.h"
#include <limits>
namespace mozilla {
@ -700,6 +701,27 @@ GeckoMediaPluginServiceParent::LoadFromEnvironment()
mScannedPluginOnDisk = true;
}
class NotifyObserversTask final : public nsRunnable {
public:
explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
: mTopic(aTopic)
, mData(aData)
{}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
MOZ_ASSERT(obsService);
if (obsService) {
obsService->NotifyObservers(nullptr, mTopic, mData.get());
}
return NS_OK;
}
private:
~NotifyObserversTask() {}
const char* mTopic;
const nsString mData;
};
NS_IMETHODIMP
GeckoMediaPluginServiceParent::PathRunnable::Run()
{
@ -710,6 +732,16 @@ GeckoMediaPluginServiceParent::PathRunnable::Run()
mOperation == REMOVE_AND_DELETE_FROM_DISK,
mDefer);
}
#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures.
// For e10s, we must fire a notification so that all ContentParents notify
// their children to update the codecs that the GMPDecoderModule can use.
NS_DispatchToMainThread(new NotifyObserversTask("gmp-changed"), NS_DISPATCH_NORMAL);
// For non-e10s, and for decoding in the chrome process, must update GMP
// PDM's codecs list directly.
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
GMPDecoderModule::UpdateUsableCodecs();
}));
#endif
return NS_OK;
}
@ -935,27 +967,6 @@ GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
return gmp.get();
}
class NotifyObserversTask final : public nsRunnable {
public:
explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
: mTopic(aTopic)
, mData(aData)
{}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
MOZ_ASSERT(obsService);
if (obsService) {
obsService->NotifyObservers(nullptr, mTopic, mData.get());
}
return NS_OK;
}
private:
~NotifyObserversTask() {}
const char* mTopic;
const nsString mData;
};
void
GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
{

View File

@ -250,10 +250,6 @@ PDMFactory::CreatePDMs()
return;
}
if (sGMPDecoderEnabled) {
m = new GMPDecoderModule();
StartupPDM(m);
}
#ifdef MOZ_WIDGET_ANDROID
if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled) {
m = new AndroidDecoderModule();
@ -291,6 +287,11 @@ PDMFactory::CreatePDMs()
m = new AgnosticDecoderModule();
StartupPDM(m);
if (sGMPDecoderEnabled) {
m = new GMPDecoderModule();
StartupPDM(m);
}
}
bool

View File

@ -11,8 +11,12 @@
#include "mozIGeckoMediaPluginService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticMutex.h"
#include "gmp-audio-decode.h"
#include "gmp-video-decode.h"
#ifdef XP_WIN
#include "WMFDecoderModule.h"
#endif
namespace mozilla {
@ -89,6 +93,71 @@ GMPDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
}
}
static bool
HasGMPFor(const nsACString& aAPI,
const nsACString& aCodec,
const nsACString& aGMP)
{
MOZ_ASSERT(NS_IsMainThread());
#ifdef XP_WIN
// gmp-clearkey uses WMF for decoding, so if we're using clearkey we must
// verify that WMF works before continuing.
if (aGMP.EqualsLiteral("org.w3.clearkey")) {
RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
if (aCodec.EqualsLiteral("aac") &&
!pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"))) {
return false;
}
if (aCodec.EqualsLiteral("h264") &&
!pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"))) {
return false;
}
}
#endif
nsTArray<nsCString> tags;
tags.AppendElement(aCodec);
tags.AppendElement(aGMP);
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
bool hasPlugin = false;
if (NS_FAILED(mps->HasPluginForAPI(aAPI, &tags, &hasPlugin))) {
return false;
}
return hasPlugin;
}
StaticMutex sGMPCodecsMutex;
struct GMPCodecs {
const char* mKeySystem;
bool mHasAAC;
bool mHasH264;
};
static GMPCodecs sGMPCodecs[] = {
{ "org.w3.clearkey", false, false },
{ "com.adobe.primetime", false, false },
};
void
GMPDecoderModule::UpdateUsableCodecs()
{
MOZ_ASSERT(NS_IsMainThread());
StaticMutexAutoLock lock(sGMPCodecsMutex);
for (GMPCodecs& gmp : sGMPCodecs) {
gmp.mHasAAC = HasGMPFor(NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac"),
nsDependentCString(gmp.mKeySystem));
gmp.mHasH264 = HasGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
NS_LITERAL_CSTRING("h264"),
nsDependentCString(gmp.mKeySystem));
}
}
static uint32_t sPreferredAacGmp = 0;
static uint32_t sPreferredH264Gmp = 0;
@ -96,6 +165,13 @@ static uint32_t sPreferredH264Gmp = 0;
void
GMPDecoderModule::Init()
{
MOZ_ASSERT(NS_IsMainThread());
// GMPService::HasPluginForAPI is main thread only, so to implement
// SupportsMimeType() we build a table of the codecs which each whitelisted
// GMP has and update it when any GMPs are removed or added at runtime.
UpdateUsableCodecs();
Preferences::AddUintVarCache(&sPreferredAacGmp,
"media.gmp.decoder.aac", 0);
Preferences::AddUintVarCache(&sPreferredH264Gmp,
@ -115,7 +191,8 @@ GMPDecoderModule::PreferredGMP(const nsACString& aMimeType)
}
}
if (aMimeType.EqualsLiteral("video/avc")) {
if (aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("video/mp4")) {
switch (sPreferredH264Gmp) {
case 1: rv.emplace(NS_LITERAL_CSTRING("org.w3.clearkey")); break;
case 2: rv.emplace(NS_LITERAL_CSTRING("com.adobe.primetime")); break;
@ -126,35 +203,28 @@ GMPDecoderModule::PreferredGMP(const nsACString& aMimeType)
return rv;
}
/* static */
bool
GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType,
const Maybe<nsCString>& aGMP)
{
nsTArray<nsCString> tags;
nsCString api;
if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
tags.AppendElement(NS_LITERAL_CSTRING("aac"));
api = NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER);
} else if (aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("video/mp4")) {
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
api = NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER);
} else {
return false;
const bool isAAC = aMimeType.EqualsLiteral("audio/mp4a-latm");
const bool isH264 = aMimeType.EqualsLiteral("video/avc") ||
aMimeType.EqualsLiteral("video/mp4");
StaticMutexAutoLock lock(sGMPCodecsMutex);
for (GMPCodecs& gmp : sGMPCodecs) {
if (isAAC && gmp.mHasAAC &&
(aGMP.isNothing() || aGMP.value().EqualsASCII(gmp.mKeySystem))) {
return true;
}
if (isH264 && gmp.mHasH264 &&
(aGMP.isNothing() || aGMP.value().EqualsASCII(gmp.mKeySystem))) {
return true;
}
}
if (aGMP.isSome()) {
tags.AppendElement(aGMP.value());
}
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
bool hasPlugin = false;
if (NS_FAILED(mps->HasPluginForAPI(api, &tags, &hasPlugin))) {
return false;
}
return hasPlugin;
return false;
}
bool

View File

@ -38,6 +38,7 @@ public:
bool
SupportsMimeType(const nsACString& aMimeType) override;
// Main thread only.
static void Init();
static const Maybe<nsCString> PreferredGMP(const nsACString& aMimeType);
@ -45,6 +46,8 @@ public:
static bool SupportsMimeType(const nsACString& aMimeType,
const Maybe<nsCString>& aGMP);
// Main thread only.
static void UpdateUsableCodecs();
};
} // namespace mozilla

View File

@ -1406,6 +1406,11 @@ const DEBUG_TEST_LOOP_FOREVER = false;
// or end the mochitest if all the tests are done.
function MediaTestManager() {
// Return how many seconds elapsed since |begin|.
function elapsedTime(begin) {
var end = new Date();
return (end.getTime() - begin.getTime()) / 1000;
}
// Sets up a MediaTestManager to runs through the 'tests' array, which needs
// to be one of, or have the same fields as, the g*Test arrays of tests. Uses
// the user supplied 'startTest' function to initialize the test. This
@ -1450,7 +1455,8 @@ function MediaTestManager() {
this.tokens.push(token);
this.numTestsRunning++;
this.handlers[token] = handler;
is(this.numTestsRunning, this.tokens.length, "[started " + token + "] Length of array should match number of running tests");
is(this.numTestsRunning, this.tokens.length,
"[started " + token + " t=" + elapsedTime(this.startTime) + "] Length of array should match number of running tests");
}
// Registers that the test corresponding to 'token' has finished. Call when
@ -1466,7 +1472,8 @@ function MediaTestManager() {
info("[finished " + token + "] remaining= " + this.tokens);
this.numTestsRunning--;
is(this.numTestsRunning, this.tokens.length, "[finished " + token + "] Length of array should match number of running tests");
is(this.numTestsRunning, this.tokens.length,
"[finished " + token + " t=" + elapsedTime(this.startTime) + "] Length of array should match number of running tests");
if (this.tokens.length < PARALLEL_TESTS) {
this.nextTest();
}
@ -1504,7 +1511,7 @@ function MediaTestManager() {
var onCleanup = function() {
var end = new Date();
SimpleTest.info("Finished at " + end + " (" + (end.getTime() / 1000) + "s)");
SimpleTest.info("Running time: " + (end.getTime() - this.startTime.getTime())/1000 + "s");
SimpleTest.info("Running time: " + elapsedTime(this.startTime) + "s");
SimpleTest.finish();
}.bind(this);
mediaTestCleanup(onCleanup);

View File

@ -390,6 +390,18 @@ function waitUntil(func, time) {
var timeout = (promise, time, msg) =>
Promise.race([promise, wait(time).then(() => Promise.reject(new Error(msg)))]);
/** Use event listener to call passed-in function on fire until it returns true */
var listenUntil = (target, eventName, onFire) => {
return new Promise(resolve => target.addEventListener(eventName,
function callback() {
var result = onFire();
if (result) {
target.removeEventListener(eventName, callback, false);
resolve(result);
}
}, false));
};
/*** Test control flow methods */
/**

View File

@ -2,13 +2,12 @@
* 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/. */
const TIMEUPDATE_TIMEOUT_LENGTH = 10000;
const ENDED_TIMEOUT_LENGTH = 30000;
/* Time we wait for the canplaythrough event to fire
/* The time we wait depends primarily on the canplaythrough event firing
* Note: this needs to be at least 30s because the
* B2G emulator in VMs is really slow. */
const CANPLAYTHROUGH_TIMEOUT_LENGTH = 60000;
const VERIFYPLAYING_TIMEOUT_LENGTH = 60000;
/**
* This class manages playback of a HTMLMediaElement with a MediaStream.
@ -34,7 +33,8 @@ MediaStreamPlayback.prototype = {
* from a previous run
*/
playMedia : function(isResume) {
return this.startMedia(isResume)
this.startMedia(isResume);
return this.verifyPlaying()
.then(() => this.stopMediaElement());
},
@ -45,28 +45,34 @@ MediaStreamPlayback.prototype = {
* is being resumed from a previous run
*/
startMedia : function(isResume) {
var canPlayThroughFired = false;
// If we're playing this media element for the first time,
// check that the time is zero.
// If we're playing media element for the first time, check that time is zero.
if (!isResume) {
is(this.mediaElement.currentTime, 0,
"Before starting the media element, currentTime = 0");
}
this.canPlayThroughFired = listenUntil(this.mediaElement, 'canplaythrough',
() => true);
return new Promise((resolve, reject) => {
/**
* Callback fired when the canplaythrough event is fired. We only
* run the logic of this function once, as this event can fire
* multiple times while a HTMLMediaStream is playing content from
* a real-time MediaStream.
*/
var canPlayThroughCallback = () => {
// Disable the canplaythrough event listener to prevent multiple calls
canPlayThroughFired = true;
this.mediaElement.removeEventListener('canplaythrough',
canPlayThroughCallback, false);
// Hooks up the media stream to the media element and starts playing it
this.mediaElement.srcObject = this.mediaStream;
this.mediaElement.play();
},
/**
* Verifies that media is playing.
*/
verifyPlaying : function() {
var lastStreamTime = this.mediaStream.currentTime;
var lastElementTime = this.mediaElement.currentTime;
var mediaTimeProgressed = listenUntil(this.mediaElement, 'timeupdate',
() => this.mediaStream.currentTime > lastStreamTime &&
this.mediaElement.currentTime > lastElementTime);
return timeout(Promise.all([this.canPlayThroughFired, mediaTimeProgressed]),
VERIFYPLAYING_TIMEOUT_LENGTH, "verifyPlaying timed out")
.then(() => {
is(this.mediaElement.paused, false,
"Media element should be playing");
is(this.mediaElement.duration, Number.POSITIVE_INFINITY,
@ -93,45 +99,7 @@ MediaStreamPlayback.prototype = {
is(this.mediaElement.src, "", "No src should be defined");
is(this.mediaElement.currentSrc, "",
"Current src should still be an empty string");
var timeUpdateCallback = () => {
if (this.mediaStream.currentTime > 0 &&
this.mediaElement.currentTime > 0) {
this.mediaElement.removeEventListener('timeupdate',
timeUpdateCallback, false);
resolve();
}
};
// When timeupdate fires, we validate time has passed and move
// onto the success condition
this.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
false);
// If timeupdate doesn't fire in enough time, we fail the test
setTimeout(() => {
this.mediaElement.removeEventListener('timeupdate',
timeUpdateCallback, false);
reject(new Error("timeUpdate event never fired"));
}, TIMEUPDATE_TIMEOUT_LENGTH);
};
// Adds a listener intended to be fired when playback is available
// without further buffering.
this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
false);
// Hooks up the media stream to the media element and starts playing it
this.mediaElement.srcObject = this.mediaStream;
this.mediaElement.play();
// If canplaythrough doesn't fire in enough time, we fail the test
setTimeout(() => {
this.mediaElement.removeEventListener('canplaythrough',
canPlayThroughCallback, false);
reject(new Error("canplaythrough event never fired"));
}, CANPLAYTHROUGH_TIMEOUT_LENGTH);
});
});
},
/**
@ -172,7 +140,8 @@ LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype
*/
playMediaWithMediaStreamTracksStop: {
value: function(isResume) {
return this.startMedia(isResume)
this.startMedia(isResume);
return this.verifyPlaying()
.then(() => this.stopTracksForStreamInMediaPlayback())
.then(() => this.stopMediaElement());
}
@ -217,7 +186,8 @@ LocalMediaStreamPlayback.prototype = Object.create(MediaStreamPlayback.prototype
*/
playMediaWithDeprecatedStreamStop : {
value: function(isResume) {
return this.startMedia(isResume)
this.startMedia(isResume);
return this.verifyPlaying()
.then(() => this.deprecatedStopStreamInMediaPlayback())
.then(() => this.stopMediaElement());
}

View File

@ -43,6 +43,8 @@ skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failu
skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
[test_getUserMedia_basicScreenshare.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no screenshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
[test_getUserMedia_basicTabshare.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
[test_getUserMedia_basicWindowshare.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
[test_getUserMedia_basicVideoAudio.html]

View File

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
title: "getUserMedia Basic Tabshare Test",
bug: "1193075"
});
/**
* Run a test to verify that we can complete a start and stop media playback
* cycle for a tabshare LocalMediaStream on a video HTMLMediaElement.
*
* Additionally, exercise applyConstraints code for tabshare viewport offset.
*/
runTest(function () {
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
if (IsMacOSX10_6orOlder() || isWinXP) {
ok(true, "Screensharing disabled for OSX10.6 and WinXP");
return;
}
var testVideo = createMediaElement('video', 'testVideo');
return Promise.resolve()
.then(() => getUserMedia({ video: { mediaSource: "browser",
scrollWithPage: true } }))
.then(stream => {
var playback = new LocalMediaStreamPlayback(testVideo, stream);
return playback.playMediaWithDeprecatedStreamStop(false);
})
.then(() => getUserMedia({
video: {
mediaSource: "browser",
viewportOffsetX: 0,
viewportOffsetY: 0,
viewportWidth: 100,
viewportHeight: 100
}
}))
.then(stream => {
var playback = new LocalMediaStreamPlayback(testVideo, stream);
playback.startMedia(false);
return playback.verifyPlaying()
.then(() => Promise.all([
() => testVideo.srcObject.getVideoTracks()[0].applyConstraints({
mediaSource: "browser",
viewportOffsetX: 10,
viewportOffsetY: 50,
viewportWidth: 90,
viewportHeight: 50
}),
() => listenUntil(testVideo, "resize", () => true)
]))
.then(() => playback.verifyPlaying()) // still playing
.then(() => playback.deprecatedStopStreamInMediaPlayback())
.then(() => playback.stopMediaElement());
});
});
</script>
</pre>
</body>
</html>

View File

@ -72,9 +72,9 @@ var tests = [
var mustSupport = [
'width', 'height', 'frameRate', 'facingMode', 'deviceId',
// Yet to add:
// 'aspectRatio', 'frameRate', 'volume', 'sampleRate', 'sampleSize',
// 'echoCancellation', 'latency', 'groupId'
// Yet to add:
// 'aspectRatio', 'frameRate', 'volume', 'sampleRate', 'sampleSize',
// 'echoCancellation', 'latency', 'groupId'
// http://fluffy.github.io/w3c-screen-share/#screen-based-video-constraints
// OBE by http://w3c.github.io/mediacapture-screen-share
@ -82,6 +82,7 @@ var mustSupport = [
// Experimental https://bugzilla.mozilla.org/show_bug.cgi?id=1131568#c3
'browserWindow', 'scrollWithPage',
'viewportOffsetX', 'viewportOffsetY', 'viewportWidth', 'viewportHeight'
];
/**

View File

@ -31,9 +31,17 @@ using namespace mozilla::gfx;
NS_IMPL_ISUPPORTS(MediaEngineTabVideoSource, nsIDOMEventListener, nsITimerCallback)
MediaEngineTabVideoSource::MediaEngineTabVideoSource()
: mData(NULL), mDataSize(0), mMonitor("MediaEngineTabVideoSource"), mTabSource(nullptr)
{
}
: mBufWidthMax(0)
, mBufHeightMax(0)
, mWindowId(0)
, mScrollWithPage(false)
, mViewportOffsetX(0)
, mViewportOffsetY(0)
, mViewportWidth(0)
, mViewportHeight(0)
, mTimePerFrame(0)
, mDataSize(0)
, mMonitor("MediaEngineTabVideoSource") {}
nsresult
MediaEngineTabVideoSource::StartRunnable::Run()
@ -123,23 +131,39 @@ MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstrain
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
{
// windowId and scrollWithPage are not proper constraints, so just read them.
// They have no well-defined behavior in advanced, so ignore them there.
// windowId is not a proper constraint, so just read it.
// It has no well-defined behavior in advanced, so ignore it there.
mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
aConstraints.mBrowserWindow.Value() : -1;
return Restart(aConstraints, aPrefs, aDeviceId);
}
nsresult
MediaEngineTabVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
const mozilla::MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
{
// scrollWithPage is not proper a constraint, so just read it.
// It has no well-defined behavior in advanced, so ignore it there.
mScrollWithPage = aConstraints.mScrollWithPage.WasPassed() ?
aConstraints.mScrollWithPage.Value() : true;
aConstraints.mScrollWithPage.Value() : false;
FlattenedConstraints c(aConstraints);
mBufWidthMax = c.mWidth.Clamp(c.mWidth.mIdeal.WasPassed() ?
c.mWidth.mIdeal.Value() : DEFAULT_TABSHARE_VIDEO_MAX_WIDTH);
mBufHeightMax = c.mHeight.Clamp(c.mHeight.mIdeal.WasPassed() ?
c.mHeight.mIdeal.Value() : DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT);
double frameRate = c.mFrameRate.Clamp(c.mFrameRate.mIdeal.WasPassed() ?
c.mFrameRate.mIdeal.Value() : DEFAULT_TABSHARE_VIDEO_FRAMERATE);
mBufWidthMax = c.mWidth.Get(DEFAULT_TABSHARE_VIDEO_MAX_WIDTH);
mBufHeightMax = c.mHeight.Get(DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT);
double frameRate = c.mFrameRate.Get(DEFAULT_TABSHARE_VIDEO_FRAMERATE);
mTimePerFrame = std::max(10, int(1000.0 / (frameRate > 0? frameRate : 1)));
if (!mScrollWithPage) {
mViewportOffsetX = c.mViewportOffsetX.Get(0);
mViewportOffsetY = c.mViewportOffsetY.Get(0);
mViewportWidth = c.mViewportWidth.Get(INT32_MAX);
mViewportHeight = c.mViewportHeight.Get(INT32_MAX);
}
return NS_OK;
}
@ -192,30 +216,32 @@ MediaEngineTabVideoSource::Draw() {
return;
}
int32_t innerWidth, innerHeight;
win->GetInnerWidth(&innerWidth);
win->GetInnerHeight(&innerHeight);
if (innerWidth == 0 || innerHeight == 0) {
if (mScrollWithPage || mViewportWidth == INT32_MAX) {
win->GetInnerWidth(&mViewportWidth);
}
if (mScrollWithPage || mViewportHeight == INT32_MAX) {
win->GetInnerHeight(&mViewportHeight);
}
if (!mViewportWidth || !mViewportHeight) {
return;
}
float pixelRatio;
win->GetDevicePixelRatio(&pixelRatio);
const int deviceInnerWidth = (int)(pixelRatio * innerWidth);
const int deviceInnerHeight = (int)(pixelRatio * innerHeight);
IntSize size;
{
float pixelRatio;
win->GetDevicePixelRatio(&pixelRatio);
const int32_t deviceWidth = (int32_t)(pixelRatio * mViewportWidth);
const int32_t deviceHeight = (int32_t)(pixelRatio * mViewportHeight);
if ((deviceInnerWidth <= mBufWidthMax) && (deviceInnerHeight <= mBufHeightMax)) {
size = IntSize(deviceInnerWidth, deviceInnerHeight);
} else {
if ((deviceWidth <= mBufWidthMax) && (deviceHeight <= mBufHeightMax)) {
size = IntSize(deviceWidth, deviceHeight);
} else {
const float scaleWidth = (float)mBufWidthMax / (float)deviceWidth;
const float scaleHeight = (float)mBufHeightMax / (float)deviceHeight;
const float scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
const float scaleWidth = (float)mBufWidthMax / (float)deviceInnerWidth;
const float scaleHeight = (float)mBufHeightMax / (float)deviceInnerHeight;
const float scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
size = IntSize((int)(scale * deviceInnerWidth), (int)(scale * deviceInnerHeight));
size = IntSize((int)(scale * deviceWidth), (int)(scale * deviceHeight));
}
}
gfxImageFormat format = gfxImageFormat::RGB24;
@ -225,28 +251,31 @@ MediaEngineTabVideoSource::Draw() {
mDataSize = stride * size.height;
mData = static_cast<unsigned char*>(malloc(mDataSize));
}
if (!mData) {
return;
}
RefPtr<nsPresContext> presContext;
nsIDocShell* docshell = win->GetDocShell();
if (docshell) {
docshell->GetPresContext(getter_AddRefs(presContext));
}
if (!presContext) {
return;
nsCOMPtr<nsIPresShell> presShell;
{
RefPtr<nsPresContext> presContext;
nsIDocShell* docshell = win->GetDocShell();
if (docshell) {
docshell->GetPresContext(getter_AddRefs(presContext));
}
if (!presContext) {
return;
}
presShell = presContext->PresShell();
}
nscolor bgColor = NS_RGB(255, 255, 255);
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
uint32_t renderDocFlags = 0;
if (!mScrollWithPage) {
renderDocFlags |= nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
}
nsRect r(0, 0, nsPresContext::CSSPixelsToAppUnits((float)innerWidth),
nsPresContext::CSSPixelsToAppUnits((float)innerHeight));
uint32_t renderDocFlags = mScrollWithPage? 0 :
(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
nsIPresShell::RENDER_DOCUMENT_RELATIVE);
nsRect r(nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetX),
nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetY),
nsPresContext::CSSPixelsToAppUnits((float)mViewportWidth),
nsPresContext::CSSPixelsToAppUnits((float)mViewportHeight));
RefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer();
RefPtr<DrawTarget> dt =
@ -259,8 +288,8 @@ MediaEngineTabVideoSource::Draw() {
return;
}
RefPtr<gfxContext> context = new gfxContext(dt);
context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/innerWidth),
(((float) size.height)/innerHeight)));
context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/mViewportWidth),
(((float) size.height)/mViewportHeight)));
NS_ENSURE_SUCCESS_VOID(presShell->RenderDocument(r, renderDocFlags, bgColor, context));
@ -291,15 +320,6 @@ MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
return NS_OK;
}
nsresult
MediaEngineTabVideoSource::Restart(const dom::MediaTrackConstraints& aConstraints,
const mozilla::MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
{
// TODO
return NS_OK;
}
nsresult
MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
{

View File

@ -76,11 +76,15 @@ protected:
~MediaEngineTabVideoSource() {}
private:
int mBufWidthMax;
int mBufHeightMax;
int32_t mBufWidthMax;
int32_t mBufHeightMax;
int64_t mWindowId;
bool mScrollWithPage;
int mTimePerFrame;
int32_t mViewportOffsetX;
int32_t mViewportOffsetY;
int32_t mViewportWidth;
int32_t mViewportHeight;
int32_t mTimePerFrame;
ScopedFreePtr<unsigned char> mData;
size_t mDataSize;
nsCOMPtr<nsIDOMWindow> mWindow;

View File

@ -48,6 +48,9 @@ struct NormalizedConstraintSet
template<class ConstrainRange>
void SetFrom(const ConstrainRange& aOther);
ValueType Clamp(ValueType n) const { return std::max(mMin, std::min(n, mMax)); }
ValueType Get(ValueType defaultValue) const {
return Clamp(mIdeal.WasPassed() ? mIdeal.Value() : defaultValue);
}
bool Intersects(const Range& aOther) const {
return mMax >= aOther.mMin && mMin <= aOther.mMax;
}
@ -72,12 +75,17 @@ struct NormalizedConstraintSet
// Do you need to add your constraint here? Only if your code uses flattening
LongRange mWidth, mHeight;
DoubleRange mFrameRate;
LongRange mViewportOffsetX, mViewportOffsetY, mViewportWidth, mViewportHeight;
NormalizedConstraintSet(const dom::MediaTrackConstraintSet& aOther,
bool advanced)
: mWidth(aOther.mWidth, advanced)
, mHeight(aOther.mHeight, advanced)
, mFrameRate(aOther.mFrameRate, advanced) {}
, mFrameRate(aOther.mFrameRate, advanced)
, mViewportOffsetX(aOther.mViewportOffsetX, advanced)
, mViewportOffsetY(aOther.mViewportOffsetY, advanced)
, mViewportWidth(aOther.mViewportWidth, advanced)
, mViewportHeight(aOther.mViewportHeight, advanced) {}
};
struct FlattenedConstraints : public NormalizedConstraintSet

View File

@ -153,6 +153,9 @@ PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchComple
else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_ARM;
}
else if (base::PROCESS_ARCH_MIPS & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_MIPS;
}
else {
return false;
}

View File

@ -50,6 +50,10 @@ dictionary MediaTrackConstraintSet {
long long browserWindow;
boolean scrollWithPage;
ConstrainDOMString deviceId;
ConstrainLong viewportOffsetX;
ConstrainLong viewportOffsetY;
ConstrainLong viewportWidth;
ConstrainLong viewportHeight;
};
dictionary MediaTrackConstraints : MediaTrackConstraintSet {

View File

@ -29,7 +29,12 @@ dictionary MediaTrackSupportedConstraints {
boolean mediaSource = true;
// Experimental https://bugzilla.mozilla.org/show_bug.cgi?id=1131568#c3
// https://bugzilla.mozilla.org/show_bug.cgi?id=1193075
boolean browserWindow = true;
boolean scrollWithPage = true;
boolean viewportOffsetX = true;
boolean viewportOffsetY = true;
boolean viewportWidth = true;
boolean viewportHeight = true;
};

View File

@ -133,7 +133,8 @@ enum class LogReason : int {
MustBeMoreThanThis = -1,
// Start. Do not insert, always add at end. If you remove items,
// make sure the other items retain their values.
D3D11InvalidCallDeviceRemoved = 0,
D3D11InvalidCall = 1,
// End
MustBeLessThanThis = 101,
};

View File

@ -27,10 +27,10 @@ SharedSurfaceTextureClient::SharedSurfaceTextureClient(ISurfaceAllocator* aAlloc
TextureFlags aFlags,
UniquePtr<gl::SharedSurface> surf,
gl::SurfaceFactory* factory)
: TextureClient(aAllocator, aFlags | TextureFlags::RECYCLE)
: TextureClient(aAllocator,
aFlags | TextureFlags::RECYCLE | surf->GetTextureFlags())
, mSurf(Move(surf))
{
AddFlags(mSurf->GetTextureFlags());
}
SharedSurfaceTextureClient::~SharedSurfaceTextureClient()

View File

@ -480,6 +480,10 @@ CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect,
CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in CreateRenderTarget";
}
RefPtr<ID3D11Texture2D> texture;
HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
if (Failed(hr) || !texture) {
@ -513,6 +517,10 @@ CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
aRect.width, aRect.height, 1, 1,
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in CreateRenderTargetFromSource";
}
RefPtr<ID3D11Texture2D> texture;
HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
NS_ASSERTION(texture, "Could not create texture");
@ -1015,6 +1023,10 @@ CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
Rect* aClipRectOut,
Rect* aRenderBoundsOut)
{
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in BeginFrame";
}
// Don't composite if we are minimised. Other than for the sake of efficency,
// this is important because resizing our buffers when mimised will fail and
// cause a crash when we're restored.
@ -1025,10 +1037,9 @@ CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
}
IntSize oldSize = mSize;
UpdateRenderTarget();
// Failed to create a render target or the view.
if (!mDefaultRT || !mDefaultRT->mRTView ||
if (!UpdateRenderTarget() || !mDefaultRT || !mDefaultRT->mRTView ||
mSize.width <= 0 || mSize.height <= 0) {
*aRenderBoundsOut = Rect();
return;
@ -1140,7 +1151,10 @@ CompositorD3D11::EndFrame()
params.pDirtyRects = params.DirtyRectsCount ? rects.data() : nullptr;
chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, &params);
} else {
mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
hr = mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
if (Failed(hr)) {
gfxCriticalNote << "D3D11 swap chain preset failed " << hexa(hr);
}
}
mDisableSequenceForNextFrame = false;
if (mTarget) {
@ -1224,26 +1238,33 @@ CompositorD3D11::VerifyBufferSize()
hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
DXGI_FORMAT_B8G8R8A8_UNORM,
0);
if (Failed(hr)) {
gfxCriticalNote << "D3D11 swap resize buffers failed " << hexa(hr) << " on " << mSize;
}
return Succeeded(hr);
}
void
bool
CompositorD3D11::UpdateRenderTarget()
{
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in UpdateRenderTarget";
}
EnsureSize();
if (!VerifyBufferSize()) {
gfxCriticalNote << "Failed VerifyBufferSize in UpdateRenderTarget " << mSize;
return;
return false;
}
if (mDefaultRT) {
return;
return true;
}
if (mSize.width <= 0 || mSize.height <= 0) {
gfxCriticalNote << "Invalid size in UpdateRenderTarget " << mSize;
return;
return false;
}
HRESULT hr;
@ -1255,16 +1276,18 @@ CompositorD3D11::UpdateRenderTarget()
// This happens on some GPUs/drivers when there's a TDR.
if (mDevice->GetDeviceRemovedReason() != S_OK) {
gfxCriticalError() << "GetBuffer returned invalid call!";
return;
return false;
}
}
if (Failed(hr)) {
gfxCriticalNote << "Failed in UpdateRenderTarget " << hexa(hr);
return;
return false;
}
mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
mDefaultRT->SetSize(mSize);
return true;
}
bool
@ -1282,6 +1305,10 @@ DeviceAttachmentsD3D11::InitSyncObject()
D3D11_BIND_RENDER_TARGET);
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in InitSyncObject";
}
RefPtr<ID3D11Texture2D> texture;
HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
if (Failed(hr, "create sync texture")) {
@ -1410,6 +1437,10 @@ CompositorD3D11::PaintToTarget()
RefPtr<ID3D11Texture2D> readTexture;
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in PaintToTarget";
}
hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
if (Failed(hr)) {
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in PaintToTarget 2";
@ -1457,10 +1488,20 @@ CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
MOZ_CRASH("Unrecoverable D3D11 error");
}
if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
gfxCriticalError() << "Out of sync D3D11 devices in HandleError";
}
HRESULT hrOnReset = S_OK;
bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
if (deviceRemoved && mDevice) {
hr = mDevice->GetDeviceRemovedReason();
hrOnReset = mDevice->GetDeviceRemovedReason();
} else if (hr == DXGI_ERROR_INVALID_CALL && mDevice) {
hrOnReset = mDevice->GetDeviceRemovedReason();
if (hrOnReset != S_OK) {
deviceRemoved = true;
}
}
// Device reset may not be an error on our side, but can mess things up so
@ -1468,11 +1509,11 @@ CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
gfxCriticalError(CriticalLog::DefaultOptions(!deviceRemoved))
<< (deviceRemoved ? "[CompositorD3D11] device removed with error code: "
: "[CompositorD3D11] error code: ")
<< hexa(hr);
<< hexa(hr) << ", " << hexa(hrOnReset);
// Always crash if we are making invalid calls
// Crash if we are making invalid calls outside of device removal
if (hr == DXGI_ERROR_INVALID_CALL) {
MOZ_CRASH("Invalid D3D11 api call");
gfxCrash(deviceRemoved ? LogReason::D3D11InvalidCallDeviceRemoved : LogReason::D3D11InvalidCall) << "Invalid D3D11 api call";
}
if (aSeverity == Recoverable) {

View File

@ -167,7 +167,7 @@ private:
// ensure mSize is up to date with respect to mWidget
void EnsureSize();
bool VerifyBufferSize();
void UpdateRenderTarget();
bool UpdateRenderTarget();
bool UpdateConstantBuffers();
void SetSamplerForFilter(gfx::Filter aFilter);
void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);

View File

@ -71,7 +71,8 @@ enum ProcessArchitecture {
PROCESS_ARCH_I386 = 0x1,
PROCESS_ARCH_X86_64 = 0x2,
PROCESS_ARCH_PPC = 0x4,
PROCESS_ARCH_ARM = 0x8
PROCESS_ARCH_ARM = 0x8,
PROCESS_ARCH_MIPS = 0x10
};
inline ProcessArchitecture GetCurrentProcessArchitecture()
@ -85,6 +86,8 @@ inline ProcessArchitecture GetCurrentProcessArchitecture()
currentArchitecture = base::PROCESS_ARCH_PPC;
#elif defined(ARCH_CPU_ARMEL)
currentArchitecture = base::PROCESS_ARCH_ARM;
#elif defined(ARCH_CPU_MIPS)
currentArchitecture = base::PROCESS_ARCH_MIPS;
#endif
return currentArchitecture;
}

View File

@ -42,12 +42,11 @@ using mozilla::UniquePtr;
struct KeywordInfo {
const char* chars; // C string with keyword text
TokenKind tokentype;
JSVersion version;
};
static const KeywordInfo keywords[] = {
#define KEYWORD_INFO(keyword, name, type, version) \
{js_##keyword##_str, type, version},
#define KEYWORD_INFO(keyword, name, type) \
{js_##keyword##_str, type},
FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_INFO)
#undef KEYWORD_INFO
};
@ -997,24 +996,21 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
return reportError(JSMSG_RESERVED_ID, kw->chars);
}
if (kw->tokentype != TOK_STRICT_RESERVED) {
if (kw->version <= versionNumber()) {
// Treat 'let' as an identifier and contextually a keyword in
// sloppy mode. It is always a keyword in strict mode.
if (kw->tokentype == TOK_LET && !strictMode())
return true;
if (kw->tokentype == TOK_STRICT_RESERVED)
return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
// Working keyword.
if (ttp) {
*ttp = kw->tokentype;
return true;
}
return reportError(JSMSG_RESERVED_ID, kw->chars);
}
// Treat 'let' as an identifier and contextually a keyword in sloppy mode.
// It is always a keyword in strict mode.
if (kw->tokentype == TOK_LET && !strictMode())
return true;
// Working keyword.
if (ttp) {
*ttp = kw->tokentype;
return true;
}
// Strict reserved word.
return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
return reportError(JSMSG_RESERVED_ID, kw->chars);
}
bool

View File

@ -6,7 +6,7 @@
//
// These should not run with --no-asmjs.
if (!isAsmJSCompilationAvailable())
if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
quit(0);
//////////////////////////////////////////////////////////////////////

View File

@ -492,8 +492,8 @@ class MacroAssembler : public MacroAssemblerSpecific
// Push the return address and make a call. On platforms where this function
// is not defined, push the link register (pushReturnAddress) at the entry
// point of the callee.
void callAndPushReturnAddress(Register reg) DEFINED_ON(mips32, x86_shared);
void callAndPushReturnAddress(Label* label) DEFINED_ON(mips32, x86_shared);
void callAndPushReturnAddress(Register reg) DEFINED_ON(mips_shared, x86_shared);
void callAndPushReturnAddress(Label* label) DEFINED_ON(mips_shared, x86_shared);
void pushReturnAddress() DEFINED_ON(arm, arm64);

View File

@ -111,11 +111,22 @@ CodeGeneratorMIPSShared::visitTestIAndBranch(LTestIAndBranch* test)
void
CodeGeneratorMIPSShared::visitCompare(LCompare* comp)
{
Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop());
MCompare* mir = comp->mir();
Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop());
const LAllocation* left = comp->getOperand(0);
const LAllocation* right = comp->getOperand(1);
const LDefinition* def = comp->getDef(0);
#ifdef JS_CODEGEN_MIPS64
if (mir->compareType() == MCompare::Compare_Object) {
if (right->isGeneralReg())
masm.cmpPtrSet(cond, ToRegister(left), ToRegister(right), ToRegister(def));
else
masm.cmpPtrSet(cond, ToRegister(left), ToAddress(right), ToRegister(def));
return;
}
#endif
if (right->isConstant())
masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def));
else if (right->isGeneralReg())
@ -127,7 +138,23 @@ CodeGeneratorMIPSShared::visitCompare(LCompare* comp)
void
CodeGeneratorMIPSShared::visitCompareAndBranch(LCompareAndBranch* comp)
{
Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
MCompare* mir = comp->cmpMir();
Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop());
#ifdef JS_CODEGEN_MIPS64
if (mir->compareType() == MCompare::Compare_Object) {
if (comp->right()->isGeneralReg()) {
emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond,
comp->ifTrue(), comp->ifFalse());
} else {
masm.loadPtr(ToAddress(comp->right()), ScratchRegister);
emitBranch(ToRegister(comp->left()), ScratchRegister, cond,
comp->ifTrue(), comp->ifFalse());
}
return;
}
#endif
if (comp->right()->isConstant()) {
emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond,
comp->ifTrue(), comp->ifFalse());
@ -198,16 +225,6 @@ CodeGeneratorMIPSShared::bailout(LSnapshot* snapshot)
bailoutFrom(&label, snapshot);
}
void
CodeGeneratorMIPSShared::visitOutOfLineBailout(OutOfLineBailout* ool)
{
// Push snapshotOffset and make sure stack is aligned.
masm.subPtr(Imm32(2 * sizeof(void*)), StackPointer);
masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0));
masm.jump(&deoptLabel_);
}
void
CodeGeneratorMIPSShared::visitMinMaxD(LMinMaxD* ins)
{

View File

@ -175,7 +175,7 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared
virtual void visitTruncateFToInt32(LTruncateFToInt32* ins);
// Out of line visitors.
void visitOutOfLineBailout(OutOfLineBailout* ool);
virtual void visitOutOfLineBailout(OutOfLineBailout* ool) = 0;
protected:
virtual ValueOperand ToOutValue(LInstruction* ins) = 0;
void memoryBarrier(MemoryBarrierBits barrier);

View File

@ -898,6 +898,24 @@ MacroAssembler::call(JitCode* c)
callJitNoProfiler(ScratchRegister);
}
void
MacroAssembler::callAndPushReturnAddress(Register callee)
{
// Push return address during jalr delay slot.
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
as_jalr(callee);
storePtr(ra, Address(StackPointer, 0));
}
void
MacroAssembler::callAndPushReturnAddress(Label* label)
{
// Push return address during bal delay slot.
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
ma_bal(label, DontFillDelaySlot);
storePtr(ra, Address(StackPointer, 0));
}
// ===============================================================
// Jit Frames.

View File

@ -46,6 +46,16 @@ class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPS
}
};
void
CodeGeneratorMIPS::visitOutOfLineBailout(OutOfLineBailout* ool)
{
// Push snapshotOffset and make sure stack is aligned.
masm.subPtr(Imm32(2 * sizeof(void*)), StackPointer);
masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0));
masm.jump(&deoptLabel_);
}
void
CodeGeneratorMIPS::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
{

View File

@ -40,6 +40,7 @@ class CodeGeneratorMIPS : public CodeGeneratorMIPSShared
virtual void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
// Out of line visitors.
void visitOutOfLineBailout(OutOfLineBailout* ool);
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
protected:
ValueOperand ToValue(LInstruction* ins, size_t pos);

View File

@ -2562,27 +2562,6 @@ MacroAssembler::reserveStack(uint32_t amount)
adjustFrame(amount);
}
// ===============================================================
// Simple call functions.
void
MacroAssembler::callAndPushReturnAddress(Register callee)
{
// Push return address during jalr delay slot.
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
as_jalr(callee);
storePtr(ra, Address(StackPointer, 0));
}
void
MacroAssembler::callAndPushReturnAddress(Label* label)
{
// Push return address during bal delay slot.
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
ma_bal(label, DontFillDelaySlot);
storePtr(ra, Address(StackPointer, 0));
}
// ===============================================================
// ABI function calls.

View File

@ -14,7 +14,7 @@ using namespace js::jit;
BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations,
BailoutStack* bailout)
: machine_(bailout->machine())
: machine_(bailout->machineState())
{
uint8_t* sp = bailout->parentStackPointer();
framePointer_ = sp + bailout->frameSize();

View File

@ -15,60 +15,27 @@ namespace jit {
class BailoutStack
{
uintptr_t frameClassId_;
// This is pushed in the bailout handler. Both entry points into the
// handler inserts their own value int lr, which is then placed onto the
// stack along with frameClassId_ above. This should be migrated to ip.
public:
union {
uintptr_t frameSize_;
uintptr_t tableOffset_;
};
protected:
RegisterDump::FPUArray fpregs_;
RegisterDump::GPRArray regs_;
uintptr_t frameSize_;
uintptr_t snapshotOffset_;
uintptr_t padding_;
public:
FrameSizeClass frameClass() const {
return FrameSizeClass::FromClass(frameClassId_);
}
uintptr_t tableOffset() const {
MOZ_ASSERT(frameClass() != FrameSizeClass::None());
return tableOffset_;
}
uint32_t frameSize() const {
if (frameClass() == FrameSizeClass::None())
return frameSize_;
return frameClass().frameSize();
}
MachineState machine() {
MachineState machineState() {
return MachineState::FromBailout(regs_, fpregs_);
}
SnapshotOffset snapshotOffset() const {
MOZ_ASSERT(frameClass() == FrameSizeClass::None());
uint32_t snapshotOffset() const {
return snapshotOffset_;
}
uint8_t* parentStackPointer() const {
if (frameClass() == FrameSizeClass::None())
return (uint8_t*)this + sizeof(BailoutStack);
return (uint8_t*)this + offsetof(BailoutStack, snapshotOffset_);
uint32_t frameSize() const {
return frameSize_;
}
static size_t offsetOfFrameClass() {
return offsetof(BailoutStack, frameClassId_);
uint8_t* parentStackPointer() {
return (uint8_t*)this + sizeof(BailoutStack);
}
static size_t offsetOfFrameSize() {
return offsetof(BailoutStack, frameSize_);
}
static size_t offsetOfFpRegs() {
return offsetof(BailoutStack, fpregs_);
}
static size_t offsetOfRegs() {
return offsetof(BailoutStack, regs_);
}
};
} // namespace jit

View File

@ -0,0 +1,290 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "jit/mips64/CodeGenerator-mips64.h"
#include "mozilla/MathAlgorithms.h"
#include "jit/CodeGenerator.h"
#include "jit/JitCompartment.h"
#include "jit/JitFrames.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "js/Conversions.h"
#include "vm/Shape.h"
#include "vm/TraceLogging.h"
#include "jit/MacroAssembler-inl.h"
#include "jit/shared/CodeGenerator-shared-inl.h"
using namespace js;
using namespace js::jit;
class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPS64>
{
MTableSwitch* mir_;
CodeLabel jumpLabel_;
void accept(CodeGeneratorMIPS64* codegen) {
codegen->visitOutOfLineTableSwitch(this);
}
public:
OutOfLineTableSwitch(MTableSwitch* mir)
: mir_(mir)
{}
MTableSwitch* mir() const {
return mir_;
}
CodeLabel* jumpLabel() {
return &jumpLabel_;
}
};
void
CodeGeneratorMIPS64::visitOutOfLineBailout(OutOfLineBailout* ool)
{
masm.push(ImmWord(ool->snapshot()->snapshotOffset()));
masm.jump(&deoptLabel_);
}
void
CodeGeneratorMIPS64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
{
MTableSwitch* mir = ool->mir();
masm.haltingAlign(sizeof(void*));
masm.bind(ool->jumpLabel()->src());
masm.addCodeLabel(*ool->jumpLabel());
for (size_t i = 0; i < mir->numCases(); i++) {
LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir();
Label* caseheader = caseblock->label();
uint32_t caseoffset = caseheader->offset();
// The entries of the jump table need to be absolute addresses and thus
// must be patched after codegen is finished. Each table entry uses 8
// instructions (4 for load address, 2 for branch, and 2 padding).
CodeLabel cl;
masm.ma_li(ScratchRegister, cl.dest());
masm.branch(ScratchRegister);
masm.as_nop();
masm.as_nop();
cl.src()->bind(caseoffset);
masm.addCodeLabel(cl);
}
}
void
CodeGeneratorMIPS64::emitTableSwitchDispatch(MTableSwitch* mir, Register index,
Register address)
{
Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
// Lower value with low value
if (mir->low() != 0)
masm.subPtr(Imm32(mir->low()), index);
// Jump to default case if input is out of range
int32_t cases = mir->numCases();
masm.branch32(Assembler::AboveOrEqual, index, Imm32(cases), defaultcase);
// To fill in the CodeLabels for the case entries, we need to first
// generate the case entries (we don't yet know their offsets in the
// instruction stream).
OutOfLineTableSwitch* ool = new(alloc()) OutOfLineTableSwitch(mir);
addOutOfLineCode(ool, mir);
// Compute the position where a pointer to the right case stands.
masm.ma_li(address, ool->jumpLabel()->dest());
// index = size of table entry * index.
// See CodeGeneratorMIPS64::visitOutOfLineTableSwitch
masm.lshiftPtr(Imm32(5), index);
masm.addPtr(index, address);
masm.branch(address);
}
FrameSizeClass
FrameSizeClass::FromDepth(uint32_t frameDepth)
{
return FrameSizeClass::None();
}
FrameSizeClass
FrameSizeClass::ClassLimit()
{
return FrameSizeClass(0);
}
uint32_t
FrameSizeClass::frameSize() const
{
MOZ_CRASH("MIPS64 does not use frame size classes");
}
ValueOperand
CodeGeneratorMIPS64::ToValue(LInstruction* ins, size_t pos)
{
return ValueOperand(ToRegister(ins->getOperand(pos)));
}
ValueOperand
CodeGeneratorMIPS64::ToOutValue(LInstruction* ins)
{
return ValueOperand(ToRegister(ins->getDef(0)));
}
ValueOperand
CodeGeneratorMIPS64::ToTempValue(LInstruction* ins, size_t pos)
{
return ValueOperand(ToRegister(ins->getTemp(pos)));
}
void
CodeGeneratorMIPS64::visitBox(LBox* box)
{
const LAllocation* in = box->getOperand(0);
const LDefinition* result = box->getDef(0);
if (IsFloatingPointType(box->type())) {
FloatRegister reg = ToFloatRegister(in);
if (box->type() == MIRType_Float32) {
masm.convertFloat32ToDouble(reg, ScratchDoubleReg);
reg = ScratchDoubleReg;
}
masm.moveFromDouble(reg, ToRegister(result));
} else {
masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
}
}
void
CodeGeneratorMIPS64::visitUnbox(LUnbox* unbox)
{
MUnbox* mir = unbox->mir();
if (mir->fallible()) {
const ValueOperand value = ToValue(unbox, LUnbox::Input);
masm.splitTag(value, SecondScratchReg);
bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(MIRTypeToTag(mir->type())),
unbox->snapshot());
}
Operand input = ToOperand(unbox->getOperand(LUnbox::Input));
Register result = ToRegister(unbox->output());
switch (mir->type()) {
case MIRType_Int32:
masm.unboxInt32(input, result);
break;
case MIRType_Boolean:
masm.unboxBoolean(input, result);
break;
case MIRType_Object:
masm.unboxObject(input, result);
break;
case MIRType_String:
masm.unboxString(input, result);
break;
case MIRType_Symbol:
masm.unboxSymbol(input, result);
break;
default:
MOZ_CRASH("Given MIRType cannot be unboxed.");
}
}
Register
CodeGeneratorMIPS64::splitTagForTest(const ValueOperand& value)
{
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
masm.splitTag(value.valueReg(), SecondScratchReg);
return SecondScratchReg;
}
void
CodeGeneratorMIPS64::visitCompareB(LCompareB* lir)
{
MCompare* mir = lir->mir();
const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
const LAllocation* rhs = lir->rhs();
const Register output = ToRegister(lir->output());
MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
// Load boxed boolean in ScratchRegister.
if (rhs->isConstant())
masm.moveValue(*rhs->toConstant(), ScratchRegister);
else
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
// Perform the comparison.
masm.cmpPtrSet(cond, lhs.valueReg(), ScratchRegister, output);
}
void
CodeGeneratorMIPS64::visitCompareBAndBranch(LCompareBAndBranch* lir)
{
MCompare* mir = lir->cmpMir();
const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
const LAllocation* rhs = lir->rhs();
MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
// Load boxed boolean in ScratchRegister.
if (rhs->isConstant())
masm.moveValue(*rhs->toConstant(), ScratchRegister);
else
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
// Perform the comparison.
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
emitBranch(lhs.valueReg(), ScratchRegister, cond, lir->ifTrue(), lir->ifFalse());
}
void
CodeGeneratorMIPS64::visitCompareBitwise(LCompareBitwise* lir)
{
MCompare* mir = lir->mir();
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
const ValueOperand lhs = ToValue(lir, LCompareBitwise::LhsInput);
const ValueOperand rhs = ToValue(lir, LCompareBitwise::RhsInput);
const Register output = ToRegister(lir->output());
MOZ_ASSERT(IsEqualityOp(mir->jsop()));
masm.cmpPtrSet(cond, lhs.valueReg(), rhs.valueReg(), output);
}
void
CodeGeneratorMIPS64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
{
MCompare* mir = lir->cmpMir();
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
const ValueOperand lhs = ToValue(lir, LCompareBitwiseAndBranch::LhsInput);
const ValueOperand rhs = ToValue(lir, LCompareBitwiseAndBranch::RhsInput);
MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
emitBranch(lhs.valueReg(), rhs.valueReg(), cond, lir->ifTrue(), lir->ifFalse());
}
void
CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
{
MOZ_ASSERT(ReturnFloat32Reg.reg_ == FloatRegisters::f0);
MOZ_ASSERT(ReturnDoubleReg.reg_ == FloatRegisters::f0);
FloatRegister f1 = { FloatRegisters::f1, FloatRegisters::Single };
regs->add(ReturnFloat32Reg);
regs->add(f1);
regs->add(ReturnDoubleReg);
}

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef jit_mips64_CodeGenerator_mips64_h
#define jit_mips64_CodeGenerator_mips64_h
#include "jit/mips-shared/CodeGenerator-mips-shared.h"
namespace js {
namespace jit {
class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
{
protected:
void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value,
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
{
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
masm.splitTag(value.valueReg(), SecondScratchReg);
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_NULL), cond, ifTrue, ifFalse);
}
void testUndefinedEmitBranch(Assembler::Condition cond, const ValueOperand& value,
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
{
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
masm.splitTag(value.valueReg(), SecondScratchReg);
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), cond, ifTrue, ifFalse);
}
void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand& value,
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
{
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
masm.splitTag(value.valueReg(), SecondScratchReg);
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), cond, ifTrue, ifFalse);
}
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
public:
virtual void visitCompareB(LCompareB* lir);
virtual void visitCompareBAndBranch(LCompareBAndBranch* lir);
virtual void visitCompareBitwise(LCompareBitwise* lir);
virtual void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
// Out of line visitors.
void visitOutOfLineBailout(OutOfLineBailout* ool);
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
protected:
ValueOperand ToValue(LInstruction* ins, size_t pos);
ValueOperand ToOutValue(LInstruction* ins);
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
// Functions for LTestVAndBranch.
Register splitTagForTest(const ValueOperand& value);
public:
CodeGeneratorMIPS64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
: CodeGeneratorMIPSShared(gen, graph, masm)
{ }
public:
void visitBox(LBox* box);
void visitUnbox(LUnbox* unbox);
void setReturnDoubleRegs(LiveRegisterSet* regs);
};
typedef CodeGeneratorMIPS64 CodeGeneratorSpecific;
} // namespace jit
} // namespace js
#endif /* jit_mips64_CodeGenerator_mips64_h */

View File

@ -573,20 +573,14 @@ JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
* (See: JitRuntime::generateBailoutHandler).
*/
static void
PushBailoutFrame(MacroAssembler& masm, uint32_t frameClass, Register spArg)
PushBailoutFrame(MacroAssembler& masm, Register spArg)
{
// Make sure that alignment is proper.
masm.checkStackAlignment();
// Push registers such that we can access them from [base + code].
masm.PushRegsInMask(AllRegs);
// Push the frameSize_ or tableOffset_ stored in ra
// Push the frameSize_ stored in ra
// See: CodeGeneratorMIPS64::generateOutOfLineCode()
masm.push(ra);
// Push frame class to stack
masm.push(ImmWord(frameClass));
// Push registers such that we can access them from [base + code].
masm.PushRegsInMask(AllRegs);
// Put pointer to BailoutStack as first argument to the Bailout()
masm.movePtr(StackPointer, spArg);
@ -595,12 +589,11 @@ PushBailoutFrame(MacroAssembler& masm, uint32_t frameClass, Register spArg)
static void
GenerateBailoutThunk(JSContext* cx, MacroAssembler& masm, uint32_t frameClass)
{
PushBailoutFrame(masm, frameClass, a0);
PushBailoutFrame(masm, a0);
// Put pointer to BailoutInfo
static const uint32_t sizeOfBailoutInfo = sizeof(uintptr_t) * 2;
masm.subPtr(Imm32(sizeOfBailoutInfo), StackPointer);
masm.storePtr(ImmPtr(nullptr), Address(StackPointer, 0));
masm.movePtr(StackPointer, a1);
masm.setupAlignedABICall();
@ -611,6 +604,13 @@ GenerateBailoutThunk(JSContext* cx, MacroAssembler& masm, uint32_t frameClass)
// Get BailoutInfo pointer
masm.loadPtr(Address(StackPointer, 0), a2);
// Stack is:
// [frame]
// snapshotOffset
// frameSize
// [bailoutFrame]
// [bailoutInfo]
//
// Remove both the bailout frame and the topmost Ion frame's stack.
// Load frameSize from stack
masm.loadPtr(Address(StackPointer,

View File

@ -781,7 +781,8 @@ class AsmJSHeapAccess
cmpDelta_ = cmp == NoLengthCheck ? 0 : insnOffset - cmp;
MOZ_ASSERT(offsetWithinWholeSimdVector_ == offsetWithinWholeSimdVector);
}
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32)
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
explicit AsmJSHeapAccess(uint32_t insnOffset)
{
mozilla::PodZero(this); // zero padding for Valgrind

View File

@ -1597,8 +1597,8 @@ CodeGeneratorShared::jumpToBlock(MBasicBlock* mir)
}
}
// This function is not used for MIPS. MIPS has branchToBlock.
#ifndef JS_CODEGEN_MIPS32
// This function is not used for MIPS/MIPS64. MIPS has branchToBlock.
#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64)
void
CodeGeneratorShared::jumpToBlock(MBasicBlock* mir, Assembler::Condition cond)
{

View File

@ -471,7 +471,7 @@ class CodeGeneratorShared : public LElementVisitor
void jumpToBlock(MBasicBlock* mir);
// This function is not used for MIPS. MIPS has branchToBlock.
#ifndef JS_CODEGEN_MIPS32
#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64)
void jumpToBlock(MBasicBlock* mir, Assembler::Condition cond);
#endif

View File

@ -190,7 +190,7 @@ LIRGeneratorShared::defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefini
#elif defined(JS_CODEGEN_ARM64)
lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE,
LFloatReg(FloatRegister(FloatRegisters::d1, FloatRegisters::Double))));
#elif defined(JS_CODEGEN_MIPS32)
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, LFloatReg(f2)));
#elif defined(JS_CODEGEN_NONE)
MOZ_CRASH();

View File

@ -14,7 +14,7 @@
#include "vm/Keywords.h"
static const char * const keyword_list[] = {
#define KEYWORD_STRING(keyword, name, type, version) #keyword,
#define KEYWORD_STRING(keyword, name, type) #keyword,
FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_STRING)
#undef KEYWORD_STRING
};

View File

@ -515,6 +515,7 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'jit/mips64/Bailouts-mips64.cpp',
'jit/mips64/BaselineCompiler-mips64.cpp',
'jit/mips64/BaselineIC-mips64.cpp',
'jit/mips64/CodeGenerator-mips64.cpp',
'jit/mips64/Lowering-mips64.cpp',
'jit/mips64/SharedIC-mips64.cpp',
'jit/mips64/Trampoline-mips64.cpp',

View File

@ -10,58 +10,58 @@
#define vm_Keywords_h
#define FOR_EACH_JAVASCRIPT_KEYWORD(macro) \
macro(false, false_, TOK_FALSE, JSVERSION_DEFAULT) \
macro(true, true_, TOK_TRUE, JSVERSION_DEFAULT) \
macro(null, null, TOK_NULL, JSVERSION_DEFAULT) \
macro(false, false_, TOK_FALSE) \
macro(true, true_, TOK_TRUE) \
macro(null, null, TOK_NULL) \
/* Keywords. */ \
macro(break, break_, TOK_BREAK, JSVERSION_DEFAULT) \
macro(case, case_, TOK_CASE, JSVERSION_DEFAULT) \
macro(catch, catch_, TOK_CATCH, JSVERSION_DEFAULT) \
macro(const, const_, TOK_CONST, JSVERSION_DEFAULT) \
macro(continue, continue_, TOK_CONTINUE, JSVERSION_DEFAULT) \
macro(debugger, debugger, TOK_DEBUGGER, JSVERSION_DEFAULT) \
macro(default, default_, TOK_DEFAULT, JSVERSION_DEFAULT) \
macro(delete, delete_, TOK_DELETE, JSVERSION_DEFAULT) \
macro(do, do_, TOK_DO, JSVERSION_DEFAULT) \
macro(else, else_, TOK_ELSE, JSVERSION_DEFAULT) \
macro(finally, finally_, TOK_FINALLY, JSVERSION_DEFAULT) \
macro(for, for_, TOK_FOR, JSVERSION_DEFAULT) \
macro(function, function, TOK_FUNCTION, JSVERSION_DEFAULT) \
macro(if, if_, TOK_IF, JSVERSION_DEFAULT) \
macro(in, in, TOK_IN, JSVERSION_DEFAULT) \
macro(instanceof, instanceof, TOK_INSTANCEOF, JSVERSION_DEFAULT) \
macro(new, new_, TOK_NEW, JSVERSION_DEFAULT) \
macro(return, return_, TOK_RETURN, JSVERSION_DEFAULT) \
macro(switch, switch_, TOK_SWITCH, JSVERSION_DEFAULT) \
macro(this, this_, TOK_THIS, JSVERSION_DEFAULT) \
macro(throw, throw_, TOK_THROW, JSVERSION_DEFAULT) \
macro(try, try_, TOK_TRY, JSVERSION_DEFAULT) \
macro(typeof, typeof, TOK_TYPEOF, JSVERSION_DEFAULT) \
macro(var, var, TOK_VAR, JSVERSION_DEFAULT) \
macro(void, void_, TOK_VOID, JSVERSION_DEFAULT) \
macro(while, while_, TOK_WHILE, JSVERSION_DEFAULT) \
macro(with, with, TOK_WITH, JSVERSION_DEFAULT) \
macro(import, import, TOK_IMPORT, JSVERSION_DEFAULT) \
macro(export, export, TOK_EXPORT, JSVERSION_DEFAULT) \
macro(class, class_, TOK_CLASS, JSVERSION_DEFAULT) \
macro(extends, extends, TOK_EXTENDS, JSVERSION_DEFAULT) \
macro(super, super, TOK_SUPER, JSVERSION_DEFAULT) \
macro(break, break_, TOK_BREAK) \
macro(case, case_, TOK_CASE) \
macro(catch, catch_, TOK_CATCH) \
macro(const, const_, TOK_CONST) \
macro(continue, continue_, TOK_CONTINUE) \
macro(debugger, debugger, TOK_DEBUGGER) \
macro(default, default_, TOK_DEFAULT) \
macro(delete, delete_, TOK_DELETE) \
macro(do, do_, TOK_DO) \
macro(else, else_, TOK_ELSE) \
macro(finally, finally_, TOK_FINALLY) \
macro(for, for_, TOK_FOR) \
macro(function, function, TOK_FUNCTION) \
macro(if, if_, TOK_IF) \
macro(in, in, TOK_IN) \
macro(instanceof, instanceof, TOK_INSTANCEOF) \
macro(new, new_, TOK_NEW) \
macro(return, return_, TOK_RETURN) \
macro(switch, switch_, TOK_SWITCH) \
macro(this, this_, TOK_THIS) \
macro(throw, throw_, TOK_THROW) \
macro(try, try_, TOK_TRY) \
macro(typeof, typeof, TOK_TYPEOF) \
macro(var, var, TOK_VAR) \
macro(void, void_, TOK_VOID) \
macro(while, while_, TOK_WHILE) \
macro(with, with, TOK_WITH) \
macro(import, import, TOK_IMPORT) \
macro(export, export, TOK_EXPORT) \
macro(class, class_, TOK_CLASS) \
macro(extends, extends, TOK_EXTENDS) \
macro(super, super, TOK_SUPER) \
/* Reserved keywords. */ \
macro(enum, enum_, TOK_RESERVED, JSVERSION_DEFAULT) \
macro(enum, enum_, TOK_RESERVED) \
/* Future reserved keywords, but only in strict mode. */ \
macro(implements, implements, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(interface, interface, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(package, package, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(private, private_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(static, static_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(implements, implements, TOK_STRICT_RESERVED) \
macro(interface, interface, TOK_STRICT_RESERVED) \
macro(package, package, TOK_STRICT_RESERVED) \
macro(private, private_, TOK_STRICT_RESERVED) \
macro(protected, protected_, TOK_STRICT_RESERVED) \
macro(public, public_, TOK_STRICT_RESERVED) \
macro(static, static_, TOK_STRICT_RESERVED) \
/* \
* Yield is a token inside function*. Outside of a function*, it is a \
* future reserved keyword in strict mode, but a keyword in JS1.7 even \
* when strict. Punt logic to parser. \
*/ \
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
macro(let, let, TOK_LET, JSVERSION_DEFAULT)
macro(yield, yield, TOK_YIELD) \
macro(let, let, TOK_LET)
#endif /* vm_Keywords_h */

View File

@ -385,13 +385,15 @@ QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote)
}
Fprinter::Fprinter(FILE* fp)
: file_(nullptr)
: file_(nullptr),
init_(false)
{
init(fp);
}
Fprinter::Fprinter()
: file_(nullptr)
: file_(nullptr),
init_(false)
{ }
Fprinter::~Fprinter()

View File

@ -65,9 +65,6 @@ public:
return mPresContext;
}
nsCSSFrameConstructor* FrameConstructor() const
{ return PresContext()->FrameConstructor(); }
// Should be called when a frame is going to be destroyed and
// WillDestroyFrameTree hasn't been called yet.
void NotifyDestroyingFrame(nsIFrame* aFrame);
@ -138,6 +135,9 @@ public:
}
private:
nsCSSFrameConstructor* FrameConstructor() const
{ return PresContext()->FrameConstructor(); }
// Used when restyling an element with a frame.
void ComputeAndProcessStyleChange(nsIFrame* aFrame,
nsChangeHint aMinChange,

View File

@ -5088,17 +5088,6 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
result = Matrix4x4::From2D(svgTransform);
}
if (aProperties.mChildPerspective > 0.0) {
Matrix4x4 perspective;
perspective._34 =
-1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel);
/* At the point when perspective is applied, we have been translated to the transform origin.
* The translation to the perspective origin is the difference between these values.
*/
perspective.ChangeBasis(aProperties.GetToPerspectiveOrigin() - aProperties.mToTransformOrigin);
result = result * perspective;
}
/* Account for the transform-origin property by translating the
* coordinate space to the new origin.
*/
@ -5110,12 +5099,15 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y),
0);
bool hasPerspective = aProperties.mChildPerspective > 0.0;
if (!hasSVGTransforms || !hasTransformFromSVGParent) {
// This is a simplification of the following |else| block, the
// simplification being possible because we don't need to apply
// mToTransformOrigin between two transforms.
Point3D offsets = roundedOrigin + aProperties.mToTransformOrigin;
if (aOffsetByOrigin) {
if (aOffsetByOrigin &&
!hasPerspective) {
// We can fold the final translation by roundedOrigin into the first matrix
// basis change translation. This is more stable against variation due to
// insufficient floating point precision than reversing the translation
@ -5149,7 +5141,8 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
// for mToTransformOrigin so we don't include that. We also need to reapply
// refBoxOffset.
Point3D offsets = roundedOrigin + refBoxOffset;
if (aOffsetByOrigin) {
if (aOffsetByOrigin &&
!hasPerspective) {
result.PreTranslate(-refBoxOffset);
result.PostTranslate(offsets);
} else {
@ -5157,6 +5150,19 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
}
}
if (hasPerspective) {
Matrix4x4 perspective;
perspective._34 =
-1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel);
perspective.ChangeBasis(aProperties.GetToPerspectiveOrigin() + roundedOrigin);
result = result * perspective;
if (aOffsetByOrigin) {
result.PreTranslate(roundedOrigin);
}
}
if (aDoPreserves3D && frame && frame->Combines3DTransformWithAncestors()) {
// Include the transform set on our parent
NS_ASSERTION(frame->GetParent() &&

View File

@ -430,7 +430,7 @@ public:
NS_DECL_QUERYFRAME_TARGET(nsIFrame)
nsPresContext* PresContext() const {
return StyleContext()->RuleNode()->PresContext();
return StyleContext()->PresContext();
}
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -47,6 +47,7 @@ fuzzy(1,6400) == feGaussianBlur-1.svg feGaussianBlur-1-ref.svg
== feGaussianBlur-6.svg feGaussianBlur-6-ref.svg
skip-if(d2d) pref(layers.acceleration.disabled,true) == feGaussianBlur-cap-large-directional-radius-on-software.html feGaussianBlur-cap-large-directional-radius-on-software-ref.html
!= feImage-1.svg about:blank # (Make sure our image renders at all)
== feImage-1.svg feImage-1-ref.svg
== feImage-scale-to-primitive-subregion.html feImage-scale-to-primitive-subregion-ref.html

Some files were not shown because too many files have changed in this diff Show More