зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2ginbound, a=merge
--HG-- extra : commitid : 14DCEYRe0b9
This commit is contained in:
Коммит
bff6406254
2
CLOBBER
2
CLOBBER
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
|
@ -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">
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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": {},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 });
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" }, ":"),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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.`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
|
|
|
@ -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");
|
||||
});
|
|
@ -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 };
|
||||
}
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1600,7 +1600,8 @@ MediaDecoderOwner*
|
|||
MediaDecoder::GetOwner()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mOwner;
|
||||
// mOwner is valid until shutdown.
|
||||
return !mShuttingDown ? mOwner : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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>
|
|
@ -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'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ dictionary MediaTrackConstraintSet {
|
|||
long long browserWindow;
|
||||
boolean scrollWithPage;
|
||||
ConstrainDOMString deviceId;
|
||||
ConstrainLong viewportOffsetX;
|
||||
ConstrainLong viewportOffsetY;
|
||||
ConstrainLong viewportWidth;
|
||||
ConstrainLong viewportHeight;
|
||||
};
|
||||
|
||||
dictionary MediaTrackConstraints : MediaTrackConstraintSet {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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, ¶ms);
|
||||
} 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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// These should not run with --no-asmjs.
|
||||
|
||||
if (!isAsmJSCompilationAvailable())
|
||||
if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
|
||||
quit(0);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() &&
|
||||
|
|
|
@ -430,7 +430,7 @@ public:
|
|||
NS_DECL_QUERYFRAME_TARGET(nsIFrame)
|
||||
|
||||
nsPresContext* PresContext() const {
|
||||
return StyleContext()->RuleNode()->PresContext();
|
||||
return StyleContext()->PresContext();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.4 KiB |
|
@ -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
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче