Merge m-c to fx-team. a=merge

This commit is contained in:
Ryan VanderMeulen 2016-08-23 10:11:52 -04:00
commit baeaac9b10
430 changed files with 8267 additions and 12513 deletions

View File

@ -319,7 +319,7 @@ ifdef MOZ_CRASHREPORTER
grep 'sym' $(SYMBOL_INDEX_NAME) > $(SYMBOL_INDEX_NAME).tmp && \
mv $(SYMBOL_INDEX_NAME).tmp $(SYMBOL_INDEX_NAME)
cd $(DIST)/crashreporter-symbols && \
zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt' -x '*test*' -x '*Test*'
zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt'
endif # MOZ_CRASHREPORTER
uploadsymbols:

View File

@ -32,8 +32,6 @@ else:
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']
# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
# the C++.
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.a11y += [
'DocAccessibleChildBase.h',

View File

@ -4,10 +4,10 @@
# 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/.
# With --disable-accessibility, we need to compile PDocAccessible.ipdl, but
# not the C++.
IPDL_SOURCES += ['PDocAccessible.ipdl']
# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
# the C++.
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.a11y += [
'DocAccessibleChild.h',

View File

@ -6,13 +6,13 @@
DIRS += ['typelib']
# With --disable-accessibility, we need to compile PDocAccessible.ipdl (which
# also depends on COMPtrTypes.h), but not the C++.
IPDL_SOURCES += ['PDocAccessible.ipdl']
EXPORTS.mozilla.a11y += ['COMPtrTypes.h']
# with --disable-accessibility we need to compile PDocAccessible.ipdl, but not
# the C++.
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.a11y += [
'COMPtrTypes.h',
'DocAccessibleChild.h',
'PlatformChild.h',
'ProxyAccessible.h'

View File

@ -1479,6 +1479,9 @@ pref("browser.esedbreader.loglevel", "Error");
pref("browser.laterrun.enabled", false);
pref("browser.migrate.automigrate.enabled", false);
// 4 here means the suggestion notification will be automatically
// hidden the 4th day, so it will actually be shown on 3 different days.
pref("browser.migrate.automigrate.daysToOfferUndo", 4);
// Enable browser frames for use on desktop. Only exposed to chrome callers.
pref("dom.mozBrowserFramesEnabled", true);

View File

@ -21,6 +21,7 @@
<command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
<command id="cmd_newNavigatorTab" oncommand="BrowserOpenTab(event);" reserved="true"/>
<command id="cmd_newNavigatorTabNoEvent" oncommand="BrowserOpenTab();" reserved="true"/>
<command id="Browser:OpenFile" oncommand="BrowserOpenFileWindow();"/>
<command id="Browser:SavePage" oncommand="saveBrowser(gBrowser.selectedBrowser);"/>
@ -196,7 +197,7 @@
key="&newNavigatorCmd.key;"
command="cmd_newNavigator"
modifiers="accel"/>
<key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTab"/>
<key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTabNoEvent"/>
<key id="focusURLBar" key="&openCmd.commandkey;" command="Browser:OpenLocation"
modifiers="accel"/>
#ifndef XP_MACOSX

View File

@ -979,7 +979,6 @@ var gBrowserInit = {
// adjust browser UI for popups
gURLBar.setAttribute("readonly", "true");
gURLBar.setAttribute("enablehistory", "false");
goSetCommandEnabled("cmd_newNavigatorTab", false);
}
// Misc. inits.
@ -1901,16 +1900,8 @@ function BrowserOpenTab(event) {
case "tab":
case "tabshifted":
// When accel-click or middle-click are used, open the new tab as
// related to the current tab. We need to exclude key events here,
// where the accel key is required for the shortcut.
// 'event' and its sourceEvent are command events, the latter of which
// doesn't have its own sourceEvent. These events don't indicate how
// they were invoked, except that the sourceEvent for keyboard
// shortcuts have <key> targets, and those for clicking a toolbar
// button or activating a menu item have that button or menuitem as
// their target.
relatedToCurrent = !event.sourceEvent ||
event.sourceEvent.target.localName != "key";
// related to the current tab.
relatedToCurrent = true;
break;
case "current":
where = "tab";
@ -3309,7 +3300,6 @@ var PrintPreviewListener = {
this._simplifyPageTab = null;
}
gBrowser.removeTab(this._printPreviewTab);
gBrowser.deactivatePrintPreviewBrowsers();
this._printPreviewTab = null;
},
_toggleAffectedChrome: function () {
@ -3365,11 +3355,7 @@ var PrintPreviewListener = {
if (this._chromeState.sidebarOpen)
SidebarUI.show(this._sidebarCommand);
},
activateBrowser(browser) {
gBrowser.activateBrowserForPrintPreview(browser);
},
}
}
function getMarkupDocumentViewer()

View File

@ -500,7 +500,8 @@ var ClickEventHandler = {
ctrlKey: event.ctrlKey, metaKey: event.metaKey,
altKey: event.altKey, href: null, title: null,
bookmark: false, referrerPolicy: referrerPolicy,
originAttributes: principal ? principal.originAttributes : {} };
originAttributes: principal ? principal.originAttributes : {},
isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
if (href) {
try {

View File

@ -159,6 +159,7 @@ var AboutHomeListener = {
addEventListener("click", this, true);
addEventListener("pagehide", this, true);
sendAsyncMessage("AboutHome:MaybeShowAutoMigrationUndoNotification");
sendAsyncMessage("AboutHome:RequestUpdate");
},

View File

@ -1632,7 +1632,8 @@
// As frameLoaders start out with an active docShell we have to
// deactivate it if this is not the selected tab's browser or the
// browser window is minimized.
aBrowser.docShellIsActive = this.shouldActivateDocShell(aBrowser);
aBrowser.docShellIsActive = (aBrowser == this.selectedBrowser &&
window.windowState != window.STATE_MINIMIZED);
// Create a new tab progress listener for the new browser we just injected,
// since tab progress listeners have logic for handling the initial about:blank
@ -2733,11 +2734,8 @@
remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID);
}
// If switcher is active, it will intercept swap events and
// react as needed.
if (!this._switcher) {
aOtherBrowser.docShellIsActive = this.shouldActivateDocShell(ourBrowser);
}
aOtherBrowser.docShellIsActive = (ourBrowser == this.selectedBrowser &&
window.windowState != window.STATE_MINIMIZED);
// Swap the docshells
ourBrowser.swapDocShells(aOtherBrowser);
@ -3242,56 +3240,6 @@
</body>
</method>
<!--
List of browsers whose docshells must be active in order for print preview
to work.
-->
<field name="_printPreviewBrowsers">
new Set()
</field>
<method name="activateBrowserForPrintPreview">
<parameter name="aBrowser"/>
<body>
<![CDATA[
this._printPreviewBrowsers.add(aBrowser);
if (this._switcher) {
this._switcher.activateBrowserForPrintPreview(aBrowser);
}
aBrowser.docShellIsActive = true;
]]>
</body>
</method>
<method name="deactivatePrintPreviewBrowsers">
<body>
<![CDATA[
let browsers = this._printPreviewBrowsers;
this._printPreviewBrowsers = new Set();
for (let browser of browsers) {
browser.docShellIsActive = this.shouldActivateDocShell(browser);
}
]]>
</body>
</method>
<!--
Returns true if a given browser's docshell should be active.
-->
<method name="shouldActivateDocShell">
<parameter name="aBrowser"/>
<body>
<![CDATA[
if (this._switcher) {
return this._switcher.shouldActivateDocShell(aBrowser);
}
return (aBrowser == this.selectedBrowser &&
window.windowState != window.STATE_MINIMIZED) ||
this._printPreviewBrowsers.has(aBrowser);
]]>
</body>
</method>
<!--
The tab switcher is responsible for asynchronously switching
tabs in e10s. It waits until the new tab is ready (i.e., the
@ -3323,12 +3271,35 @@
It's important that we always show either the spinner or a tab
whose layers are available. Otherwise the compositor will draw
an entirely black frame, which is very jarring. To ensure this
never happens when switching away from a tab, we assume the
old tab might still be drawn until a MozAfterPaint event
occurs. Because layout and compositing happen asynchronously,
we don't have any other way of knowing when the switch
actually takes place. Therefore, we don't unload the old tab
until the next MozAfterPaint event.
never happens, we do the following:
1. When switching away from a tab, we assume the old tab might
still be drawn until a MozAfterPaint event occurs. Because
layout and compositing happen asynchronously, we don't have
any other way of knowing when the switch actually takes
place. Therefore, we don't unload the old tab until the next
MozAfterPaint event.
2. Suppose that the user switches from tab A to B and then
back to A. Suppose that we ask for tab A's layers to be
unloaded via message M1 after the first switch and then
request them again via message M2 once the second switch
happens. Both loading and unloading of layers happens
asynchronously, and this can cause problems. It's possible
that the content process publishes one last layer tree before
M1 is received. The parent process doesn't know that this
layer tree was published before M1 and not after M2, so it
will display the tab. However, once M1 arrives, the content
process will destroy the layer tree for A and now we will
display black for it.
To counter this problem, we keep tab A in a separate
"unloading" state until the layer tree is actually dropped in
the compositor thread. While the tab is in the "unloading"
state, we're not allowed to request layers for it. Once the
layers are dropped in the compositor, an event will fire and
we will transition the tab to the "unloaded" state. Then we
are free to request the tab's layers again.
-->
<field name="_switcher">null</field>
<method name="_getSwitcher">
@ -3375,6 +3346,11 @@
// True if we're in the midst of switching tabs.
switchInProgress: false,
// Keep an exact list of content processes (tabParent) in which
// we're actively suppressing the display port. This gives a robust
// way to make sure we don't forget to un-suppress.
activeSuppressDisplayport: new Set(),
// Set of tabs that might be visible right now. We maintain
// this set because we can't be sure when a tab is actually
// drawn. A tab is added to this set when we ask to make it
@ -3416,37 +3392,26 @@
return state;
},
setTabStateNoAction(tab, state) {
setTabState: function(tab, state) {
if (state == this.STATE_UNLOADED) {
this.tabState.delete(tab);
} else {
this.tabState.set(tab, state);
}
},
setTabState: function(tab, state) {
this.setTabStateNoAction(tab, state);
let browser = tab.linkedBrowser;
let {tabParent} = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (state == this.STATE_LOADING) {
this.assert(!this.minimized);
// Ask for a MozLayerTreeReady event.
fl.requestNotifyLayerTreeReady();
browser.docShellIsActive = true;
if (!tabParent) {
this.onLayersReady(browser);
}
} else if (state == this.STATE_UNLOADING) {
// Ask for MozLayerTreeCleared event.
fl.requestNotifyLayerTreeCleared();
browser.docShellIsActive = false;
if (!tabParent) {
this.onLayersCleared(browser);
}
}
},
get minimized() {
return window.windowState == window.STATE_MINIMIZED;
},
init: function() {
this.log("START");
@ -3454,12 +3419,7 @@
window.addEventListener("MozLayerTreeReady", this);
window.addEventListener("MozLayerTreeCleared", this);
window.addEventListener("TabRemotenessChange", this);
window.addEventListener("sizemodechange", this);
window.addEventListener("SwapDocShells", this, true);
window.addEventListener("EndSwapDocShells", this, true);
if (!this.minimized) {
this.setTabState(this.requestedTab, this.STATE_LOADED);
}
this.setTabState(this.requestedTab, this.STATE_LOADED);
},
destroy: function() {
@ -3476,11 +3436,13 @@
window.removeEventListener("MozLayerTreeReady", this);
window.removeEventListener("MozLayerTreeCleared", this);
window.removeEventListener("TabRemotenessChange", this);
window.removeEventListener("sizemodechange", this);
window.removeEventListener("SwapDocShells", this, true);
window.removeEventListener("EndSwapDocShells", this, true);
this.tabbrowser._switcher = null;
this.activeSuppressDisplayport.forEach(function(tabParent) {
tabParent.suppressDisplayport(false);
});
this.activeSuppressDisplayport.clear();
},
finish: function() {
@ -3492,7 +3454,7 @@
this.assert(!this.loadTimer);
this.assert(!this.loadingTab);
this.assert(this.lastVisibleTab === this.requestedTab);
this.assert(this.minimized || this.getTabState(this.requestedTab) == this.STATE_LOADED);
this.assert(this.getTabState(this.requestedTab) == this.STATE_LOADED);
this.destroy();
@ -3532,7 +3494,7 @@
}
// Show or hide the spinner as needed.
let needSpinner = this.getTabState(showTab) != this.STATE_LOADED && !this.minimized;
let needSpinner = this.getTabState(showTab) != this.STATE_LOADED;
if (!needSpinner && this.spinnerTab) {
this.spinnerHidden();
this.tabbrowser.removeAttribute("pendingpaint");
@ -3587,15 +3549,14 @@
// We've decided to try to load requestedTab.
loadRequestedTab: function() {
this.assert(!this.loadTimer);
this.assert(!this.minimized);
// loadingTab can be non-null here if we timed out loading the current tab.
// In that case we just overwrite it with a different tab; it's had its chance.
this.loadingTab = this.requestedTab;
this.log("Loading tab " + this.tinfo(this.loadingTab));
this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
this.setTabState(this.requestedTab, this.STATE_LOADING);
this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
},
// This function runs before every event. It fixes up the state
@ -3640,21 +3601,13 @@
this.assert(!this.loadingTab || this.loadTimer);
// If we're not loading anything, try loading the requested tab.
let requestedState = this.getTabState(this.requestedTab);
if (!this.loadTimer && !this.minimized &&
(requestedState == this.STATE_UNLOADED ||
requestedState == this.STATE_UNLOADING)) {
if (!this.loadTimer && this.getTabState(this.requestedTab) == this.STATE_UNLOADED) {
this.loadRequestedTab();
}
// See how many tabs still have work to do.
let numPending = 0;
for (let [tab, state] of this.tabState) {
// Skip print preview browsers since they shouldn't affect tab switching.
if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
continue;
}
if (state == this.STATE_LOADED && tab !== this.requestedTab) {
numPending++;
}
@ -3689,10 +3642,6 @@
// Unload any tabs that can be unloaded.
for (let [tab, state] of this.tabState) {
if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
continue;
}
if (state == this.STATE_LOADED &&
!this.maybeVisibleTabs.has(tab) &&
tab !== this.lastVisibleTab &&
@ -3726,15 +3675,9 @@
// Fires when the layers become available for a tab.
onLayersReady: function(browser) {
let tab = this.tabbrowser.getTabForBrowser(browser);
if (!tab) {
// Devtools sometimes makes its own remote browsers.
return;
}
this.logState(`onLayersReady(${tab._tPos})`);
this.logState("onLayersReady");
this.assert(this.getTabState(tab) == this.STATE_LOADING ||
this.getTabState(tab) == this.STATE_LOADED);
let tab = this.tabbrowser.getTabForBrowser(browser);
this.setTabState(tab, this.STATE_LOADED);
this.maybeFinishTabSwitch();
@ -3756,11 +3699,10 @@
// Called when we're done clearing the layers for a tab.
onLayersCleared: function(browser) {
this.logState("onLayersCleared");
let tab = this.tabbrowser.getTabForBrowser(browser);
if (tab) {
this.logState(`onLayersCleared(${tab._tPos})`);
this.assert(this.getTabState(tab) == this.STATE_UNLOADING ||
this.getTabState(tab) == this.STATE_UNLOADED);
this.setTabState(tab, this.STATE_UNLOADED);
}
},
@ -3769,7 +3711,7 @@
// a MozLayerTreeReady notification that we requested may never fire,
// so we need to simulate it.
onRemotenessChange: function(tab) {
this.logState(`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`);
this.logState("onRemotenessChange");
if (!tab.linkedBrowser.isRemoteBrowser) {
if (this.getTabState(tab) == this.STATE_LOADING) {
this.onLayersReady(tab.linkedBrowser);
@ -3792,98 +3734,34 @@
}
},
onSizeModeChange() {
if (this.minimized) {
for (let [tab, state] of this.tabState) {
// Skip print preview browsers since they shouldn't affect tab switching.
if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
continue;
}
if (state == this.STATE_LOADING || state == this.STATE_LOADED) {
this.setTabState(tab, this.STATE_UNLOADING);
}
if (this.loadTimer) {
this.clearTimer(this.loadTimer);
this.loadTimer = null;
}
this.loadingTab = null;
}
} else {
// Do nothing. We'll automatically start loading the requested tab in
// postActions.
}
},
onSwapDocShells(ourBrowser, otherBrowser) {
// This event fires before the swap. ourBrowser is from
// our window. We save the state of otherBrowser since ourBrowser
// needs to take on that state at the end of the swap.
let otherTabbrowser = otherBrowser.ownerDocument.defaultView.gBrowser;
let otherState;
if (otherTabbrowser && otherTabbrowser._switcher) {
let otherTab = otherTabbrowser.getTabForBrowser(otherBrowser);
otherState = otherTabbrowser._switcher.getTabState(otherTab);
} else {
otherState = (otherBrowser.docShellIsActive
? this.STATE_LOADED
: this.STATE_UNLOADED);
}
if (!this.swapMap) {
this.swapMap = new WeakMap();
}
this.swapMap.set(otherBrowser, otherState);
},
onEndSwapDocShells(ourBrowser, otherBrowser) {
// The swap has happened. We reset the loadingTab in
// case it has been swapped. We also set ourBrowser's state
// to whatever otherBrowser's state was before the swap.
if (this.loadTimer) {
// Clearing the load timer means that we will
// immediately display a spinner if ourBrowser isn't
// ready yet. Typically it will already be ready
// though. If it's not, we're probably in a new window,
// in which case we have no other tabs to display anyway.
this.clearTimer(this.loadTimer);
this.loadTimer = null;
}
this.loadingTab = null;
let otherState = this.swapMap.get(otherBrowser);
this.swapMap.delete(otherBrowser);
let ourTab = this.tabbrowser.getTabForBrowser(ourBrowser);
if (ourTab) {
this.setTabStateNoAction(ourTab, otherState);
}
},
shouldActivateDocShell(browser) {
let tab = this.tabbrowser.getTabForBrowser(browser);
let state = this.getTabState(tab);
return state == this.STATE_LOADING || state == this.STATE_LOADED;
},
activateBrowserForPrintPreview(browser) {
let tab = this.tabbrowser.getTabForBrowser(browser);
this.setTabState(tab, this.STATE_LOADING);
},
// Called when the user asks to switch to a given tab.
requestTab: function(tab) {
if (tab === this.requestedTab) {
return;
}
// Instrumentation to figure out bug 1166351 - if the binding
// on the browser we're switching to has gone away, try to find out
// why. We should remove this and the checkBrowserBindingAlive
// method once bug 1166351 has been closed.
if (this.tabbrowser.AppConstants.E10S_TESTING_ONLY &&
!this.checkBrowserBindingAlive(tab)) {
Cu.reportError("Please report the above errors in bug 1166351.");
return;
}
this.logState("requestTab " + this.tinfo(tab));
this.startTabSwitch();
this.requestedTab = tab;
let browser = this.requestedTab.linkedBrowser;
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
fl.tabParent.suppressDisplayport(true);
this.activeSuppressDisplayport.add(fl.tabParent);
}
this.preActions();
if (this.unloadTimer) {
@ -3915,12 +3793,6 @@
this.onLayersCleared(event.originalTarget);
} else if (event.type == "TabRemotenessChange") {
this.onRemotenessChange(event.target);
} else if (event.type == "sizemodechange") {
this.onSizeModeChange();
} else if (event.type == "SwapDocShells") {
this.onSwapDocShells(event.originalTarget, event.detail);
} else if (event.type == "EndSwapDocShells") {
this.onEndSwapDocShells(event.originalTarget, event.detail);
}
this.postActions();
@ -4004,6 +3876,42 @@
return this._shouldLog;
},
// Instrumentation for bug 1166351
checkBrowserBindingAlive: function(tab) {
let err = Cu.reportError;
if (!tab.linkedBrowser) {
err("Attempting to switch to tab that has no linkedBrowser.");
return false;
}
let b = tab.linkedBrowser;
if (!b._alive) {
// The browser binding has been removed. Dump a bunch of
// diagnostic information to the browser console.
let utils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
let results = utils.getBindingURLs(b);
let urls = [];
for (let i = 0; i < results.length; ++i) {
urls.push(results.queryElementAt(i, Ci.nsIURI).spec);
}
err("The browser has the following bindings:");
err(urls);
err("MozBinding is currently: " +
window.getComputedStyle(b).MozBinding);
if (!b.parentNode) {
err("Browser was removed from the DOM.");
} else {
err("Parent is: " + b.parentNode.outerHTML);
}
return false;
}
return true;
},
tinfo: function(tab) {
if (tab) {
return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
@ -4372,7 +4280,7 @@
case "sizemodechange":
if (aEvent.target == window) {
this.mCurrentBrowser.setDocShellIsActiveAndForeground(
this.shouldActivateDocShell(this.mCurrentBrowser));
window.windowState != window.STATE_MINIMIZED);
}
break;
}

View File

@ -284,6 +284,7 @@ subsuite = clipboard
skip-if = true # Disabled due to the clipboard not supporting real file types yet (bug 1288773)
[browser_contentAreaClick.js]
skip-if = e10s # Clicks in content don't go through contentAreaClick with e10s.
[browser_contentAltClick.js]
[browser_contextmenu.js]
subsuite = clipboard
tags = fullscreen

View File

@ -0,0 +1,107 @@
/* 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/. */
/**
* Test for Bug 1109146.
* The tests opens a new tab and alt + clicks to download files
* and confirms those files are on the download list.
*
* The difference between this and the test "browser_contentAreaClick.js" is that
* the code path in e10s uses ContentClick.jsm instead of browser.js::contentAreaClick() util.
*/
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
function setup(){
gPrefService.setBoolPref("browser.altClickSave", true);
let testPage =
'data:text/html,' +
'<p><a id="commonlink" href="http://mochi.test/moz/">Common link</a></p>' +
'<p><math id="mathxlink" xmlns="http://www.w3.org/1998/Math/MathML" xlink:type="simple" xlink:href="http://mochi.test/moz/"><mtext>MathML XLink</mtext></math></p>' +
'<p><svg id="svgxlink" xmlns="http://www.w3.org/2000/svg" width="100px" height="50px" version="1.1"><a xlink:type="simple" xlink:href="http://mochi.test/moz/"><text transform="translate(10, 25)">SVG XLink</text></a></svg></p>';
return BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
}
function* clean_up() {
// Remove downloads.
let downloadList = yield Downloads.getList(Downloads.ALL);
let downloads = yield downloadList.getAll();
for (let download of downloads) {
yield downloadList.remove(download);
yield download.finalize(true);
}
// Remove download history.
yield PlacesTestUtils.clearHistory();
gPrefService.clearUserPref("browser.altClickSave");
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
}
add_task(function* test_alt_click()
{
yield setup();
let downloadList = yield Downloads.getList(Downloads.ALL);
let downloads = [];
let downloadView;
// When 1 download has been attempted then resolve the promise.
let finishedAllDownloads = new Promise( (resolve)=> {
downloadView = {
onDownloadAdded: function (aDownload) {
downloads.push(aDownload);
resolve();
},
};
});
yield downloadList.addView(downloadView);
yield BrowserTestUtils.synthesizeMouseAtCenter("#commonlink", {altKey: true}, gBrowser.selectedBrowser);
// Wait for all downloads to be added to the download list.
yield finishedAllDownloads;
yield downloadList.removeView(downloadView);
is(downloads.length, 1, "1 downloads");
is(downloads[0].source.url, "http://mochi.test/moz/", "Downloaded #commonlink element");
yield* clean_up();
});
add_task(function* test_alt_click_on_xlinks()
{
yield setup();
let downloadList = yield Downloads.getList(Downloads.ALL);
let downloads = [];
let downloadView;
// When all 2 downloads have been attempted then resolve the promise.
let finishedAllDownloads = new Promise( (resolve)=> {
downloadView = {
onDownloadAdded: function (aDownload) {
downloads.push(aDownload);
if (downloads.length == 2) {
resolve();
}
},
};
});
yield downloadList.addView(downloadView);
yield BrowserTestUtils.synthesizeMouseAtCenter("#mathxlink", {altKey: true}, gBrowser.selectedBrowser);
yield BrowserTestUtils.synthesizeMouseAtCenter("#svgxlink", {altKey: true}, gBrowser.selectedBrowser);
// Wait for all downloads to be added to the download list.
yield finishedAllDownloads;
yield downloadList.removeView(downloadView);
is(downloads.length, 2, "2 downloads");
is(downloads[0].source.url, "http://mochi.test/moz/", "Downloaded #mathxlink element");
is(downloads[1].source.url, "http://mochi.test/moz/", "Downloaded #svgxlink element");
yield* clean_up();
});

View File

@ -44,6 +44,8 @@ function testPopupUI(win) {
EventUtils.synthesizeKey("t", { accelKey: true }, win);
is(win.gBrowser.browsers.length, 1, "Accel+T doesn't open a new tab in the popup");
is(gBrowser.browsers.length, 2, "Accel+T opened a new tab in the parent window");
gBrowser.removeCurrentTab();
EventUtils.synthesizeKey("w", { accelKey: true }, win);
ok(win.closed, "Accel+W closes the popup");

View File

@ -224,13 +224,20 @@ function openLinkIn(url, where, params) {
var aIndicateErrorPageLoad = params.indicateErrorPageLoad;
if (where == "save") {
if (!aInitiatingDoc) {
Components.utils.reportError("openUILink/openLinkIn was called with " +
"where == 'save' but without initiatingDoc. See bug 814264.");
return;
}
// TODO(1073187): propagate referrerPolicy.
saveURL(url, null, null, true, null, aNoReferrer ? null : aReferrerURI, aInitiatingDoc);
// ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
if ("isContentWindowPrivate" in params) {
saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, null, params.isContentWindowPrivate);
}
else {
if (!aInitiatingDoc) {
Components.utils.reportError("openUILink/openLinkIn was called with " +
"where == 'save' but without initiatingDoc. See bug 814264.");
return;
}
saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, aInitiatingDoc);
}
return;
}

View File

@ -508,20 +508,32 @@ this.DownloadsCommon = {
// or the file doesn't exist), try using the parent if we have it.
let parent = aFile.parent;
if (parent) {
try {
// Open the parent directory to show where the file should be.
parent.launch();
} catch (ex) {
// If launch also fails (probably because it's not implemented), let
// the OS handler try to open the parent.
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService)
.loadUrl(NetUtil.newURI(parent));
}
this.showDirectory(parent);
}
}
},
/**
* Show the specified folder in the system file manager.
*
* @param aDirectory
* a directory to be opened with system file manager.
*/
showDirectory(aDirectory) {
if (!(aDirectory instanceof Ci.nsIFile)) {
throw new Error("aDirectory must be a nsIFile object");
}
try {
aDirectory.launch();
} catch (ex) {
// If launch fails (probably because it's not implemented), let
// the OS handler try to open the directory.
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService)
.loadUrl(NetUtil.newURI(aDirectory));
}
},
/**
* Displays an alert message box which asks the user if they want to
* unblock the downloaded file or not.

View File

@ -19,7 +19,7 @@ richlistitem[type="download"].download-state[state="1"]:not([exists]) .downloadS
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
#downloadsFooter[showingsummary] > #downloadsHistory,
#downloadsFooter[showingsummary] > #downloadsFooterButtons,
#downloadsFooter:not([showingsummary]) > #downloadsSummary {
display: none;
}

View File

@ -367,6 +367,23 @@ const DownloadsPanel = {
this._state = this.kStateHidden;
},
onFooterPopupShowing(aEvent) {
let itemClearList = document.getElementById("downloadsDropdownItemClearList");
if (DownloadsCommon.getData(window).canRemoveFinished) {
itemClearList.removeAttribute("hidden");
} else {
itemClearList.setAttribute("hidden", "true");
}
document.getElementById("downloadsFooterButtonsSplitter").classList
.add("downloadsDropmarkerSplitterExtend");
},
onFooterPopupHidden(aEvent) {
document.getElementById("downloadsFooterButtonsSplitter").classList
.remove("downloadsDropmarkerSplitterExtend");
},
//////////////////////////////////////////////////////////////////////////////
//// Related operations
@ -382,6 +399,13 @@ const DownloadsPanel = {
BrowserDownloadsUI();
},
openDownloadsFolder() {
Downloads.getPreferredDownloadsDirectory().then(downloadsPath => {
DownloadsCommon.showDirectory(new FileUtils.File(downloadsPath));
}).catch(Cu.reportError);
this.hidePanel();
},
//////////////////////////////////////////////////////////////////////////////
//// Internal functions
@ -1188,6 +1212,9 @@ const DownloadsViewController = {
//// nsIController
supportsCommand(aCommand) {
if (aCommand === "downloadsCmd_clearList") {
return true;
}
// Firstly, determine if this is a command that we can handle.
if (!DownloadsViewUI.isCommandName(aCommand)) {
return false;

View File

@ -104,8 +104,8 @@
<menuseparator/>
<menuitem command="downloadsCmd_clearList"
label="&cmd.clearList.label;"
accesskey="&cmd.clearList.accesskey;"/>
label="&cmd.clearList2.label;"
accesskey="&cmd.clearList2.accesskey;"/>
</menupopup>
<panelmultiview id="downloadsPanel-multiView"
@ -149,11 +149,31 @@
crop="end"/>
</vbox>
</hbox>
<button id="downloadsHistory"
class="plain downloadsPanelFooterButton"
label="&downloadsHistory.label;"
accesskey="&downloadsHistory.accesskey;"
oncommand="DownloadsPanel.showDownloadsHistory();"/>
<hbox id="downloadsFooterButtons">
<button id="downloadsHistory"
class="plain downloadsPanelFooterButton"
label="&downloadsHistory.label;"
accesskey="&downloadsHistory.accesskey;"
flex="1"
oncommand="DownloadsPanel.showDownloadsHistory();"/>
<toolbarseparator id="downloadsFooterButtonsSplitter"
class="downloadsDropmarkerSplitter"/>
<button id="downloadsFooterDropmarker"
class="plain downloadsPanelFooterButton downloadsDropmarker"
type="menu">
<menupopup id="downloadSubPanel"
onpopupshowing="DownloadsPanel.onFooterPopupShowing(event);"
onpopuphidden="DownloadsPanel.onFooterPopupHidden(event);"
position="after_end">
<menuitem id="downloadsDropdownItemClearList"
command="downloadsCmd_clearList"
label="&cmd.clearList2.label;"/>
<menuitem id="downloadsDropdownItemOpenDownloadsFolder"
oncommand="DownloadsPanel.openDownloadsFolder();"
label="&openDownloadsFolder.label;"/>
</menupopup>
</button>
</hbox>
</vbox>
</panelview>

View File

@ -10,3 +10,4 @@ skip-if = os == "linux" # Bug 952422
[browser_confirm_unblock_download.js]
[browser_iframe_gone_mid_download.js]
[browser_downloads_panel_block.js]
[browser_downloads_panel_footer.js]

View File

@ -0,0 +1,94 @@
"use strict";
function *task_openDownloadsSubPanel() {
let downloadSubPanel = document.getElementById("downloadSubPanel");
let popupShownPromise = BrowserTestUtils.waitForEvent(downloadSubPanel, "popupshown");
let downloadsDropmarker = document.getElementById("downloadsFooterDropmarker");
EventUtils.synthesizeMouseAtCenter(downloadsDropmarker, {}, window);
yield popupShownPromise;
}
add_task(function* test_openDownloadsFolder() {
yield task_openPanel();
yield task_openDownloadsSubPanel();
yield new Promise(resolve => {
sinon.stub(DownloadsCommon, "showDirectory", file => {
resolve(Downloads.getPreferredDownloadsDirectory().then(downloadsPath => {
is(file.path, downloadsPath, "Check the download folder path.");
}));
});
let itemOpenDownloadsFolder =
document.getElementById("downloadsDropdownItemOpenDownloadsFolder");
EventUtils.synthesizeMouseAtCenter(itemOpenDownloadsFolder, {}, window);
});
yield task_resetState();
});
add_task(function* test_clearList() {
const kTestCases = [{
downloads: [
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
{ state: nsIDM.DOWNLOAD_FINISHED },
{ state: nsIDM.DOWNLOAD_FAILED },
{ state: nsIDM.DOWNLOAD_CANCELED },
],
expectClearListShown: true,
expectedItemNumber: 0,
},{
downloads: [
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
{ state: nsIDM.DOWNLOAD_FINISHED },
{ state: nsIDM.DOWNLOAD_FAILED },
{ state: nsIDM.DOWNLOAD_PAUSED },
{ state: nsIDM.DOWNLOAD_CANCELED },
],
expectClearListShown: true,
expectedItemNumber: 1,
},{
downloads: [
{ state: nsIDM.DOWNLOAD_PAUSED },
],
expectClearListShown: false,
expectedItemNumber: 1,
}];
for (let testCase of kTestCases) {
yield verify_clearList(testCase);
}
});
function *verify_clearList(testCase) {
let downloads = testCase.downloads;
yield task_addDownloads(downloads);
yield task_openPanel();
is(DownloadsView._downloads.length, downloads.length,
"Expect the number of download items");
yield task_openDownloadsSubPanel();
let itemClearList = document.getElementById("downloadsDropdownItemClearList");
let itemNumberPromise = BrowserTestUtils.waitForCondition(() => {
return DownloadsView._downloads.length === testCase.expectedItemNumber;
});
if (testCase.expectClearListShown) {
isnot("true", itemClearList.getAttribute("hidden"),
"Should show Clear Preview Panel button");
EventUtils.synthesizeMouseAtCenter(itemClearList, {}, window);
} else {
is("true", itemClearList.getAttribute("hidden"),
"Should not show Clear Preview Panel button");
}
yield itemNumberPromise;
is(DownloadsView._downloads.length, testCase.expectedItemNumber,
"Download items remained.");
yield task_resetState();
}

View File

@ -24,8 +24,17 @@ const nsIDM = Ci.nsIDownloadManager;
var gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
// Load mocking/stubbing library, sinon
// docs: http://sinonjs.org/docs/
Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
registerCleanupFunction(function () {
gTestTargetFile.remove(false);
delete window.sinon;
delete window.setImmediate;
delete window.clearImmediate;
});
////////////////////////////////////////////////////////////////////////////////

View File

@ -135,12 +135,16 @@ function* runTests(options) {
});
});
yield SpecialPowers.pushPrefEnv({set: [["general.useragent.locale", "es-ES"]]});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
yield SpecialPowers.popPrefEnv();
let node = document.getElementById(pageActionId);
is(node, null, "pageAction image removed from document");
@ -182,6 +186,18 @@ add_task(function* testTabSwitchContext() {
},
},
"_locales/es_ES/messages.json": {
"popup": {
"message": "default.html",
"description": "Popup",
},
"title": {
"message": "T\u00edtulo",
"description": "Title",
},
},
"default.png": imageBuffer,
"1.png": imageBuffer,
"2.png": imageBuffer,
@ -191,16 +207,16 @@ add_task(function* testTabSwitchContext() {
let details = [
{"icon": browser.runtime.getURL("default.png"),
"popup": browser.runtime.getURL("default.html"),
"title": "Default Title \u263a"},
"title": "Default T\u00edtulo \u263a"},
{"icon": browser.runtime.getURL("1.png"),
"popup": browser.runtime.getURL("default.html"),
"title": "Default Title \u263a"},
"title": "Default T\u00edtulo \u263a"},
{"icon": browser.runtime.getURL("2.png"),
"popup": browser.runtime.getURL("2.html"),
"title": "Title 2"},
{"icon": browser.runtime.getURL("2.png"),
"popup": browser.runtime.getURL("2.html"),
"title": "Default Title \u263a"},
"title": "Default T\u00edtulo \u263a"},
];
let promiseTabLoad = details => {

View File

@ -14,12 +14,17 @@ const kAutoMigrateStartedPref = "browser.migrate.automigrate.started";
const kAutoMigrateFinishedPref = "browser.migrate.automigrate.finished";
const kAutoMigrateBrowserPref = "browser.migrate.automigrate.browser";
const kAutoMigrateLastUndoPromptDateMsPref = "browser.migrate.automigrate.lastUndoPromptDateMs";
const kAutoMigrateDaysToOfferUndoPref = "browser.migrate.automigrate.daysToOfferUndo";
const kPasswordManagerTopic = "passwordmgr-storage-changed";
const kPasswordManagerTopicTypes = new Set([
"addLogin",
"modifyLogin",
]);
const kNotificationId = "abouthome-automigration-undo";
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
@ -249,6 +254,100 @@ const AutoMigrate = {
Services.prefs.clearUserPref(kAutoMigrateStartedPref);
Services.prefs.clearUserPref(kAutoMigrateFinishedPref);
Services.prefs.clearUserPref(kAutoMigrateBrowserPref);
let browserWindows = Services.wm.getEnumerator("navigator:browser");
while (browserWindows.hasMoreElements()) {
let win = browserWindows.getNext();
if (!win.closed) {
for (let browser of win.gBrowser.browsers) {
let nb = win.gBrowser.getNotificationBox(browser);
let notification = nb.getNotificationWithValue(kNotificationId);
if (notification) {
nb.removeNotification(notification);
}
}
}
}
},
getBrowserUsedForMigration() {
let browserId = Services.prefs.getCharPref(kAutoMigrateBrowserPref);
if (browserId) {
return MigrationUtils.getBrowserName(browserId);
}
return null;
},
maybeShowUndoNotification(target) {
this.canUndo().then(canUndo => {
// The tab might have navigated since we requested the undo state:
if (!canUndo || target.currentURI.spec != "about:home") {
return;
}
let win = target.ownerGlobal;
let notificationBox = win.gBrowser.getNotificationBox(target);
if (!notificationBox || notificationBox.getNotificationWithValue("abouthome-automigration-undo")) {
return;
}
// At this stage we're committed to show the prompt - unless we shouldn't,
// in which case we remove the undo prefs (which will cause canUndo() to
// return false from now on.):
if (!this.shouldStillShowUndoPrompt()) {
this.removeUndoOption();
return;
}
let browserName = this.getBrowserUsedForMigration();
let message;
if (browserName) {
message = MigrationUtils.getLocalizedString("automigration.undo.message",
[browserName]);
} else {
message = MigrationUtils.getLocalizedString("automigration.undo.unknownBrowserMessage");
}
let buttons = [
{
label: MigrationUtils.getLocalizedString("automigration.undo.keep.label"),
accessKey: MigrationUtils.getLocalizedString("automigration.undo.keep.accesskey"),
callback: () => {
this.removeUndoOption();
},
},
{
label: MigrationUtils.getLocalizedString("automigration.undo.dontkeep.label"),
accessKey: MigrationUtils.getLocalizedString("automigration.undo.dontkeep.accesskey"),
callback: () => {
this.undo();
},
},
];
notificationBox.appendNotification(
message, kNotificationId, null, notificationBox.PRIORITY_INFO_HIGH, buttons
);
});
},
shouldStillShowUndoPrompt() {
let today = new Date();
// Round down to midnight:
today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
// We store the unix timestamp corresponding to midnight on the last day
// on which we prompted. Fetch that and compare it to today's date.
// (NB: stored as a string because int prefs are too small for unix
// timestamps.)
let previousPromptDateMsStr = Preferences.get(kAutoMigrateLastUndoPromptDateMsPref, "0");
let previousPromptDate = new Date(parseInt(previousPromptDateMsStr, 10));
if (previousPromptDate < today) {
let remainingDays = Preferences.get(kAutoMigrateDaysToOfferUndoPref, 4) - 1;
Preferences.set(kAutoMigrateDaysToOfferUndoPref, remainingDays);
Preferences.set(kAutoMigrateLastUndoPromptDateMsPref, today.valueOf().toString());
if (remainingDays <= 0) {
return false;
}
}
return true;
},
QueryInterface: XPCOMUtils.generateQI(

View File

@ -10,19 +10,21 @@ const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
"resource://gre/modules/BookmarkHTMLUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AutoMigrate",
"resource:///modules/AutoMigrate.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
"resource://gre/modules/BookmarkHTMLUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
"resource://gre/modules/TelemetryStopwatch.jsm");
var gMigrators = null;
var gProfileStartup = null;
@ -197,6 +199,10 @@ this.MigratorPrototype = {
return types.reduce((a, b) => a |= b, 0);
},
getKey: function MP_getKey() {
return this.contractID.match(/\=([^\=]+)$/)[1];
},
/**
* DO NOT OVERRIDE - After deCOMing migration, the UI will just call
* migrate for each resource.
@ -218,6 +224,31 @@ this.MigratorPrototype = {
});
};
let getHistogramForResourceType = resourceType => {
if (resourceType == MigrationUtils.resourceTypes.HISTORY) {
return "FX_MIGRATION_HISTORY_IMPORT_MS";
}
if (resourceType == MigrationUtils.resourceTypes.BOOKMARKS) {
return "FX_MIGRATION_BOOKMARKS_IMPORT_MS";
}
if (resourceType == MigrationUtils.resourceTypes.PASSWORDS) {
return "FX_MIGRATION_LOGINS_IMPORT_MS";
}
return null;
};
let maybeStartTelemetryStopwatch = (resourceType, resource) => {
let histogram = getHistogramForResourceType(resourceType);
if (histogram) {
TelemetryStopwatch.startKeyed(histogram, this.getKey(), resource);
}
};
let maybeStopTelemetryStopwatch = (resourceType, resource) => {
let histogram = getHistogramForResourceType(resourceType);
if (histogram) {
TelemetryStopwatch.finishKeyed(histogram, this.getKey(), resource);
}
};
// Called either directly or through the bookmarks import callback.
let doMigrate = Task.async(function*() {
let resourcesGroupedByItems = new Map();
@ -246,8 +277,10 @@ this.MigratorPrototype = {
for (let res of itemResources) {
// Workaround bug 449811.
let resource = res;
maybeStartTelemetryStopwatch(migrationType, resource);
let completeDeferred = PromiseUtils.defer();
let resourceDone = function(aSuccess) {
maybeStopTelemetryStopwatch(migrationType, resource);
itemResources.delete(resource);
itemSuccess |= aSuccess;
if (itemResources.size == 0) {

View File

@ -141,6 +141,13 @@
@RESPATH@/run-mozilla.sh
#endif
#endif
#ifdef XP_WIN
#ifdef _AMD64_
@BINPATH@/@DLL_PREFIX@qipcap64@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@qipcap@DLL_SUFFIX@
#endif
#endif
; [Components]
#ifdef MOZ_ARTIFACT_BUILDS

View File

@ -62,8 +62,8 @@
<!ENTITY cmd.copyDownloadLink.accesskey "L">
<!ENTITY cmd.removeFromHistory.label "Remove From History">
<!ENTITY cmd.removeFromHistory.accesskey "e">
<!ENTITY cmd.clearList.label "Clear List">
<!ENTITY cmd.clearList.accesskey "a">
<!ENTITY cmd.clearList2.label "Clear Preview Panel">
<!ENTITY cmd.clearList2.accesskey "a">
<!ENTITY cmd.clearDownloads.label "Clear Downloads">
<!ENTITY cmd.clearDownloads.accesskey "D">
<!-- LOCALIZATION NOTE (cmd.unblock2.label):
@ -109,6 +109,8 @@
<!ENTITY downloadsHistory.label "Show All Downloads">
<!ENTITY downloadsHistory.accesskey "S">
<!ENTITY openDownloadsFolder.label "Open Downloads Folder">
<!ENTITY clearDownloadsButton.label "Clear Downloads">
<!ENTITY clearDownloadsButton.tooltip "Clears completed, canceled and failed downloads">

View File

@ -70,3 +70,11 @@ importedEdgeReadingList=Reading List (From Edge)
64_360se=Other Data
128_firefox=Windows and Tabs
# Automigration undo notification.
automigration.undo.message = We automatically imported your data from %S. Would you like to keep it?
automigration.undo.unknownBrowserMessage = We automatically imported your data from another browser. Would you like to keep it?
automigration.undo.keep.label = Keep
automigration.undo.keep.accesskey = K
automigration.undo.dontkeep.label = Dont Keep
automigration.undo.dontkeep.accesskey = D

View File

@ -15,10 +15,12 @@ Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AutoMigrate",
"resource:///modules/AutoMigrate.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
@ -99,6 +101,7 @@ var AboutHome = {
"AboutHome:Sync",
"AboutHome:Settings",
"AboutHome:RequestUpdate",
"AboutHome:MaybeShowAutoMigrationUndoNotification",
],
init: function() {
@ -148,6 +151,10 @@ var AboutHome = {
case "AboutHome:RequestUpdate":
this.sendAboutHomeData(aMessage.target);
break;
case "AboutHome:MaybeShowAutoMigrationUndoNotification":
AutoMigrate.maybeShowUndoNotification(aMessage.target);
break;
}
},
@ -182,5 +189,6 @@ var AboutHome = {
}).then(null, function onError(x) {
Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
});
}
},
};

View File

@ -81,7 +81,8 @@ var ContentClick = {
referrerURI: browser.documentURI,
referrerPolicy: json.referrerPolicy,
noReferrer: json.noReferrer,
allowMixedContent: json.allowMixedContent };
allowMixedContent: json.allowMixedContent,
isContentWindowPrivate: json.isContentWindowPrivate};
// The new tab/window must use the same userContextId.
if (json.originAttributes.userContextId) {

View File

@ -397,7 +397,7 @@ description#identity-popup-content-verifier,
.identity-popup-permission-state-label {
margin-inline-end: 5px;
text-align: end;
opacity: 0.6;
color: graytext;
}
.identity-popup-permission-remove-button {
@ -421,21 +421,22 @@ description#identity-popup-content-verifier,
height: 16px;
list-style-image: url(chrome://browser/skin/panel-icons.svg#cancel);
filter: url(chrome://browser/skin/filters.svg#fill);
fill: #999;
fill: graytext;
}
.identity-popup-permission-remove-button > .button-box > .button-text {
display: none;
}
/* swap foreground / background colors on hover */
.identity-popup-permission-remove-button:hover {
background-color: #999;
background-color: graytext;
}
.identity-popup-permission-remove-button:hover > .button-box > .button-icon {
fill: #fff;
fill: -moz-field;
}
.identity-popup-permission-remove-button:hover:active {
background-color: #808080;
background-color: -moz-fieldtext;
}

View File

@ -30,7 +30,8 @@
}
#emptyDownloads {
padding: 10px 20px;
padding: 16px 25px;
margin: 0;
/* The panel can be wider than this description after the blocked subview is
shown, so center the text. */
text-align: center;
@ -41,7 +42,7 @@
border-top: 1px solid var(--panel-separator-color);
}
.downloadsPanelFooter > toolbarseparator {
.downloadsPanelFooter toolbarseparator {
margin: 0;
border: 0;
min-width: 0;
@ -55,6 +56,7 @@
color: inherit;
margin: 0;
padding: 0;
min-width: 0;
min-height: 40px;
}
@ -63,7 +65,8 @@
background-color: hsla(210,4%,10%,.07);
}
.downloadsPanelFooterButton:hover:active {
.downloadsPanelFooterButton:hover:active,
.downloadsPanelFooterButton[open="true"] {
outline: 1px solid hsla(210,4%,10%,.12);
background-color: hsla(210,4%,10%,.12);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
@ -82,6 +85,47 @@
background-color: #0568ba;
}
#downloadsPanel[hasdownloads] #downloadsHistory {
padding-left: 58px !important;
}
toolbarseparator.downloadsDropmarkerSplitter {
margin: 7px 0;
}
#downloadsFooter:hover toolbarseparator.downloadsDropmarkerSplitter,
#downloadsFooter toolbarseparator.downloadsDropmarkerSplitterExtend {
margin: 0;
}
.downloadsDropmarker {
padding: 0 19px !important;
}
.downloadsDropmarker > .button-box > hbox {
display: none;
}
.downloadsDropmarker > .button-box > .button-menu-dropmarker {
/* This is to override the linux !important */
-moz-appearance: none !important;
display: -moz-box;
}
.downloadsDropmarker > .button-box > .button-menu-dropmarker > .dropmarker-icon {
width: 16px;
height: 16px;
list-style-image: url("chrome://browser/skin/downloads/menubutton-dropmarker.svg");
filter: url("chrome://browser/skin/filters.svg#fill");
fill: currentColor;
}
/* Override default icon size which is too small for this dropdown */
.downloadsDropmarker > .button-box > .button-menu-dropmarker {
width: 16px;
height: 16px;
}
#downloadsSummary {
padding: 0 12px;
cursor: pointer;

View File

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg"
width="16" height="16" viewBox="0 0 16 16">
<path d="m 2,6 6,6 6,-6 -1.5,-1.5 -4.5,4.5 -4.5,-4.5 z" />
</svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@ -54,6 +54,7 @@
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
skin/classic/browser/downloads/contentAreaDownloadsView.css (../shared/downloads/contentAreaDownloadsView.css)
skin/classic/browser/downloads/download-blocked.svg (../shared/downloads/download-blocked.svg)
skin/classic/browser/downloads/menubutton-dropmarker.svg (../shared/downloads/menubutton-dropmarker.svg)
skin/classic/browser/drm-icon.svg (../shared/drm-icon.svg)
skin/classic/browser/filters.svg (../shared/filters.svg)
skin/classic/browser/fullscreen/insecure.svg (../shared/fullscreen/insecure.svg)

View File

@ -50,6 +50,12 @@
--urlbar-separator-color: ThreeDLightShadow;
}
@media (-moz-windows-default-theme) {
:root {
--panel-separator-color: hsla(210,4%,10%,.14);
}
}
#nav-bar[brighttext] {
--toolbarbutton-hover-background: rgba(255,255,255,.25);
--toolbarbutton-hover-bordercolor: rgba(255,255,255,.5);

View File

@ -199,7 +199,6 @@ def old_configure_options(*options):
'--enable-memory-sanitizer',
'--enable-mobile-optimize',
'--enable-mozril-geoloc',
'--enable-necko-protocols',
'--enable-necko-wifi',
'--enable-negotiateauth',
'--enable-nfc',

View File

@ -25,7 +25,7 @@ define(function (require, exports, module) {
},
render: function () {
// Use `Object.assign` to keep `this.props` without changes becuase:
// Use `Object.assign` to keep `this.props` without changes because:
// 1. JSON.stringify/JSON.parse is slow.
// 2. Immutable.js is planned for the future.
let props = Object.assign({}, this.props);
@ -35,8 +35,32 @@ define(function (require, exports, module) {
delete props.object.preview.properties;
props.object.ownPropertyLength =
Object.keys(props.object.preview.ownProperties).length;
switch (props.object.class) {
case "MouseEvent":
props.isInterestingProp = (type, value, name) => {
return (name == "clientX" ||
name == "clientY" ||
name == "layerX" ||
name == "layerY");
};
break;
case "KeyboardEvent":
props.isInterestingProp = (type, value, name) => {
return (name == "key" ||
name == "charCode" ||
name == "keyCode");
};
break;
case "MessageEvent":
props.isInterestingProp = (type, value, name) => {
return (name == "isTrusted" ||
name == "data");
};
break;
}
return rep(props);
},
}
});
// Registration

View File

@ -27,6 +27,7 @@ define(function (require, exports, module) {
propTypes: {
object: React.PropTypes.object.isRequired,
mode: React.PropTypes.string,
isInterestingProp: React.PropTypes.func
},
getTitle: function (object) {
@ -50,21 +51,21 @@ define(function (require, exports, module) {
propIterator: function (object, max) {
// Property filter. Show only interesting properties to the user.
let isInterestingProp = (type, value) => {
let isInterestingProp = this.props.isInterestingProp || ((type, value) => {
return (
type == "boolean" ||
type == "number" ||
(type == "string" && value.length != 0)
);
};
});
let ownProperties = object.preview ? object.preview.ownProperties : [];
let indexes = this.getPropIndexes(ownProperties, max, isInterestingProp);
if (indexes.length < max && indexes.length < object.ownPropertyLength) {
// There are not enough props yet. Then add uninteresting props to display them.
indexes = indexes.concat(
this.getPropIndexes(ownProperties, max - indexes.length, (t, value) => {
return !isInterestingProp(t, value);
this.getPropIndexes(ownProperties, max - indexes.length, (t, value, name) => {
return !isInterestingProp(t, value, name);
})
);
}
@ -152,7 +153,7 @@ define(function (require, exports, module) {
let type = (value.class || typeof value);
type = type.toLowerCase();
if (filter(type, value)) {
if (filter(type, value, name)) {
indexes.push(i);
}
i++;

View File

@ -43,7 +43,7 @@ window.onload = Task.async(function* () {
function testMouseEvent() {
const renderedComponent = renderComponent(Event.rep, { object: getGripStub("testMouseEvent") });
is(renderedComponent.textContent,
"MouseEvent { buttons: 0, clientX: 62, clientY: 18, 2 more… }",
"MouseEvent { clientX: 62, clientY: 18, layerX: 0, 2 more… }",
"Event rep has expected text content for a mouse event");
}

View File

@ -18,129 +18,11 @@ const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
// We prefix all our local storage items with this.
const PREFIX = "Services.prefs:";
/**
* Create a new preference object.
*
* @param {PrefBranch} branch the branch holding this preference
* @param {String} name the base name of this preference
* @param {String} fullName the fully-qualified name of this preference
*/
function Preference(branch, name, fullName) {
this.branch = branch;
this.name = name;
this.fullName = fullName;
this.defaultValue = null;
this.hasUserValue = false;
this.userValue = null;
this.type = null;
}
Preference.prototype = {
/**
* Return this preference's current value.
*
* @return {Any} The current value of this preference. This may
* return a string, a number, or a boolean depending on the
* preference's type.
*/
get: function () {
if (this.hasUserValue) {
return this.userValue;
}
return this.defaultValue;
},
/**
* Set the preference's value. The new value is assumed to be a
* user value. After setting the value, this function emits a
* change notification.
*
* @param {Any} value the new value
*/
set: function (value) {
if (!this.hasUserValue || value !== this.userValue) {
this.userValue = value;
this.hasUserValue = true;
this.saveAndNotify();
}
},
/**
* Set the default value for this preference, and emit a
* notification if this results in a visible change.
*
* @param {Any} value the new default value
*/
setDefault: function (value) {
if (this.defaultValue !== value) {
this.defaultValue = value;
if (!this.hasUserValue) {
this.saveAndNotify();
}
}
},
/**
* If this preference has a user value, clear it. If a change was
* made, emit a change notification.
*/
clearUserValue: function () {
if (this.hasUserValue) {
this.userValue = null;
this.hasUserValue = false;
this.saveAndNotify();
}
},
/**
* Helper function to write the preference's value to local storage
* and then emit a change notification.
*/
saveAndNotify: function () {
let store = {
type: this.type,
defaultValue: this.defaultValue,
hasUserValue: this.hasUserValue,
userValue: this.userValue,
};
localStorage.setItem(PREFIX + this.fullName, JSON.stringify(store));
this.branch._notify(this.name);
},
/**
* Change this preference's value without writing it back to local
* storage. This is used to handle changes to local storage that
* were made externally.
*
* @param {Number} type one of the PREF_* values
* @param {Any} userValue the user value to use if the pref does not exist
* @param {Any} defaultValue the default value to use if the pref
* does not exist
* @param {Boolean} hasUserValue if a new pref is created, whether
* the default value is also a user value
* @param {Object} store the new value of the preference. It should
* be of the form {type, defaultValue, hasUserValue, userValue};
* where |type| is one of the PREF_* type constants; |defaultValue|
* and |userValue| are the default and user values, respectively;
* and |hasUserValue| is a boolean indicating whether the user value
* is valid
*/
storageUpdated: function (type, userValue, hasUserValue, defaultValue) {
this.type = type;
this.defaultValue = defaultValue;
this.hasUserValue = hasUserValue;
this.userValue = userValue;
// There's no need to write this back to local storage, since it
// came from there; and this avoids infinite event loops.
this.branch._notify(this.name);
},
};
/**
* Create a new preference branch. This object conforms largely to
* nsIPrefBranch and nsIPrefService, though it only implements the
* subset needed by devtools.
* subset needed by devtools. A preference branch can hold child
* preferences while also holding a preference value itself.
*
* @param {PrefBranch} parent the parent branch, or null for the root
* branch.
@ -154,6 +36,12 @@ function PrefBranch(parent, name, fullName) {
this._observers = {};
this._children = {};
// Properties used when this branch has a value as well.
this._defaultValue = null;
this._hasUserValue = false;
this._userValue = null;
this._type = PREF_INVALID;
if (!parent) {
this._initializeRoot();
}
@ -172,16 +60,16 @@ PrefBranch.prototype = {
/** @see nsIPrefBranch.getPrefType. */
getPrefType: function (prefName) {
return this._findPref(prefName).type;
return this._findPref(prefName)._type;
},
/** @see nsIPrefBranch.getBoolPref. */
getBoolPref: function (prefName) {
let thePref = this._findPref(prefName);
if (thePref.type !== PREF_BOOL) {
if (thePref._type !== PREF_BOOL) {
throw new Error(`${prefName} does not have bool type`);
}
return thePref.get();
return thePref._get();
},
/** @see nsIPrefBranch.setBoolPref. */
@ -190,19 +78,19 @@ PrefBranch.prototype = {
throw new Error("non-bool passed to setBoolPref");
}
let thePref = this._findOrCreatePref(prefName, value, true, value);
if (thePref.type !== PREF_BOOL) {
if (thePref._type !== PREF_BOOL) {
throw new Error(`${prefName} does not have bool type`);
}
thePref.set(value);
thePref._set(value);
},
/** @see nsIPrefBranch.getCharPref. */
getCharPref: function (prefName) {
let thePref = this._findPref(prefName);
if (thePref.type !== PREF_STRING) {
if (thePref._type !== PREF_STRING) {
throw new Error(`${prefName} does not have string type`);
}
return thePref.get();
return thePref._get();
},
/** @see nsIPrefBranch.setCharPref. */
@ -211,19 +99,19 @@ PrefBranch.prototype = {
throw new Error("non-string passed to setCharPref");
}
let thePref = this._findOrCreatePref(prefName, value, true, value);
if (thePref.type !== PREF_STRING) {
if (thePref._type !== PREF_STRING) {
throw new Error(`${prefName} does not have string type`);
}
thePref.set(value);
thePref._set(value);
},
/** @see nsIPrefBranch.getIntPref. */
getIntPref: function (prefName) {
let thePref = this._findPref(prefName);
if (thePref.type !== PREF_INT) {
if (thePref._type !== PREF_INT) {
throw new Error(`${prefName} does not have int type`);
}
return thePref.get();
return thePref._get();
},
/** @see nsIPrefBranch.setIntPref. */
@ -232,22 +120,22 @@ PrefBranch.prototype = {
throw new Error("non-number passed to setIntPref");
}
let thePref = this._findOrCreatePref(prefName, value, true, value);
if (thePref.type !== PREF_INT) {
if (thePref._type !== PREF_INT) {
throw new Error(`${prefName} does not have int type`);
}
thePref.set(value);
thePref._set(value);
},
/** @see nsIPrefBranch.clearUserPref */
clearUserPref: function (prefName) {
let thePref = this._findPref(prefName);
thePref.clearUserValue();
thePref._clearUserValue();
},
/** @see nsIPrefBranch.prefHasUserValue */
prefHasUserValue: function (prefName) {
let thePref = this._findPref(prefName);
return thePref.hasUserValue;
return thePref._hasUserValue;
},
/** @see nsIPrefBranch.addObserver */
@ -294,6 +182,106 @@ PrefBranch.prototype = {
return this._findPref(prefRoot);
},
/**
* Return this preference's current value.
*
* @return {Any} The current value of this preference. This may
* return a string, a number, or a boolean depending on the
* preference's type.
*/
_get: function () {
if (this._hasUserValue) {
return this._userValue;
}
return this._defaultValue;
},
/**
* Set the preference's value. The new value is assumed to be a
* user value. After setting the value, this function emits a
* change notification.
*
* @param {Any} value the new value
*/
_set: function (value) {
if (!this._hasUserValue || value !== this._userValue) {
this._userValue = value;
this._hasUserValue = true;
this._saveAndNotify();
}
},
/**
* Set the default value for this preference, and emit a
* notification if this results in a visible change.
*
* @param {Any} value the new default value
*/
_setDefault: function (value) {
if (this._defaultValue !== value) {
this._defaultValue = value;
if (!this._hasUserValue) {
this._saveAndNotify();
}
}
},
/**
* If this preference has a user value, clear it. If a change was
* made, emit a change notification.
*/
_clearUserValue: function () {
if (this._hasUserValue) {
this._userValue = null;
this._hasUserValue = false;
this._saveAndNotify();
}
},
/**
* Helper function to write the preference's value to local storage
* and then emit a change notification.
*/
_saveAndNotify: function () {
let store = {
type: this._type,
defaultValue: this._defaultValue,
hasUserValue: this._hasUserValue,
userValue: this._userValue,
};
localStorage.setItem(PREFIX + this.fullName, JSON.stringify(store));
this._parent._notify(this._name);
},
/**
* Change this preference's value without writing it back to local
* storage. This is used to handle changes to local storage that
* were made externally.
*
* @param {Number} type one of the PREF_* values
* @param {Any} userValue the user value to use if the pref does not exist
* @param {Any} defaultValue the default value to use if the pref
* does not exist
* @param {Boolean} hasUserValue if a new pref is created, whether
* the default value is also a user value
* @param {Object} store the new value of the preference. It should
* be of the form {type, defaultValue, hasUserValue, userValue};
* where |type| is one of the PREF_* type constants; |defaultValue|
* and |userValue| are the default and user values, respectively;
* and |hasUserValue| is a boolean indicating whether the user value
* is valid
*/
_storageUpdated: function (type, userValue, hasUserValue, defaultValue) {
this._type = type;
this._defaultValue = defaultValue;
this._hasUserValue = hasUserValue;
this._userValue = userValue;
// There's no need to write this back to local storage, since it
// came from there; and this avoids infinite event loops.
this._parent._notify(this._name);
},
/**
* Helper function to find either a Preference or PrefBranch object
* given its name. If the name is not found, throws an exception.
@ -378,36 +366,34 @@ PrefBranch.prototype = {
* the default value is also a user value
*/
_findOrCreatePref: function (keyName, userValue, hasUserValue, defaultValue) {
let branchName = keyName.split(".");
let prefName = branchName.pop();
let branch = this._createBranch(keyName.split("."));
let branch = this._createBranch(branchName);
if (!(prefName in branch._children)) {
if (hasUserValue && typeof (userValue) !== typeof (defaultValue)) {
throw new Error("inconsistent values when creating " + keyName);
}
let type;
switch (typeof (defaultValue)) {
case "boolean":
type = PREF_BOOL;
break;
case "number":
type = PREF_INT;
break;
case "string":
type = PREF_STRING;
break;
default:
throw new Error("unhandled argument type: " + typeof (defaultValue));
}
let thePref = new Preference(branch, prefName, keyName);
thePref.storageUpdated(type, userValue, hasUserValue, defaultValue);
branch._children[prefName] = thePref;
if (hasUserValue && typeof (userValue) !== typeof (defaultValue)) {
throw new Error("inconsistent values when creating " + keyName);
}
return branch._children[prefName];
let type;
switch (typeof (defaultValue)) {
case "boolean":
type = PREF_BOOL;
break;
case "number":
type = PREF_INT;
break;
case "string":
type = PREF_STRING;
break;
default:
throw new Error("unhandled argument type: " + typeof (defaultValue));
}
if (branch._type === PREF_INVALID) {
branch._storageUpdated(type, userValue, hasUserValue, defaultValue);
} else if (branch._type !== type) {
throw new Error("attempt to change type of pref " + keyName);
}
return branch;
},
/**
@ -432,7 +418,7 @@ PrefBranch.prototype = {
this._findOrCreatePref(event.key, userValue, hasUserValue, defaultValue);
} else {
let thePref = this._findPref(event.key);
thePref.storageUpdated(type, userValue, hasUserValue, defaultValue);
thePref._storageUpdated(type, userValue, hasUserValue, defaultValue);
}
},
@ -592,7 +578,7 @@ const Services = {
*/
function pref(name, value) {
let thePref = Services.prefs._findOrCreatePref(name, value, true, value);
thePref.setDefault(value);
thePref._setDefault(value);
}
module.exports = Services;

View File

@ -218,6 +218,9 @@ function do_tests() {
"someotherstring": true
}, "pref worked");
// Regression test for bug 1296427.
pref("devtools.hud.loglimit", 1000);
pref("devtools.hud.loglimit.network", 1000);
// Clean up.
localStorage.clear();

View File

@ -395,6 +395,10 @@ html, body, #app, #memory-tool {
padding-inline-end: 5px;
}
.children-pointer:dir(rtl) {
transform: scaleX(-1);
}
/**
* Heap tree view columns
*/

View File

@ -27,7 +27,6 @@ function VariablesViewLink(props) {
onClick: openVariablesView.bind(null, object),
className: "cm-variable",
draggable: false,
href: "#"
}, children)
);
}

View File

@ -44,7 +44,10 @@ const chromeRDPEnums = {
// Undocumented in Chrome RDP, but is used for evaluation results.
RESULT: "result",
// Undocumented in Chrome RDP, but is used for input.
COMMAND: "command"
COMMAND: "command",
// Undocumented in Chrome RDP, but is used for messages that should not
// output anything (e.g. `console.time()` calls).
NULL_MESSAGE: "nullMessage",
},
MESSAGE_LEVEL: {
LOG: "log",

View File

@ -21,7 +21,11 @@ function messages(state = new MessageState(), action) {
case constants.MESSAGE_ADD:
let newMessage = action.message;
if (newMessage.type === "clear") {
if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) {
return state;
}
if (newMessage.type === constants.MESSAGE_TYPE.CLEAR) {
return state.set("messagesById", Immutable.List([newMessage]));
}

View File

@ -46,6 +46,27 @@ describe("ConsoleAPICall component:", () => {
expect(messageBody.textContent).toBe(message.messageText);
});
});
describe("console.time", () => {
it("does not show anything", () => {
const message = stubConsoleMessages.get("console.time('bar')");
const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
const messageBody = getMessageBody(rendered);
expect(messageBody.textContent).toBe("");
});
});
describe("console.timeEnd", () => {
it("renders as expected", () => {
const message = stubConsoleMessages.get("console.timeEnd('bar')");
const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
const messageBody = getMessageBody(rendered);
expect(messageBody.textContent).toBe(message.messageText);
expect(messageBody.textContent).toMatch(/^bar: \d+(\.\d+)?ms$/);
});
});
});
function getMessageBody(rendered) {
@ -56,4 +77,4 @@ function getMessageBody(rendered) {
function getRepeatNode(rendered) {
const repeatPath = "span > span.message-flex-body > span.message-body.devtools-monospace + span.message-repeats";
return rendered.querySelectorAll(repeatPath);
}
}

View File

@ -12,11 +12,10 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-cons
let stubs = [];
snippets.forEach((code, key) => {
add_task(function* () {
let tempFilePath = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
add_task(function* () {
let tempFilePath = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
for (var [key, {keys, code}] of snippets) {
OS.File.writeAtomic(tempFilePath, `function triggerPacket() {${code}}`);
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
let {ui} = hud;
@ -24,17 +23,23 @@ snippets.forEach((code, key) => {
ok(ui.jsterm, "jsterm exists");
ok(ui.newConsoleOutput, "newConsoleOutput exists");
toolbox.target.client.addListener("consoleAPICall", (type, res) => {
stubs.push(formatStub(key, res));
if (stubs.length == snippets.size) {
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
OS.File.writeAtomic(tempFilePath, "");
}
let received = new Promise(resolve => {
let i = 0;
toolbox.target.client.addListener("consoleAPICall", (type, res) => {
stubs.push(formatStub(keys[i], res));
if(++i === keys.length ){
resolve();
}
});
});
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.triggerPacket();
});
});
yield received;
}
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
OS.File.writeAtomic(tempFilePath, "");
});

View File

@ -15,10 +15,12 @@ const consoleApiCommands = [
"console.count('bar')",
];
let consoleApi = new Map(consoleApiCommands.map(cmd => [cmd, cmd]));
let consoleApi = new Map(consoleApiCommands.map(
cmd => [cmd, {keys: [cmd], code: cmd}]));
consoleApi.set("console.trace()",
`
consoleApi.set("console.trace()", {
keys: ["console.trace()"],
code: `
function bar() {
console.trace()
}
@ -27,13 +29,14 @@ function foo() {
}
foo()
`);
`});
consoleApi.set("console.time()",
`
console.time()
console.timeEnd()
`);
consoleApi.set("console.time('bar')", {
keys: ["console.time('bar')", "console.timeEnd('bar')"],
code: `
console.time("bar");
console.timeEnd("bar");
`});
// Evaluation Result

View File

@ -196,5 +196,41 @@ stubConsoleMessages.set("console.trace()", new ConsoleMessage({
}
}));
stubConsoleMessages.set("console.time('bar')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "nullMessage",
"level": "log",
"messageText": null,
"parameters": null,
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"nullMessage\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":2,\"column\":1}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 2,
"column": 1
}
}));
stubConsoleMessages.set("console.timeEnd('bar')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "timeEnd",
"level": "log",
"messageText": "bar: 3.87ms",
"parameters": null,
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 3.87ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":3,\"column\":1}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 3,
"column": 1
}
}));
module.exports = stubConsoleMessages

View File

@ -96,4 +96,14 @@ describe("Message reducer:", () => {
expect(messages.first().parameters[0]).toBe(`message num 2`);
expect(messages.last().parameters[0]).toBe(`message num ${logLimit + 1}`);
});
it("does not add null messages to the store", () => {
const { dispatch, getState } = setupStore([]);
const message = stubConsoleMessages.get("console.time('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
expect(messages.size).toBe(0);
});
});

View File

@ -45,6 +45,7 @@ function transformPacket(packet) {
let type = message.level;
let level = getLevelFromType(type);
let messageText = null;
const timer = message.timer;
// Special per-type conversion.
switch (type) {
@ -60,6 +61,23 @@ function transformPacket(packet) {
messageText = `${label}: ${counter.count}`;
parameters = null;
break;
case "time":
// We don't show anything for console.time calls to match Chrome's behaviour.
parameters = null;
type = MESSAGE_TYPE.NULL_MESSAGE;
break;
case "timeEnd":
parameters = null;
if (timer) {
// We show the duration to users when calls console.timeEnd() is called,
// if corresponding console.time() was called before.
let duration = Math.round(timer.duration * 100) / 100;
messageText = l10n.getFormatStr("timeEnd", [timer.name, duration]);
} else {
// If the `timer` property does not exists, we don't output anything.
type = MESSAGE_TYPE.NULL_MESSAGE;
}
break;
}
const frame = {

View File

@ -133,7 +133,9 @@ StructuredCloneCallbacksError(JSContext* aCx,
NS_WARNING("Failed to clone data.");
}
const JSStructuredCloneCallbacks gCallbacks = {
} // anonymous namespace
const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = {
StructuredCloneCallbacksRead,
StructuredCloneCallbacksWrite,
StructuredCloneCallbacksError,
@ -142,8 +144,6 @@ const JSStructuredCloneCallbacks gCallbacks = {
StructuredCloneCallbacksFreeTransfer
};
} // anonymous namespace
// StructuredCloneHolderBase class
StructuredCloneHolderBase::StructuredCloneHolderBase(StructuredCloneScope aScope)
@ -185,9 +185,9 @@ StructuredCloneHolderBase::Write(JSContext* aCx,
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
mBuffer = new JSAutoStructuredCloneBuffer(mStructuredCloneScope, &gCallbacks, this);
mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
if (!mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this)) {
if (!mBuffer->write(aCx, aValue, aTransfer, &StructuredCloneHolder::sCallbacks, this)) {
mBuffer = nullptr;
return false;
}
@ -202,7 +202,7 @@ StructuredCloneHolderBase::Read(JSContext* aCx,
MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
bool ok = mBuffer->read(aCx, aValue, &StructuredCloneHolder::sCallbacks, this);
return ok;
}
@ -311,20 +311,18 @@ StructuredCloneHolder::Read(nsISupports* aParent,
void
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
JS::MutableHandle<JS::Value> aValue,
ErrorResult& aRv)
{
ReadFromBuffer(aParent, aCx, aBuffer, aBufferLength,
ReadFromBuffer(aParent, aCx, aBuffer,
JS_STRUCTURED_CLONE_VERSION, aValue, aRv);
}
void
StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
uint32_t aAlgorithmVersion,
JS::MutableHandle<JS::Value> aValue,
ErrorResult& aRv)
@ -333,53 +331,18 @@ StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent,
mCreationThread == NS_GetCurrentThread());
MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
MOZ_ASSERT(aBuffer);
mozilla::AutoRestore<nsISupports*> guard(mParent);
mParent = aParent;
if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion,
mStructuredCloneScope, aValue, &gCallbacks,
if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
mStructuredCloneScope, aValue, &sCallbacks,
this)) {
JS_ClearPendingException(aCx);
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
}
void
StructuredCloneHolder::MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
ErrorResult& aRv)
{
MOZ_ASSERT_IF(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
mCreationThread == NS_GetCurrentThread());
MOZ_ASSERT(mBuffer, "MoveBuffer() cannot be called without a Write().");
if (NS_WARN_IF(!aArray.SetLength(BufferSize(), mozilla::fallible))) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
uint64_t* buffer;
size_t size;
mBuffer->steal(&buffer, &size);
mBuffer = nullptr;
memcpy(aArray.Elements(), buffer, size);
js_free(buffer);
}
void
StructuredCloneHolder::FreeBuffer(uint64_t* aBuffer,
size_t aBufferLength)
{
MOZ_ASSERT(!mBuffer, "FreeBuffer() must be called without a Write().");
MOZ_ASSERT(aBuffer);
MOZ_ASSERT(aBufferLength);
JS_ClearStructuredClone(aBuffer, aBufferLength, &gCallbacks, this, false);
}
/* static */ JSObject*
StructuredCloneHolder::ReadFullySerializableObjects(JSContext* aCx,
JSStructuredCloneReader* aReader,

View File

@ -8,7 +8,7 @@
#include "js/StructuredClone.h"
#include "mozilla/Move.h"
#include "nsAutoPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsISupports.h"
#include "nsTArray.h"
@ -36,6 +36,8 @@ public:
StructuredCloneHolderBase(StructuredCloneScope aScope = StructuredCloneScope::SameProcessSameThread);
virtual ~StructuredCloneHolderBase();
StructuredCloneHolderBase(StructuredCloneHolderBase&& aOther) = default;
// These methods should be implemented in order to clone data.
// Read more documentation in js/public/StructuredClone.h.
@ -102,20 +104,14 @@ public:
return !!mBuffer;
}
uint64_t* BufferData() const
JSStructuredCloneData& BufferData() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBuffer->data();
}
size_t BufferSize() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBuffer->nbytes();
}
protected:
nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
UniquePtr<JSAutoStructuredCloneBuffer> mBuffer;
StructuredCloneScope mStructuredCloneScope;
@ -156,6 +152,8 @@ public:
StructuredCloneScope aStructuredCloneScope);
virtual ~StructuredCloneHolder();
StructuredCloneHolder(StructuredCloneHolder&& aOther) = default;
// Normally you should just use Write() and Read().
void Write(JSContext* aCx,
@ -172,12 +170,6 @@ public:
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv);
// Sometimes, when IPC is involved, you must send a buffer after a Write().
// This method 'steals' the internal data from this class.
// You should free this buffer with StructuredCloneHolder::FreeBuffer().
void MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
ErrorResult& aRv);
// Call this method to know if this object is keeping some DOM object alive.
bool HasClonedDOMObjects() const
{
@ -266,29 +258,25 @@ public:
JSStructuredCloneWriter* aWriter,
JS::Handle<JSObject*> aObj);
static const JSStructuredCloneCallbacks sCallbacks;
protected:
// If you receive a buffer from IPC, you can use this method to retrieve a
// JS::Value. It can happen that you want to pre-populate the array of Blobs
// and/or the PortIdentifiers.
void ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv);
void ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
JSStructuredCloneData& aBuffer,
uint32_t aAlgorithmVersion,
JS::MutableHandle<JS::Value> aValue,
ErrorResult &aRv);
// Use this method to free a buffer generated by MoveToBuffer().
void FreeBuffer(uint64_t* aBuffer,
size_t aBufferLength);
bool mSupportsCloning;
bool mSupportsTransferring;

View File

@ -17,7 +17,7 @@
#include "nsIAtom.h"
#include "nsUnicharUtils.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoBindingHelpers.h"
#include "mozilla/css/Declaration.h"
#include "nsContentUtils.h"
#include "nsReadableUtils.h"
@ -463,12 +463,12 @@ nsAttrValue::SetTo(css::Declaration* aValue, const nsAString* aSerialized)
}
void
nsAttrValue::SetTo(ServoDeclarationBlock* aValue,
nsAttrValue::SetTo(already_AddRefed<ServoDeclarationBlock> aValue,
const nsAString* aSerialized)
{
MiscContainer* cont = EnsureEmptyMiscContainer();
MOZ_ASSERT(cont->mValue.mRefCount == 0);
cont->mValue.mServoCSSDeclaration = aValue;
cont->mValue.mServoCSSDeclaration = aValue.take();
cont->mType = eServoCSSDeclaration;
NS_ADDREF(cont);
SetMiscAtomOrString(aSerialized);
@ -1745,12 +1745,11 @@ nsAttrValue::ParseStyleAttribute(const nsAString& aString,
if (ownerDoc->GetStyleBackendType() == StyleBackendType::Servo) {
NS_ConvertUTF16toUTF8 value(aString);
ServoDeclarationBlock* decl = Servo_ParseStyleAttribute(
RefPtr<ServoDeclarationBlock> decl = Servo_ParseStyleAttribute(
reinterpret_cast<const uint8_t*>(value.get()),
value.Length(),
sheet);
value.Length(), sheet).Consume();
MOZ_ASSERT(decl);
SetTo(decl, &aString);
SetTo(decl.forget(), &aString);
} else {
css::Loader* cssLoader = ownerDoc->CSSLoader();
nsCSSParser cssParser(cssLoader);
@ -1862,7 +1861,7 @@ nsAttrValue::ClearMiscContainer()
if (cont->mType == eGeckoCSSDeclaration) {
NS_RELEASE(cont->mValue.mGeckoCSSDeclaration);
} else {
Servo_DropDeclarationBlock(cont->mValue.mServoCSSDeclaration);
Servo_DeclarationBlock_Release(cont->mValue.mServoCSSDeclaration);
}
break;
}

View File

@ -148,7 +148,7 @@ public:
void SetTo(int32_t aInt, const nsAString* aSerialized);
void SetTo(double aValue, const nsAString* aSerialized);
void SetTo(mozilla::css::Declaration* aValue, const nsAString* aSerialized);
void SetTo(ServoDeclarationBlock* aDeclarationBlock,
void SetTo(already_AddRefed<ServoDeclarationBlock> aDeclarationBlock,
const nsAString* aSerialized);
void SetTo(mozilla::css::URLValue* aValue, const nsAString* aSerialized);
void SetTo(const nsIntMargin& aValue);

View File

@ -1021,6 +1021,8 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
return rv;
}
mRemoteBrowser->SwapLayerTreeObservers(aOther->mRemoteBrowser);
nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
aOther->mRemoteBrowser->GetBrowserDOMWindow();
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
@ -3201,6 +3203,46 @@ nsFrameLoader::RequestNotifyAfterRemotePaint()
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeReady()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeReady() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
if (!mOwnerContent) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeReady"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeCleared()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeCleared() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
if (!mOwnerContent) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeCleared"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::Print(uint64_t aOuterWindowID,
nsIPrintSettings* aPrintSettings,

View File

@ -276,8 +276,13 @@ BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType*
ClonedMessageData& aClonedData)
{
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
buffer.data = aData.Data();
buffer.dataLength = aData.DataLength();
auto iter = aData.Data().Iter();
size_t size = aData.Data().Size();
bool success;
buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
if (NS_WARN_IF(!success)) {
return false;
}
aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
@ -325,7 +330,7 @@ UnpackClonedMessageData(const ClonedMessageData& aClonedData,
const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aClonedData);
const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
aData.UseExternalData(buffer.data, buffer.dataLength);
aData.UseExternalData(buffer.data);
aData.PortIdentifiers().AppendElements(identifiers);

View File

@ -11121,14 +11121,14 @@ nsGlobalWindow::ShowSlowScriptDialog()
buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
// Null out the operation callback while we're re-entering JS here.
bool old = JS_DisableInterruptCallback(cx);
JSInterruptCallback old = JS_SetInterruptCallback(cx, nullptr);
// Open the dialog.
rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
debugButton, neverShowDlg, &neverShowDlgChk,
&buttonPressed);
JS_ResetInterruptCallback(cx, old);
JS_SetInterruptCallback(cx, old);
if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;

View File

@ -138,6 +138,14 @@ interface nsIFrameLoader : nsISupports
*/
void requestNotifyAfterRemotePaint();
/**
* Request an event when the layer tree from the remote tab becomes
* available or unavailable. When this happens, a mozLayerTreeReady
* or mozLayerTreeCleared event is fired.
*/
void requestNotifyLayerTreeReady();
void requestNotifyLayerTreeCleared();
/**
* Print the current document.
*

View File

@ -137,7 +137,11 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
return NS_ERROR_FAILURE;
}
nsAutoCString binaryData(reinterpret_cast<char*>(Data()), DataLength());
auto iter = Data().Iter();
size_t size = Data().Size();
nsAutoCString binaryData;
binaryData.SetLength(size);
Data().ReadBytes(iter, binaryData.BeginWriting(), size);
nsAutoCString base64Data;
nsresult rv = Base64Encode(binaryData, base64Data);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -152,9 +152,13 @@ public:
ClonedMessageData message;
bool success;
SerializedStructuredCloneBuffer& buffer = message.data();
buffer.data = mData->BufferData();
buffer.dataLength = mData->BufferSize();
auto iter = mData->BufferData().Iter();
buffer.data = mData->BufferData().Borrow<js::SystemAllocPolicy>(iter, mData->BufferData().Size(), &success);
if (NS_WARN_IF(!success)) {
return NS_OK;
}
PBackgroundChild* backgroundManager = mActor->Manager();
MOZ_ASSERT(backgroundManager);

View File

@ -91,12 +91,11 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
cloneData.BlobImpls().AppendElements(blobs);
const SerializedStructuredCloneBuffer& buffer = aData.data();
cloneData.UseExternalData(buffer.data, buffer.dataLength);
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx, JS::NullValue());
if (buffer.dataLength) {
if (buffer.data.Size()) {
ErrorResult rv;
cloneData.UseExternalData(buffer.data);
cloneData.Read(cx, &value, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();

View File

@ -4,7 +4,11 @@
"use strict";
dump("######################## BrowserElementChildPreload.js loaded\n");
function debug(msg) {
// dump("BrowserElementChildPreload - " + msg + "\n");
}
debug("loaded");
var BrowserElementIsReady = false;
@ -30,10 +34,6 @@ const Timer = Components.Constructor("@mozilla.org/timer;1",
"nsITimer",
"initWithCallback");
function debug(msg) {
//dump("BrowserElementChildPreload - " + msg + "\n");
}
function sendAsyncMsg(msg, data) {
// Ensure that we don't send any messages before BrowserElementChild.js
// finishes loading.

View File

@ -6,7 +6,11 @@
"use strict";
dump("###################################### BrowserElementCopyPaste.js loaded\n");
function debug(msg) {
// dump("BrowserElementCopyPaste - " + msg + "\n");
}
debug("loaded");
var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;

View File

@ -6,7 +6,12 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
dump("############################### browserElementPanning.js loaded\n");
function debug(msg) {
// dump("BrowserElementPanning - " + msg + "\n");
}
debug("loaded");
var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
@ -151,7 +156,7 @@ const ContentPanning = {
_isRectZoomedIn: function(aRect, aViewport) {
// This function checks to see if the area of the rect visible in the
// viewport (i.e. the "overlapArea" variable below) is approximately
// viewport (i.e. the "overlapArea" variable below) is approximately
// the max area of the rect we can show.
let vRect = new Rect(aViewport.x, aViewport.y, aViewport.width, aViewport.height);
let overlap = vRect.intersect(aRect);
@ -161,7 +166,7 @@ const ContentPanning = {
let ratioW = (aRect.width / vRect.width);
let ratioH = (aRect.height / vRect.height);
return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9));
return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9));
},
_unloadHandler: function() {

View File

@ -125,7 +125,6 @@ WebGLContext::WebGLContext()
, mNeedsFakeNoDepth(false)
, mNeedsFakeNoStencil(false)
, mNeedsEmulatedLoneDepthStencil(false)
, mVRPresentationActive(false)
{
mGeneration = 0;
mInvalidated = false;
@ -1307,9 +1306,8 @@ public:
HTMLCanvasElement* canvas = userdata->mCanvas;
WebGLContext* webgl = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
// Present our screenbuffer, if needed.
webgl->PresentScreenBuffer();
webgl->mDrawCallsSinceLastFlush = 0;
// Prepare the context for composition
webgl->BeginComposition();
}
/** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,
@ -1320,10 +1318,8 @@ public:
HTMLCanvasElement* canvas = userdata->mCanvas;
WebGLContext* webgl = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
// Mark ourselves as no longer invalidated.
webgl->MarkContextClean();
webgl->UpdateLastUseIndex();
// Clean up the context after composition
webgl->EndComposition();
}
private:
@ -1612,6 +1608,24 @@ WebGLContext::PresentScreenBuffer()
return true;
}
// Prepare the context for capture before compositing
void
WebGLContext::BeginComposition()
{
// Present our screenbuffer, if needed.
PresentScreenBuffer();
mDrawCallsSinceLastFlush = 0;
}
// Clean up the context after captured for compositing
void
WebGLContext::EndComposition()
{
// Mark ourselves as no longer invalidated.
MarkContextClean();
UpdateLastUseIndex();
}
void
WebGLContext::DummyReadFramebufferOperation(const char* funcName)
{
@ -2340,40 +2354,43 @@ WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
already_AddRefed<layers::SharedSurfaceTextureClient>
WebGLContext::GetVRFrame()
{
VRManagerChild *vrmc = VRManagerChild::Get();
if (!vrmc) {
return nullptr;
}
PresentScreenBuffer();
mDrawCallsSinceLastFlush = 0;
MarkContextClean();
UpdateLastUseIndex();
gl::GLScreenBuffer* screen = gl->Screen();
if (!screen) {
return nullptr;
}
RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
if (!sharedSurface) {
return nullptr;
}
if (sharedSurface && sharedSurface->GetAllocator() != vrmc) {
RefPtr<SharedSurfaceTextureClient> dest =
screen->Factory()->NewTexClient(sharedSurface->GetSize());
if (!dest) {
return nullptr;
VRManagerChild* vrmc = VRManagerChild::Get();
if (!vrmc) {
return nullptr;
}
gl::SharedSurface* destSurf = dest->Surf();
destSurf->ProducerAcquire();
SharedSurface::ProdCopy(sharedSurface->Surf(), dest->Surf(), screen->Factory());
destSurf->ProducerRelease();
return dest.forget();
}
/**
* Swap buffers as though composition has occurred.
* We will then share the resulting front buffer to be submitted to the VR
* compositor.
*/
BeginComposition();
EndComposition();
gl::GLScreenBuffer* screen = gl->Screen();
if (!screen) {
return nullptr;
}
RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
if (!sharedSurface) {
return nullptr;
}
if (sharedSurface && sharedSurface->GetAllocator() != vrmc) {
RefPtr<SharedSurfaceTextureClient> dest =
screen->Factory()->NewTexClient(sharedSurface->GetSize());
if (!dest) {
return nullptr;
}
gl::SharedSurface* destSurf = dest->Surf();
destSurf->ProducerAcquire();
SharedSurface::ProdCopy(sharedSurface->Surf(), dest->Surf(),
screen->Factory());
destSurf->ProducerRelease();
return dest.forget();
}
return sharedSurface.forget();
}
@ -2381,26 +2398,25 @@ WebGLContext::GetVRFrame()
bool
WebGLContext::StartVRPresentation()
{
VRManagerChild *vrmc = VRManagerChild::Get();
if (!vrmc) {
return false;
}
gl::GLScreenBuffer* screen = gl->Screen();
if (!screen) {
return false;
}
gl::SurfaceCaps caps = screen->mCaps;
VRManagerChild* vrmc = VRManagerChild::Get();
if (!vrmc) {
return false;
}
gl::GLScreenBuffer* screen = gl->Screen();
if (!screen) {
return false;
}
gl::SurfaceCaps caps = screen->mCaps;
UniquePtr<gl::SurfaceFactory> factory =
gl::GLScreenBuffer::CreateFactory(gl,
caps,
vrmc,
vrmc->GetBackendType(),
TextureFlags::ORIGIN_BOTTOM_LEFT);
UniquePtr<gl::SurfaceFactory> factory =
gl::GLScreenBuffer::CreateFactory(gl,
caps,
vrmc,
vrmc->GetBackendType(),
TextureFlags::ORIGIN_BOTTOM_LEFT);
screen->Morph(Move(factory));
mVRPresentationActive = true;
return true;
screen->Morph(Move(factory));
return true;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -355,6 +355,11 @@ public:
bool PresentScreenBuffer();
// Prepare the context for capture before compositing
void BeginComposition();
// Clean up the context after captured for compositing
void EndComposition();
// a number that increments every time we have an event that causes
// all context resources to be lost.
uint32_t Generation() { return mGeneration.value(); }
@ -1517,7 +1522,6 @@ protected:
bool mNeedsFakeNoDepth;
bool mNeedsFakeNoStencil;
bool mNeedsEmulatedLoneDepthStencil;
bool mVRPresentationActive;
bool HasTimestampBits() const;

View File

@ -59,7 +59,7 @@ DragEvent::InitDragEvent(const nsAString& aType,
aView, aDetail, aScreenX, aScreenY,
aClientX, aClientY, aCtrlKey, aAltKey,
aShiftKey, aMetaKey, aButton, aRelatedTarget);
if (mEventIsInternal && mEvent) {
if (mEventIsInternal) {
mEvent->AsDragEvent()->mDataTransfer = aDataTransfer;
}
}

View File

@ -6,8 +6,9 @@ support-files =
pointerevent_styles.css
pointerevent_support.js
[test_pointerevent_button_attribute_mouse-manual.html]
support-files = pointerevent_button_attribute_mouse-manual.html
[test_pointerevent_attributes_mouse-manual.html]
support-files = pointerevent_attributes_mouse-manual.html
disabled = should be investigated
[test_pointerevent_capture_mouse-manual.html]
support-files = pointerevent_capture_mouse-manual.html
[test_pointerevent_capture_suppressing_mouse-manual.html]
@ -17,6 +18,7 @@ support-files =
disabled = disabled
[test_pointerevent_constructor.html]
support-files = pointerevent_constructor.html
disabled = should be investigated
[test_pointerevent_element_haspointercapture.html]
support-files = pointerevent_element_haspointercapture.html
[test_pointerevent_gotpointercapture_before_first_pointerevent-manual.html]
@ -26,14 +28,23 @@ support-files =
support-files = pointerevent_lostpointercapture_for_disconnected_node-manual.html
[test_pointerevent_lostpointercapture_is_first-manual.html]
support-files = pointerevent_lostpointercapture_is_first-manual.html
[test_pointerevent_multiple_primary_pointers_boundary_events-manual.html]
support-files = pointerevent_multiple_primary_pointers_boundary_events-manual.html
disabled = should be investigated
[test_pointerevent_pointercancel_touch-manual.html]
support-files = pointerevent_pointercancel_touch-manual.html
[test_pointerevent_pointerdown-manual.html]
support-files = pointerevent_pointerdown-manual.html
disabled = should be investigated
[test_pointerevent_pointerenter_does_not_bubble-manual.html]
support-files = pointerevent_pointerenter_does_not_bubble-manual.html
[test_pointerevent_pointerenter_nohover-manual.html]
support-files = pointerevent_pointerenter_nohover-manual.html
[test_pointerevent_pointerId_scope-manual.html]
support-files =
test_pointerevent_pointerId_scope-manual.html
./resources/pointerevent_pointerId_scope-iframe.html
disabled = should be investigated
[test_pointerevent_pointerenter-manual.html]
support-files = pointerevent_pointerenter-manual.html
[test_pointerevent_pointerleave_after_pointercancel_touch-manual.html]
@ -57,6 +68,9 @@ support-files =
support-files = pointerevent_pointermove-manual.html
[test_pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html]
support-files = pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html
[test_pointerevent_pointermove-on-chorded-mouse-button.html]
support-files = pointerevent_pointermove-on-chorded-mouse-button.html
disabled = should be investigated
[test_pointerevent_pointermove_pointertype-manual.html]
support-files = pointerevent_pointermove_pointertype-manual.html
[test_pointerevent_pointerout-manual.html]
@ -80,6 +94,7 @@ support-files =
support-files = pointerevent_pointertype_touch-manual.html
[test_pointerevent_pointerup-manual.html]
support-files = pointerevent_pointerup-manual.html
disabled = should be investigated
[test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html]
support-files = pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
[test_pointerevent_pointerup_pointertype-manual.html]
@ -96,10 +111,17 @@ support-files =
support-files = pointerevent_setpointercapture_disconnected-manual.html
[test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
disabled = should be investigated
[test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
[test_pointerevent_setpointercapture_relatedtarget-manual.html]
support-files = pointerevent_setpointercapture_relatedtarget-manual.html
[test_pointerevent_suppress_compat_events_on_click.html]
support-files = pointerevent_suppress_compat_events_on_click.html
disabled = should be investigated
[test_pointerevent_suppress_compat_events_on_drag_mouse.html]
support-files = pointerevent_suppress_compat_events_on_drag_mouse.html
disabled = should be investigated
[test_touch_action.html]
# Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
skip-if = (toolkit == 'windows')

View File

@ -0,0 +1,105 @@
<!doctype html>
<html>
<head>
<title>Pointer Events properties tests</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<!-- Additional helper script for common checks across event types -->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
<script>
var detected_pointertypes = {};
var detected_eventTypes = {};
var test_pointerEvent = async_test("pointerevent attributes");
// showPointerTypes is defined in pointerevent_support.js
// Requirements: the callback function will reference the test_pointerEvent object and
// will fail unless the async_test is created with the var name "test_pointerEvent".
add_completion_callback(showPointerTypes);
function run() {
var square1 = document.getElementById("square1");
var rectSquare1 = square1.getBoundingClientRect();
var pointerover_event;
var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerdown', 'pointerup', 'pointerout', 'pointerleave'];
eventList.forEach(function(eventName) {
on_event(square1, eventName, function (event) {
if (detected_eventTypes[event.type])
return;
detected_pointertypes[event.pointerType] = true;
test(function () {
assert_equals(event.pointerType, 'mouse', 'pointerType should be mouse');
}, event.type + ".pointerType attribute is correct.");
// Test button and buttons
if (event.type == 'pointerdown') {
test(function() {
assert_true(event.button == 0, "If left mouse button is pressed button attribute is 0")
}, event.type + "'s button attribute is 0 when left mouse button is pressed.");
test(function() {
assert_true(event.buttons == 1, "If left mouse button is pressed buttons attribute is 1")
}, event.type + "'s buttons attribute is 1 when left mouse button is pressed.");
} else if (event.type == 'pointerup') {
test(function() {
assert_true(event.button == 0, "If left mouse button is just released button attribute is 0")
}, event.type + "'s button attribute is 0 when left mouse button is just released.");
test(function() {
assert_true(event.buttons == 0, "If left mouse button is just released buttons attribute is 0")
}, event.type + "'s buttons attribute is 0 when left mouse button is just released.");
} else {
test(function() {
assert_true(event.button == -1, "If mouse buttons are released button attribute is -1")
}, event.type + "'s button is -1 when mouse buttons are released.");
test(function() {
assert_true(event.buttons == 0, "If mouse buttons are released buttons attribute is 0")
}, event.type + "'s buttons is 0 when mouse buttons are released.");
}
// Test clientX and clientY
if (event.type != 'pointerout' && event.type != 'pointerleave' ) {
test(function () {
assert_true(event.clientX >= rectSquare1.left && event.clientX < rectSquare1.right, "ClientX should be in the boundaries of the black box");
}, event.type + ".clientX attribute is correct.");
test(function () {
assert_true(event.clientY >= rectSquare1.top && event.clientY < rectSquare1.bottom, "ClientY should be in the boundaries of the black box");
}, event.type + ".clientY attribute is correct.");
} else {
test(function () {
assert_true(event.clientX < rectSquare1.left || event.clientX > rectSquare1.right - 1 || event.clientY < rectSquare1.top || event.clientY > rectSquare1.bottom - 1, "ClientX/Y should be out of the boundaries of the black box");
}, event.type + "'s ClientX and ClientY attributes are correct.");
}
// Test isPrimary
test(function () {
assert_equals(event.isPrimary, true, "isPrimary should be true");
}, event.type + ".isPrimary attribute is correct.");
check_PointerEvent(event);
detected_eventTypes[event.type] = true;
if (Object.keys(detected_eventTypes).length == eventList.length)
test_pointerEvent.done();
});
});
}
</script>
</head>
<body onload="run()">
<h1>Pointer Events pointerdown tests</h1>
<!--
<h4>
Test Description: This test checks the properties of mouse pointer events. Move your mouse over the black square and click on it. Then move it off the black square.
</h4>
-->
Test passes if the proper behavior of the events is observed.
<div id="square1" class="square"></div>
<div class="spacer"></div>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
<p>Refresh the page to run the tests again with a different pointer type.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -1,61 +0,0 @@
<!doctype html>
<html>
<head>
<title>Button and buttons attribute test for mouse</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
</head>
<body onload="run()">
<h1>Button attribute test for mouse</h1>
<!--
<h2>This test is for mouse only</h2>
<h4>
Test Description: This test checks if button attribute for mouse handled properly.
<p>Put your mouse over the black rectangle</p>
</h4>
<p>
-->
<div id="target0" style="background:black"></div>
<div id="target1" style="background:yellow"></div>
<script>
var eventTested = false;
var detected_pointertypes = {};
setup({ explicit_done: true });
add_completion_callback(showPointerTypes);
function run() {
var target0 = document.getElementById("target0");
// If pointerType is "mouse" and no mouse button is depressed, then the button attribute of the pointermove event must be -1 and the buttons attribute must be 0.
// TA: 5.8
on_event(target0, "pointerover", function (event) {
detected_pointertypes[event.pointerType] = true;
if(event.pointerType != "mouse") {
alert("Use mouse for this test please!");
return;
}
if (eventTested == false) {
test(function() {
assert_true(event.button == -1, "If mouse buttons are released button attribute is -1")
}, "If mouse buttons are released button attribute is -1");
test(function() {
assert_true(event.buttons == 0, "If mouse buttons are released buttons attribute is 0")
}, "If mouse buttons are released buttons attribute is 0");
eventTested = true;
done();
}
});
}
</script>
<h1>Pointer Events button attribute test for mouse test</h1>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -37,6 +37,7 @@
<script type='text/javascript'>
var isPointerCapture = false;
var isRelatedTargetValueTested = false;
var isTargetAuthenticityTested = false;
var count = 0;
var detected_pointertypes = {};
@ -95,6 +96,13 @@
}, "relatedTarget is null when the capture is set. relatedTarget is " + event.relatedTarget);
isRelatedTargetValueTested = true;
}
var hitTest = document.elementFromPoint(event.clientX, event.clientY);
if(event.target !== hitTest && !isTargetAuthenticityTested) {
test(function () {
assert_unreached("pointerover for this target shouldn't trigger events on capture target");
}, "pointerover should only trigger over the black rectangle");
isTargetAuthenticityTested = true;
}
}
else {
test_pointerover_no_capture.done();

View File

@ -15,11 +15,10 @@
<h4>Test Description: This test checks if PointerEvent constructor works properly using synthetic pointerover and pointerout events. For valid results, this test must be run without generating real (trusted) pointerover or pointerout events on the black rectangle below.</h4>
<div id="target0"></div>
<script>
var eventTested = false;
var detected_pointertypes = {};
setup({ explicit_done: true });
add_completion_callback(showPointerTypes);
function run() {
async_test(function() {
var target0 = document.getElementById("target0");
// set values for non-default constructor
var testBubbles = true;
@ -34,10 +33,8 @@
var testTiltY = 30;
var testPressure = 0.4;
var testIsPrimary = true;
var pointerEventCustom;
var pointerEventDefault;
on_event(target0, "pointerover", function(event) {
on_event(target0, "pointerover", this.step_func(function(event) {
detected_pointertypes[ event.pointerType ] = true;
generate_tests(assert_equals, [
["custom bubbles", event.bubbles, testBubbles],
@ -55,25 +52,25 @@
test(function() {
assert_approx_equals(event.pressure, testPressure, 0.00000001, "custom pressure: ");
}, "custom pressure: ");
});
}));
on_event(target0, "pointerout", function(event) {
on_event(target0, "pointerout", this.step_func(function(event) {
generate_tests(assert_equals, [
["default pointerId", event.pointerId, 0],
["default pointerType", event.pointerType, ""],
["default width", event.width, 0],
["default height", event.height, 0],
["default width", event.width, 1],
["default height", event.height, 1],
["default tiltX", event.tiltX, 0],
["default tiltY", event.tiltY, 0],
["default pressure", event.pressure, 0],
["default isPrimary", event.isPrimary, false]
]);
});
}));
test(function() {
on_event(window, "load", this.step_func_done(function() {
assert_not_equals(window.PointerEvent, undefined);
pointerEventCustom = new PointerEvent("pointerover",
var pointerEventCustom = new PointerEvent("pointerover",
{bubbles: testBubbles,
cancelable: testCancelable,
pointerId: testPointerId,
@ -91,11 +88,10 @@
// For attributes where values are not provided to the constructor, the corresponding default values must be used.
// TA: 12.1
target0.dispatchEvent(pointerEventCustom);
pointerEventDefault = new PointerEvent("pointerout");
var pointerEventDefault = new PointerEvent("pointerout");
target0.dispatchEvent(pointerEventDefault);
done();
}, "PointerEvent constructor");
}
}, "PointerEvent constructor"));
})
</script>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>

View File

@ -0,0 +1,148 @@
<!doctype html>
<html>
<head>
<title>Pointer Event: Boundary compatibility events for multiple primary pointers</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="author" title="Google" href="http://www.google.com "/>
<meta name="assert" content="When more than one primary pointers are active, each will have an independent sequence of pointer boundary events but the compatibilty mouse boundary events have their own sequence."/>
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
<script type="text/javascript">
var test_pointerEvent = async_test("Multi-pointer boundary compat events");
add_completion_callback(end_of_test);
var detected_pointertypes = {};
var event_log = [];
// These two ids help us detect two different pointing devices.
var first_entry_pointer_id = -1;
var second_entry_pointer_id = -1;
// Current node for each pointer id
var current_node_for_id = {};
function end_of_test() {
showLoggedEvents();
showPointerTypes();
}
function end_of_interaction() {
test(function () {
assert_equals(event_log.join(", "),
"mouseover@target0, mouseenter@target0, mouseout@target0, mouseleave@target0, " +
"mouseover@target1, mouseenter@target1, mouseout@target1, mouseleave@target1, " +
"mouseover@target0, mouseenter@target0, mouseout@target0, mouseleave@target0"
);
}, "Event log");
test_pointerEvent.done(); // complete test
}
function log_event(label) {
event_log.push(label);
}
function run() {
on_event(document.getElementById("done"), "click", end_of_interaction);
var target_list = ["target0", "target1"];
var pointer_event_list = ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointerdown"];
var mouse_event_list = ["mouseenter", "mouseleave", "mouseover", "mouseout"];
target_list.forEach(function(targetId) {
var target = document.getElementById(targetId);
pointer_event_list.forEach(function(eventName) {
on_event(target, eventName, function (event) {
var label = event.type + "@" + targetId;
detected_pointertypes[event.pointerType] = true;
if (!event.isPrimary) {
test(function () {
assert_unreached("Non-primary pointer " + label);
}, "Non-primary pointer " + label);
}
if (event.type === "pointerenter") {
var pointer_id = event.pointerId;
if (current_node_for_id[pointer_id] !== undefined) {
test(function () {
assert_unreached("Double entry " + label);
}, "Double entry " + label);
}
current_node_for_id[pointer_id] = event.target;
// Test that two different pointing devices are used
if (first_entry_pointer_id === -1) {
first_entry_pointer_id = pointer_id;
} else if (second_entry_pointer_id === -1) {
second_entry_pointer_id = pointer_id;
test(function () {
assert_true(first_entry_pointer_id !== second_entry_pointer_id);
}, "Different pointing devices");
}
} else if (event.type === "pointerleave") {
var pointer_id = event.pointerId;
if (current_node_for_id[pointer_id] !== event.target) {
test(function () {
assert_unreached("Double exit " + label);
}, "Double exit " + label);
}
current_node_for_id[pointer_id] = undefined;
}
});
});
mouse_event_list.forEach(function(eventName) {
on_event(target, eventName, function (event) {
log_event(event.type + "@" + targetId);
});
});
});
}
</script>
<style>
#target0, #target1 {
margin: 20px;
}
#done {
margin: 20px;
border: 2px solid black;
}
</style>
</head>
<body onload="run()">
<h1>Pointer Event: Boundary compatibility events for multiple primary pointers</h1>
<!--
<h4>
When more than one primary pointers are active, each will have an independent sequence of pointer boundary events but the compatibilty mouse boundary events have their own sequence.
</h4>
Instruction:
<ol>
<li>Move the mouse directly into Target0 (without going through Target1), and then leave the mouse there unmoved.</li>
<li>Tap directly on Target1 with a finger or a stylus, and then lift the finger/stylus off the screen/digitizer without crossing Target1 boundary.</li>
<li>Move the mouse into Target0 (if not there already) and move inside it.</li>
<li>Click Done (without passing over Target1).</li>
</ol>
-->
<div id="done">
Done
</div>
<div id="target0">
Target0
</div>
<div id="target1">
Target1
</div>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
<p>The following events were logged: <span id="event-log"></span>.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -0,0 +1,85 @@
<!doctype html>
<html>
<!--
Test cases for Pointer Events v1 spec
This document references Test Assertions (abbrev TA below) written by Cathy Chan
http://www.w3.org/wiki/PointerEvents/TestAssertions
-->
<head>
<title>Pointer Events pointerdown tests</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<!-- Additional helper script for common checks across event types -->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
<script>
var detected_pointertypes = {};
var test_pointerEvent = async_test("pointerId of an active pointer is the same across iframes");
// showPointerTypes is defined in pointerevent_support.js
// Requirements: the callback function will reference the test_pointerEvent object and
// will fail unless the async_test is created with the var name "test_pointerEvent".
add_completion_callback(showPointerTypes);
var detected_pointertypes = {};
function run() {
var target0 = document.getElementById("target0");
var pointerover_pointerId = null;
var pointerover_pointerType = null;
var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
var receivedEvents = {};
var receivedEventsInnerFrame = {};
function checkPointerId(event, inner) {
detected_pointertypes[event.pointerType] = true;
var eventName = (inner ? "inner frame " : "" ) + event.type;
test_pointerEvent.step(function() {
assert_equals(event.pointerId, pointerover_pointerId, "PointerId of " + eventName + " is not correct");
assert_equals(event.pointerType, pointerover_pointerType, "PointerType of " + eventName + " is not correct");
}, eventName + ".pointerId were the same as first pointerover");
}
on_event(window, "message", function(event) {
var pe_event = JSON.parse(event.data);
receivedEventsInnerFrame[pe_event.type] = 1;
checkPointerId(pe_event, true);
if (Object.keys(receivedEvents).length == eventList.length && Object.keys(receivedEventsInnerFrame).length == eventList.length)
test_pointerEvent.done();
});
eventList.forEach(function(eventName) {
on_event(target0, eventName, function (event) {
if (pointerover_pointerId === null && event.type == 'pointerover') {
pointerover_pointerId = event.pointerId;
pointerover_pointerType = event.pointerType;
} else {
checkPointerId(event, false);
}
receivedEvents[event.type] = 1;
});
});
}
</script>
</head>
<body onload="run()">
<h1>Pointer Events pointerdown tests</h1>
Complete the following actions:
<!--
<ol>
<li>Start with your pointing device outside of black box, then move it into black box. If using touch just press in black box and don't release.
<li>Move your pointing device into purple box (without leaving the digitizer range if you are using hover supported pen or without releasing touch if using touch). Then move it out of the purple box.
</ol>
-->
<div id="target0" class="touchActionNone">
</div>
<iframe src="resources/pointerevent_pointerId_scope-iframe.html" id="innerframe"></iframe>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
<p>Refresh the page to run the tests again with a different pointer type.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -0,0 +1,77 @@
<!doctype html>
<html>
<head>
<title>Pointermove on button state changes</title>
<meta name="viewport" content="width=device-width">
<meta name="assert" content="When a pointer changes button state and does not produce a different event, the pointermove event must be dispatched."/>
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<!-- Additional helper script for common checks across event types -->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
</head>
<body onload="run()">
<h2>PointerMove</h2>
<h4>Test Description: This test checks if pointermove event are triggered by button state changes
<ol>
<li>Put your mouse over the black rectangle</li>
<li>Press a button and hold it</li>
<li>Press a second button</li>
<li>Release the second button</li>
<li>Release the first button to complete the test</li>
</ol>
</h4>
<div id="target0" style="background:black"></div>
<script>
var eventTested = false;
var detected_pointertypes = {};
var test_pointermove = async_test("pointermove events received for button state changes");
add_completion_callback(showPointerTypes);
var step = 0;
var firstButton = 0;
function run() {
var target0 = document.getElementById("target0");
// When a pointer changes button state and the circumstances produce no other pointer event, the pointermove event must be dispatched.
// 5.2.6
on_event(target0, "pointerdown", function (event) {
detected_pointertypes[event.pointerType] = true;
test_pointermove.step(function() {assert_true(step === 0, "There must not be more than one pointer down event.");});
if (step == 0) {
step = 1;
firstButton = event.buttons;
}
});
on_event(target0, "pointermove", function (event) {
detected_pointertypes[event.pointerType] = true;
if (step == 1 && event.button != -1) { // second button pressed
test_pointermove.step(function() {assert_true(event.buttons !== firstButton, "The pointermove event must be triggered by pressing a second button.");});
test_pointermove.step(function() {assert_true((event.buttons & firstButton) != 0, "The first button must still be reported pressed.");});
step = 2;
} else if (step == 2 && event.button != -1) { // second button released
test_pointermove.step(function() {assert_true(event.buttons === firstButton, "The pointermove event must be triggered by releasing the second button.");});
step = 3;
}
});
on_event(target0, "pointerup", function (event) {
detected_pointertypes[event.pointerType] = true;
test_pointermove.step(function() {assert_true(step === 3, "The pointerup event must be triggered after pressing and releasing the second button.");});
test_pointermove.step(function() {assert_true(event.buttons === 0, "The pointerup event must be triggered by releasing the last pressed button.");});
test_pointermove.done();
eventTested = true;
});
}
</script>
<h1>Pointer Events pointermove on button state changes Tests</h1>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
<p>Refresh the page to run the tests again.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -24,8 +24,6 @@
function eventHandler(event) {
detected_pointertypes[event.pointerType] = true;
if(!eventTested) {
if("pen" != event.pointerType)
return;
check_PointerEvent(event);
test_pointerEvent.step(function () {
assert_equals(event.pointerType, "pen", "Verify event.pointerType is 'pen'.");

View File

@ -24,8 +24,6 @@
function eventHandler(event) {
detected_pointertypes[event.pointerType] = true;
if(!eventTested) {
if("touch" != event.pointerType)
return;
check_PointerEvent(event);
test_pointerEvent.step(function () {
assert_equals(event.pointerType, "touch", "Verify event.pointerType is 'touch'.");

View File

@ -22,7 +22,6 @@
<p>
-->
<div id="target0" style="background:black; color:white;"></div>
<div id="target1" style="background:yellow;"></div>
<script>
var detected_pointertypes = {};

View File

@ -1,3 +1,20 @@
.spacer {
height: 100px;
}
#square1 {
background: black;
top: 150px;
left: 100px;
}
.square {
height: 20px;
width: 20px;
position: absolute;
padding: 0px;
}
#target0 {
background: black;
color: white;
@ -14,6 +31,17 @@ overflow-y: auto;
overflow-x: auto;
}
.touchActionNone {
touch-action: none;
}
#innerframe {
width: 90%;
margin: 10px;
margin-left: 10%;
height: 200px;
}
.scroller {
width: 700px;
height: 430px;
@ -49,6 +77,10 @@ display: none;
font-weight: bold;
}
#event-log {
font-weight: bold;
}
#listener {
background: orange;
border: 1px solid orange;

View File

@ -13,9 +13,10 @@ var All_Pointer_Events = [
// Check for conformance to PointerEvent interface
// TA: 1.1, 1.2, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13
function check_PointerEvent(event) {
var pointerTestName = event.pointerType + ' ' + event.type;
test(function () {
assert_true(event instanceof PointerEvent, "event is a PointerEvent event");
}, event.type + " event is a PointerEvent event");
}, pointerTestName + " event is a PointerEvent event");
// Check attributes for conformance to WebIDL:
@ -37,31 +38,37 @@ function check_PointerEvent(event) {
["readonly", "long", "tiltX"],
["readonly", "long", "tiltY"],
["readonly", "string", "pointerType"],
["readonly", "boolean", "isPrimary"]
["readonly", "boolean", "isPrimary"],
["readonly", "long", "detail", 0]
].forEach(function (attr) {
var readonly = attr[0];
var type = attr[1];
var name = attr[2];
var value = attr[3];
// existence check
test(function () {
assert_true(name in event, name + " attribute in " + event.type + " event");
}, event.type + "." + name + " attribute exists");
}, pointerTestName + "." + name + " attribute exists");
// readonly check
if (readonly === "readonly") {
test(function () {
assert_readonly(event.type, name, event.type + "." + name + " cannot be changed");
}, event.type + "." + name + " is readonly");
}, pointerTestName + "." + name + " is readonly");
}
// type check
test(function () {
assert_true(idl_type_check[type](event[name]), name + " attribute of type " + type);
}, event.type + "." + name + " IDL type " + type + " (JS type was " + typeof event[name] + ")");
}, pointerTestName + "." + name + " IDL type " + type + " (JS type was " + typeof event[name] + ")");
// value check if defined
if (value != undefined) {
test(function () {
assert_equals(event[name], value, name + " attribute value");
}, pointerTestName + "." + name + " value is " + value + ".");
}
});
@ -81,7 +88,7 @@ function check_PointerEvent(event) {
assert_equals(event.pressure, 0.5, "pressure is 0.5 for mouse with a button pressed");
}
}
}, event.type + ".pressure value is valid");
}, pointerTestName + ".pressure value is valid");
// Check mouse-specific properties
@ -91,7 +98,7 @@ function check_PointerEvent(event) {
assert_equals(event.tiltX, 0, event.type + ".tiltX is 0 for mouse");
assert_equals(event.tiltY, 0, event.type + ".tiltY is 0 for mouse");
assert_true(event.isPrimary, event.type + ".isPrimary is true for mouse");
}, event.type + " properties for pointerType = mouse");
}, pointerTestName + " properties for pointerType = mouse");
// Check properties for pointers other than mouse
}
}
@ -105,6 +112,14 @@ function showPointerTypes() {
complete_notice.style.display = "block";
}
function showLoggedEvents() {
var event_log_elem = document.getElementById("event-log");
event_log_elem.innerHTML = event_log.length ? event_log.join(", ") : "(none)";
var complete_notice = document.getElementById("complete-notice");
complete_notice.style.display = "block";
}
function log(msg, el) {
if (++count > 10){
count = 0;
@ -154,35 +169,17 @@ function objectScroller(target, direction, value) {
function sPointerCapture(e) {
try {
if(target0.setPointerCapture)
target0.setPointerCapture(e.pointerId);
else
test(function() {
assert_equals(typeof(target0.setPointerCapture), "function", "target0 should have function setPointerCapture");
}, "target0 should have function setPointerCapture");
target0.setPointerCapture(e.pointerId);
}
catch(e) {
console.log("catch exception: " + e);
test(function() {
assert_true(false, "Exception in function setPointerCapture");
}, "Exception in function setPointerCapture");
}
}
function rPointerCapture(e) {
try {
captureButton.value = 'Set Capture';
if(target0.releasePointerCapture)
target0.releasePointerCapture(e.pointerId);
else
test(function() {
assert_equals(typeof(target0.releasePointerCapture), "function", "target0 should have function releasePointerCapture");
}, "target0 should have function releasePointerCapture");
target0.releasePointerCapture(e.pointerId);
}
catch(e) {
console.log("catch exception: " + e);
test(function() {
assert_true(false, "Exception in function releasePointerCapture");
}, "Exception in function releasePointerCapture");
}
}

View File

@ -0,0 +1,104 @@
<!doctype html>
<html>
<head>
<title>Pointer Event: Suppress compatibility mouse events on click</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="author" title="Google" href="http://www.google.com "/>
<meta name="assert" content="When a pointerdown is canceled, a click/tap shouldn't fire any compatibility mouse events."/>
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
<script type="text/javascript">
var test_pointerEvent = async_test("Suppress compat mouse events on click");
add_completion_callback(end_of_test);
var detected_pointertypes = {};
var event_log = [];
function end_of_test() {
showLoggedEvents();
showPointerTypes();
}
function end_of_interaction() {
test(function () {
assert_equals(event_log.join(", "),
"mousedown@target1, mouseup@target1");
}, "Event log");
test_pointerEvent.done(); // complete test
}
function run() {
on_event(document.getElementById("done"), "click", end_of_interaction);
var target_list = ["target0", "target1"];
var pointer_event_list = ["pointerdown"];
var mouse_event_list = ["mousedown", "mouseup"];
target_list.forEach(function(targetId) {
var target = document.getElementById(targetId);
pointer_event_list.forEach(function(eventName) {
on_event(target, eventName, function (event) {
detected_pointertypes[event.pointerType] = true;
var label = event.type + "@" + targetId;
test(function () {
assert_true(event.isPrimary);
}, "primary pointer " + label);
if (label === "pointerdown@target0")
event.preventDefault();
});
});
mouse_event_list.forEach(function(eventName) {
on_event(target, eventName, function (event) {
event_log.push(event.type + "@" + targetId);
});
});
});
}
</script>
<style>
#target0, #target1 {
margin: 20px;
}
#done {
margin: 20px;
border: 2px solid black;
}
</style>
</head>
<body onload="run()">
<h1>Pointer Event: Suppress compatibility mouse events on click</h1>
<h4>
When a pointerdown is canceled, a click/tap shouldn't fire any compatibility mouse events.
</h4>
<!--
<ol>
<li> Click or tap on Target0.</li>
<li> Click or tap on Target1.</li>
<li> Click Done.</li>
</ol>
-->
<div id="target0">
Target0
</div>
<div id="target1">
Target1
</div>
<div id="done">
Done
</div>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
<p>The following events were logged: <span id="event-log"></span>.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -0,0 +1,117 @@
<!doctype html>
<html>
<head>
<title>Pointer Event: Suppress compatibility mouse events on drag</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="author" title="Google" href="http://www.google.com "/>
<meta name="assert" content="When a pointerdown is canceled, a mouse drag shouldn't fire any compatibility mouse events."/>
<link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
<script src="/resources/testharness.js"></script>
<!--script src="/resources/testharnessreport.js"></script-->
<script type="text/javascript" src="pointerevent_support.js"></script>
<script type="text/javascript" src="mochitest_support_internal.js"></script>
<script type="text/javascript">
var test_pointerEvent = async_test("Suppress compat mouse events on drag");
add_completion_callback(end_of_test);
var detected_pointertypes = {};
var event_log = [];
function end_of_test() {
showLoggedEvents();
showPointerTypes();
}
var include_next_mousemove = false;
// Limits logging/testing of mousemove.
function drop_event(event_type) {
return (event_type == "mousemove" && !include_next_mousemove);
}
function end_of_interaction() {
test(function () {
assert_equals(event_log.join(", "),
"mousedown@target1, mousemove@target1, mouseup@target1");
}, "Event log");
test_pointerEvent.done(); // complete test
}
function run() {
on_event(document.getElementById("done"), "click", end_of_interaction);
var target_list = ["target0", "target1"];
var pointer_event_list = ["pointerdown"];
var mouse_event_list = ["mousedown", "mouseup", "mousemove"];
target_list.forEach(function(targetId) {
var target = document.getElementById(targetId);
pointer_event_list.forEach(function(eventName) {
on_event(target, eventName, function (event) {
detected_pointertypes[event.pointerType] = true;
var label = event.type + "@" + targetId;
test(function () {
assert_true(event.isPrimary);
}, "primary pointer " + label);
if (label === "pointerdown@target0")
event.preventDefault();
});
});
mouse_event_list.forEach(function(eventName) {
on_event(target, eventName, function (event) {
if (drop_event(event.type))
return;
event_log.push(event.type + "@" + targetId);
include_next_mousemove = (event.type == "mousedown");
});
});
});
}
</script>
<style>
#target0, #target1 {
margin: 20px;
touch-action: none;
}
#done {
margin: 20px;
border: 2px solid black;
}
</style>
</head>
<body onload="run()">
<h1>Pointer Event: Suppress compatibility mouse events on drag</h1>
<!--
<h4>
When a pointerdown is canceled, a mouse drag shouldn't fire any compatibility mouse events.
</h4>
<ol>
<li> Drag mouse within Target0 &amp; release.</li>
<li> Drag mouse within Target1 &amp; release.</li>
<li> Click Done.</li>
</ol>
-->
<div id="target0">
Target0
</div>
<div id="target1">
Target1
</div>
<div id="done">
Done
</div>
<div id="complete-notice">
<p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
<p>The following events were logged: <span id="event-log"></span>.</p>
</div>
<div id="log"></div>
</body>
</html>

View File

@ -30,7 +30,7 @@
<h4 id="desc">Test Description: Try to scroll black element DOWN moving your touch outside of the red border. Wait for description update.</h4>
<p>Note: this test is for touch only</p>
<div id="target0">
<button>Test Button</button>
<button id="testButton">Test Button</button>
</div>
<br>
<input type="button" id="btnComplete" value="Complete test">

View File

@ -23,7 +23,7 @@
<h4 id="desc">Test Description: Try to scroll black element DOWN moving your touch outside of the red border. Wait for description update.</h4>
<p>Note: this test is for touch only</p>
<div id="target0">
<svg width="555" height="555" style="touch-action: none; border: 4px double red;">
<svg id="testSvg" width="555" height="555" style="touch-action: none; border: 4px double red;">
<circle cx="305" cy="305" r="250" stroke="green" stroke-width="4" fill="yellow" />
Sorry, your browser does not support inline SVG.
</svg>

View File

@ -0,0 +1,37 @@
<!doctype html>
<html>
<!--
Test cases for Pointer Events v1 spec
This document references Test Assertions (abbrev TA below) written by Cathy Chan
http://www.w3.org/wiki/PointerEvents/TestAssertions
-->
<head>
<title>Pointer Events pointerdown tests</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
<script>
function run() {
var target1 = document.getElementById("target1");
var pointerover_event;
var ponterId = null;
var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
eventList.forEach(function(eventName) {
target1.addEventListener(eventName, function (event) {
var pass_data = {
'pointerId' : event.pointerId,
'type' : event.type,
'pointerType' : event.pointerType
};
top.postMessage(JSON.stringify(pass_data), "*");
});
});
}
</script>
</head>
<body onload="run()">
<div id="target1" class="touchActionNone">
</div>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1000870</title>
<meta name="author" content="Maksim Lebedev" />
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="mochitest_support_external.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
function startTest() {
var iframe = document.getElementById("testFrame");
iframe.src = "pointerevent_attributes_mouse-manual.html";
}
function executeTest(int_win) {
var square1 = int_win.document.getElementById("square1");
var rect = square1.getBoundingClientRect();
var x = rect.left + rect.width / 4;
var y = rect.top + rect.height / 2
synthesizeMouseAtPoint(x, y, {type: "mousemove", button:-1}, int_win);
synthesizeMouseAtPoint(x, y, {type: "mousedown", button:0}, int_win);
synthesizeMouseAtPoint(x, y, {type: "mouseup", button:0}, int_win);
synthesizeMouseAtPoint(x, y, {type: "mousemove", button:-1}, int_win);
synthesizeMouseAtPoint(rect.left-1, rect.top-1, {type: "mousemove", button:-1}, int_win);
}
</script>
</head>
<body>
<iframe id="testFrame" height="800" width="1000"></iframe>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1000870</title>
<meta name="author" content="Maksim Lebedev" />
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="mochitest_support_external.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
function startTest() {
var iframe = document.getElementById("testFrame");
iframe.src = "pointerevent_multiple_primary_pointers_boundary_events-manual.html";
}
function executeTest(int_win) {
sendMouseEvent(int_win, "target0", "mousemove");
sendTouchEvent(int_win, "target1", "touchstart");
sendTouchEvent(int_win, "target1", "touchend");
sendMouseEvent(int_win, "target0", "mousemove");
sendMouseEvent(int_win, "done", "mousedown", {button:0});
sendMouseEvent(int_win, "done", "mouseup", {button:0});
}
</script>
</head>
<body>
<iframe id="testFrame" height="800" width="1000"></iframe>
</body>
</html>

View File

@ -15,11 +15,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
SimpleTest.waitForExplicitFinish();
function startTest() {
var iframe = document.getElementById("testFrame");
iframe.src = "pointerevent_button_attribute_mouse-manual.html";
iframe.src = "pointerevent_pointerId_scope-manual.html";
}
function executeTest(int_win) {
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
sendTouchEvent(int_win, "target0", "touchstart");
sendTouchEvent(int_win, "target0", "touchend");
}
</script>
</head>

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1000870</title>
<meta name="author" content="Maksim Lebedev" />
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="mochitest_support_external.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
function startTest() {
var iframe = document.getElementById("testFrame");
iframe.src = "pointerevent_pointermove-on-chorded-mouse-button.html";
}
function executeTest(int_win) {
sendMouseEvent(int_win, "target0", "mousedown");
sendMouseEvent(int_win, "target0", "mouseup");
}
</script>
</head>
<body>
<iframe id="testFrame" height="800" width="1000"></iframe>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1000870</title>
<meta name="author" content="Maksim Lebedev" />
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="mochitest_support_external.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
function startTest() {
var iframe = document.getElementById("testFrame");
iframe.src = "pointerevent_suppress_compat_events_on_click.html";
}
function executeTest(int_win) {
sendMouseEvent(int_win, "target0", "mousedown", {button:0});
sendMouseEvent(int_win, "target0", "mouseup", {button:0});
sendMouseEvent(int_win, "target1", "mousedown", {button:0});
sendMouseEvent(int_win, "target1", "mouseup", {button:0});
sendMouseEvent(int_win, "done", "mousedown", {button:0});
sendMouseEvent(int_win, "done", "mouseup", {button:0});
}
</script>
</head>
<body>
<iframe id="testFrame" height="800" width="1000"></iframe>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1000870</title>
<meta name="author" content="Maksim Lebedev" />
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="mochitest_support_external.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
function startTest() {
var iframe = document.getElementById("testFrame");
iframe.src = "pointerevent_suppress_compat_events_on_drag_mouse.html";
}
function executeTest(int_win) {
sendMouseEvent(int_win, "target0", "mousedown", {button:0});
sendMouseEvent(int_win, "target0", "mousemove", {button:0});
sendMouseEvent(int_win, "target0", "mouseup", {button:0});
sendMouseEvent(int_win, "target1", "mousedown", {button:0});
sendMouseEvent(int_win, "target1", "mousemove", {button:0});
sendMouseEvent(int_win, "target1", "mouseup", {button:0});
sendMouseEvent(int_win, "done", "mousedown", {button:0});
sendMouseEvent(int_win, "done", "mouseup", {button:0});
}
</script>
</head>
<body>
<iframe id="testFrame" height="800" width="1000"></iframe>
</body>
</html>

View File

@ -907,6 +907,7 @@ void HTMLMediaElement::ShutdownDecoder()
{
RemoveMediaElementFromURITable();
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
mWaitingForKeyListener.DisconnectIfExists();
mDecoder->Shutdown();
mDecoder = nullptr;
}
@ -983,6 +984,7 @@ void HTMLMediaElement::AbortExistingLoads()
#ifdef MOZ_EME
mPendingEncryptedInitData.mInitDatas.Clear();
#endif // MOZ_EME
mWaitingForKey = false;
mSourcePointer = nullptr;
mTags = nullptr;
@ -2509,6 +2511,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mMediaSecurityVerified(false),
mCORSMode(CORS_NONE),
mIsEncrypted(false),
mWaitingForKey(false),
mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
mAudioChannelVolume(1.0),
mPlayingThroughTheAudioChannel(false),
@ -3439,6 +3442,13 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
}
#endif
MediaEventSource<void>* waitingForKeyProducer = mDecoder->WaitingForKeyEvent();
// Not every decoder will produce waitingForKey events, only add ones that can
if (waitingForKeyProducer) {
mWaitingForKeyListener = waitingForKeyProducer->Connect(
AbstractThread::MainThread(), this, &HTMLMediaElement::CannotDecryptWaitingForKey);
}
if (mChannelLoader) {
mChannelLoader->Done();
mChannelLoader = nullptr;
@ -4501,6 +4511,7 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
IsPotentiallyPlaying()) {
mWaitingForKey = false;
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
}
@ -5886,6 +5897,27 @@ HTMLMediaElement::GetTopLevelPrincipal()
}
#endif // MOZ_EME
void
HTMLMediaElement::CannotDecryptWaitingForKey()
{
// See: http://w3c.github.io/encrypted-media/#dom-evt-waitingforkey
// Spec: 7.5.4 Queue a "waitingforkey" Event
// Spec: 1. Let the media element be the specified HTMLMediaElement object.
// Note, existing code will handle the ready state of this element, as
// such this function does not handle changing or checking mReadyState.
// Spec: 2. If the media element's waiting for key value is true, abort these steps.
if (!mWaitingForKey) {
// Spec: 3. Set the media element's waiting for key value to true.
// Spec: 4. Queue a task to fire a simple event named waitingforkey at the media element.
DispatchAsyncEvent(NS_LITERAL_STRING("waitingforkey"));
mWaitingForKey = true;
// No need to explicitly suspend playback, it happens automatically when
// it's starving for decoded frames.
}
}
NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture)
{
MOZ_ASSERT(mAudioChannelAgent);

View File

@ -641,6 +641,8 @@ public:
bool ContainsRestrictedContent();
#endif // MOZ_EME
void CannotDecryptWaitingForKey();
bool MozAutoplayEnabled() const
{
return mAutoplayEnabled;
@ -1537,6 +1539,14 @@ protected:
// True if the media has encryption information.
bool mIsEncrypted;
// True when the CDM cannot decrypt the current block, and the
// waitingforkey event has been fired. Back to false when keys have become
// available and we can advance the current playback position.
bool mWaitingForKey;
// Listens for waitingForKey events from the owned decoder.
MediaEventListener mWaitingForKeyListener;
#ifdef MOZ_EME
// Init Data that needs to be sent in 'encrypted' events in MetadataLoaded().
EncryptionInfo mPendingEncryptedInitData;

View File

@ -430,8 +430,6 @@ private:
{
bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult);
aCloneInfo->mCloneBuffer.clear();
if (NS_WARN_IF(!ok)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}

View File

@ -14840,7 +14840,7 @@ TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
return false;
}
if (NS_WARN_IF(aParams.cloneInfo().data().IsEmpty())) {
if (NS_WARN_IF(!aParams.cloneInfo().data().data.Size())) {
ASSERT_UNLESS_FUZZING();
return false;
}
@ -14855,13 +14855,13 @@ TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams)
return false;
}
if (NS_WARN_IF(cloneInfo.data().Length() < sizeof(uint64_t))) {
if (NS_WARN_IF(cloneInfo.data().data.Size() < sizeof(uint64_t))) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(cloneInfo.offsetToKeyProp() >
(cloneInfo.data().Length() - sizeof(uint64_t)))) {
(cloneInfo.data().data.Size() - sizeof(uint64_t)))) {
ASSERT_UNLESS_FUZZING();
return false;
}
@ -19065,7 +19065,9 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob(
return NS_ERROR_FILE_CORRUPTED;
}
aInfo->mData.SwapElements(uncompressed);
if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!aFileIds.IsVoid()) {
AutoTArray<int64_t, 10> array;
@ -25427,6 +25429,13 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
MOZ_ASSERT(!keyUnset || mMetadata->mCommonMetadata.autoIncrement(),
"Should have key unless autoIncrement");
const JSStructuredCloneData& data = mParams.cloneInfo().data().data;
size_t cloneDataSize = data.Size();
nsCString cloneData;
cloneData.SetLength(cloneDataSize);
auto iter = data.Iter();
data.ReadBytes(iter, cloneData.BeginWriting(), cloneDataSize);
int64_t autoIncrementNum = 0;
if (mMetadata->mCommonMetadata.autoIncrement()) {
@ -25448,16 +25457,14 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
if (keyUnset && keyPath.IsValid()) {
const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo();
MOZ_ASSERT(cloneInfo.offsetToKeyProp());
MOZ_ASSERT(cloneInfo.data().Length() > sizeof(uint64_t));
MOZ_ASSERT(cloneDataSize > sizeof(uint64_t));
MOZ_ASSERT(cloneInfo.offsetToKeyProp() <=
(cloneInfo.data().Length() - sizeof(uint64_t)));
(cloneDataSize - sizeof(uint64_t)));
// Special case where someone put an object into an autoIncrement'ing
// objectStore with no key in its keyPath set. We needed to figure out
// which row id we would get above before we could set that properly.
uint8_t* keyPropPointer =
const_cast<uint8_t*>(cloneInfo.data().Elements() +
cloneInfo.offsetToKeyProp());
char* keyPropPointer = cloneData.BeginWriting() + cloneInfo.offsetToKeyProp();
uint64_t keyPropValue =
ReinterpretDoubleAsUInt64(static_cast<double>(autoIncrementNum));
@ -25468,9 +25475,8 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection)
key.BindToStatement(stmt, NS_LITERAL_CSTRING("key"));
// Compress the bytes before adding into the database.
const char* uncompressed =
reinterpret_cast<const char*>(mParams.cloneInfo().data().Elements());
size_t uncompressedLength = mParams.cloneInfo().data().Length();
const char* uncompressed = cloneData.BeginReading();
size_t uncompressedLength = cloneDataSize;
// We don't have a smart pointer class that calls free, so we need to
// manage | compressed | manually.
@ -25802,7 +25808,7 @@ ObjectStoreGetRequestOp::ConvertResponse(
StructuredCloneReadInfo& info = mResponse[aIndex];
info.mData.SwapElements(aSerializedInfo.data());
aSerializedInfo.data().data = Move(info.mData);
FallibleTArray<BlobOrMutableFile> blobs;
nsresult rv = ConvertBlobsToActors(mBackgroundParent,
@ -26525,7 +26531,7 @@ IndexGetRequestOp::GetResponse(RequestResponse& aResponse)
SerializedStructuredCloneReadInfo& serializedInfo =
fallibleCloneInfos[index];
info.mData.SwapElements(serializedInfo.data());
serializedInfo.data().data = Move(info.mData);
FallibleTArray<BlobOrMutableFile> blobs;
nsresult rv = ConvertBlobsToActors(mBackgroundParent,
@ -26559,7 +26565,7 @@ IndexGetRequestOp::GetResponse(RequestResponse& aResponse)
SerializedStructuredCloneReadInfo& serializedInfo =
aResponse.get_IndexGetResponse().cloneInfo();
info.mData.SwapElements(serializedInfo.data());
serializedInfo.data().data = Move(info.mData);
FallibleTArray<BlobOrMutableFile> blobs;
nsresult rv =
@ -26873,7 +26879,7 @@ CursorOpBase::PopulateResponseFromStatement(
auto& responses = mResponse.get_ArrayOfObjectStoreCursorResponse();
auto& response = *responses.AppendElement();
response.cloneInfo().data().SwapElements(cloneInfo.mData);
response.cloneInfo().data().data = Move(cloneInfo.mData);
response.key() = mCursor->mKey;
mFiles.AppendElement(Move(cloneInfo.mFiles));
@ -26911,7 +26917,7 @@ CursorOpBase::PopulateResponseFromStatement(
mResponse = IndexCursorResponse();
auto& response = mResponse.get_IndexCursorResponse();
response.cloneInfo().data().SwapElements(cloneInfo.mData);
response.cloneInfo().data().data = Move(cloneInfo.mData);
response.key() = mCursor->mKey;
response.sortKey() = mCursor->mSortKey;
response.objectKey() = mCursor->mObjectKey;

View File

@ -107,36 +107,6 @@ struct IDBObjectStore::StructuredCloneWriteInfo
{
MOZ_COUNT_DTOR(StructuredCloneWriteInfo);
}
bool
operator==(const StructuredCloneWriteInfo& aOther) const
{
return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() &&
this->mCloneBuffer.data() == aOther.mCloneBuffer.data() &&
this->mBlobOrMutableFiles == aOther.mBlobOrMutableFiles &&
this->mDatabase == aOther.mDatabase &&
this->mOffsetToKeyProp == aOther.mOffsetToKeyProp;
}
bool
SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther)
{
if (aOther.data().IsEmpty()) {
mCloneBuffer.clear();
} else {
auto* aOtherBuffer =
reinterpret_cast<uint64_t*>(
const_cast<uint8_t*>(aOther.data().Elements()));
if (!mCloneBuffer.copy(aOtherBuffer, aOther.data().Length())) {
return false;
}
}
mBlobOrMutableFiles.Clear();
mOffsetToKeyProp = aOther.offsetToKeyProp();
return true;
}
};
namespace {
@ -897,15 +867,6 @@ CommonStructuredCloneReadCallback(JSContext* aCx,
aTag);
}
// static
void
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
{
if (aBuffer.data()) {
aBuffer.clear();
}
}
} // namespace
const JSClass IDBObjectStore::sDummyPropJSClass = {
@ -1072,11 +1033,10 @@ IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
// This is kind of tricky, we only want to release stuff on the main thread,
// but we can end up being called on other threads if we have already been
// cleared on the main thread.
if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
if (!aReadInfo.mFiles.Length()) {
return;
}
ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer);
aReadInfo.mFiles.Clear();
}
@ -1088,15 +1048,12 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
{
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
if (!aCloneReadInfo.mData.Size()) {
aValue.setUndefined();
return true;
}
auto* data = reinterpret_cast<uint64_t*>(aCloneReadInfo.mData.Elements());
size_t dataLen = aCloneReadInfo.mData.Length();
MOZ_ASSERT(!(dataLen % sizeof(*data)));
MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
@ -1111,7 +1068,7 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
// FIXME: Consider to use StructuredCloneHolder here and in other
// deserializing methods.
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1129,18 +1086,12 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
if (!aCloneReadInfo.mData.Size()) {
aValue.setUndefined();
return true;
}
size_t dataLen = aCloneReadInfo.mData.Length();
uint64_t* data =
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
aCloneReadInfo.mData.Elements()));
MOZ_ASSERT(!(dataLen % sizeof(*data)));
MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
@ -1150,7 +1101,7 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
nullptr
};
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1170,18 +1121,12 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
if (!aCloneReadInfo.mData.Size()) {
aValue.setUndefined();
return true;
}
size_t dataLen = aCloneReadInfo.mData.Length();
uint64_t* data =
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
aCloneReadInfo.mData.Elements()));
MOZ_ASSERT(!(dataLen % sizeof(*data)));
MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
@ -1194,7 +1139,7 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
nullptr
};
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1326,22 +1271,9 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
return nullptr;
}
FallibleTArray<uint8_t> cloneData;
if (NS_WARN_IF(!cloneData.SetLength(cloneWriteInfo.mCloneBuffer.nbytes(),
fallible))) {
aRv = NS_ERROR_OUT_OF_MEMORY;
return nullptr;
}
// XXX Remove this
memcpy(cloneData.Elements(), cloneWriteInfo.mCloneBuffer.data(),
cloneWriteInfo.mCloneBuffer.nbytes());
cloneWriteInfo.mCloneBuffer.clear();
ObjectStoreAddPutParams commonParams;
commonParams.objectStoreId() = Id();
commonParams.cloneInfo().data().SwapElements(cloneData);
commonParams.cloneInfo().data().data = Move(cloneWriteInfo.mCloneBuffer.data());
commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
commonParams.key() = key;
commonParams.indexUpdateInfos().SwapElements(updateInfo);

View File

@ -45,13 +45,10 @@ struct StructuredCloneFile
struct StructuredCloneReadInfo
{
nsTArray<uint8_t> mData;
JSStructuredCloneData mData;
nsTArray<StructuredCloneFile> mFiles;
IDBDatabase* mDatabase;
// XXX Remove!
JSAutoStructuredCloneBuffer mCloneBuffer;
// In IndexedDatabaseInlines.h
inline
StructuredCloneReadInfo();
@ -60,6 +57,10 @@ struct StructuredCloneReadInfo
inline
~StructuredCloneReadInfo();
// In IndexedDatabaseInlines.h
inline
StructuredCloneReadInfo(StructuredCloneReadInfo&& aOther);
// In IndexedDatabaseInlines.h
inline StructuredCloneReadInfo&
operator=(StructuredCloneReadInfo&& aOther);

View File

@ -47,19 +47,29 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
inline
StructuredCloneReadInfo::StructuredCloneReadInfo()
: mDatabase(nullptr)
, mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
nullptr)
{
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
}
inline
StructuredCloneReadInfo::StructuredCloneReadInfo(
StructuredCloneReadInfo&& aCloneReadInfo)
: mData(Move(aCloneReadInfo.mData))
{
MOZ_ASSERT(&aCloneReadInfo != this);
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
mFiles.Clear();
mFiles.SwapElements(aCloneReadInfo.mFiles);
mDatabase = aCloneReadInfo.mDatabase;
aCloneReadInfo.mDatabase = nullptr;
}
inline
StructuredCloneReadInfo::StructuredCloneReadInfo(
SerializedStructuredCloneReadInfo&& aCloneReadInfo)
: mData(Move(aCloneReadInfo.data()))
: mData(Move(aCloneReadInfo.data().data))
, mDatabase(nullptr)
, mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr,
nullptr)
{
MOZ_COUNT_CTOR(StructuredCloneReadInfo);
}
@ -76,7 +86,6 @@ StructuredCloneReadInfo::operator=(StructuredCloneReadInfo&& aCloneReadInfo)
MOZ_ASSERT(&aCloneReadInfo != this);
mData = Move(aCloneReadInfo.mData);
mCloneBuffer = Move(aCloneReadInfo.mCloneBuffer);
mFiles.Clear();
mFiles.SwapElements(aCloneReadInfo.mFiles);
mDatabase = aCloneReadInfo.mDatabase;

View File

@ -30,6 +30,9 @@ using class mozilla::dom::indexedDB::KeyPath
using mozilla::dom::quota::PersistenceType
from "mozilla/dom/quota/PersistenceType.h";
using mozilla::SerializedStructuredCloneBuffer
from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace dom {
namespace indexedDB {
@ -57,13 +60,13 @@ union BlobOrMutableFile
struct SerializedStructuredCloneReadInfo
{
uint8_t[] data;
SerializedStructuredCloneBuffer data;
BlobOrMutableFile[] blobs;
};
struct SerializedStructuredCloneWriteInfo
{
uint8_t[] data;
SerializedStructuredCloneBuffer data;
uint64_t offsetToKeyProp;
};

View File

@ -5501,12 +5501,3 @@ ContentParent::SendGetFilesResponseAndForget(const nsID& aUUID,
Unused << SendGetFilesResponse(aUUID, aResult);
}
}
void
ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch)
{
if (!mHangMonitorActor) {
return;
}
ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
}

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