mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Merge m-c to fx-team. a=merge
This commit is contained in:
commit
baeaac9b10
@ -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:
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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'
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -159,6 +159,7 @@ var AboutHomeListener = {
|
||||
addEventListener("click", this, true);
|
||||
addEventListener("pagehide", this, true);
|
||||
|
||||
sendAsyncMessage("AboutHome:MaybeShowAutoMigrationUndoNotification");
|
||||
sendAsyncMessage("AboutHome:RequestUpdate");
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
107
browser/base/content/test/general/browser_contentAltClick.js
Normal file
107
browser/base/content/test/general/browser_contentAltClick.js
Normal 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();
|
||||
});
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 => {
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
||||
|
@ -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 = Don’t Keep
|
||||
automigration.undo.dontkeep.accesskey = D
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 |
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -395,6 +395,10 @@ html, body, #app, #memory-tool {
|
||||
padding-inline-end: 5px;
|
||||
}
|
||||
|
||||
.children-pointer:dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Heap tree view columns
|
||||
*/
|
||||
|
@ -27,7 +27,6 @@ function VariablesViewLink(props) {
|
||||
onClick: openVariablesView.bind(null, object),
|
||||
className: "cm-variable",
|
||||
draggable: false,
|
||||
href: "#"
|
||||
}, children)
|
||||
);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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]));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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, "");
|
||||
});
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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))) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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')
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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();
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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'.");
|
||||
|
@ -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'.");
|
||||
|
@ -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 = {};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
@ -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 & release.</li>
|
||||
<li> Drag mouse within Target1 & 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>
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user