mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
3cab03a461
@ -33,7 +33,6 @@ scopes:
|
||||
|
||||
tasks:
|
||||
- taskId: '{{#as_slugid}}decision task{{/as_slugid}}'
|
||||
reruns: 3
|
||||
task:
|
||||
created: '{{now}}'
|
||||
deadline: '{{#from_now}}1 day{{/from_now}}'
|
||||
|
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1273625 - Update win32 rustc target to i686
|
||||
Bug 1271829 - Compile nss with SSE2 optimizations
|
||||
|
@ -190,16 +190,7 @@ Accessible::Description(nsString& aDescription)
|
||||
aDescription);
|
||||
|
||||
if (aDescription.IsEmpty()) {
|
||||
bool isXUL = mContent->IsXULElement();
|
||||
if (isXUL) {
|
||||
// Try XUL <description control="[id]">description text</description>
|
||||
XULDescriptionIterator iter(Document(), mContent);
|
||||
Accessible* descr = nullptr;
|
||||
while ((descr = iter.Next())) {
|
||||
nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
|
||||
&aDescription);
|
||||
}
|
||||
}
|
||||
NativeDescription(aDescription);
|
||||
|
||||
if (aDescription.IsEmpty()) {
|
||||
// Keep the Name() method logic.
|
||||
@ -1968,6 +1959,22 @@ Accessible::NativeName(nsString& aName)
|
||||
return eNameOK;
|
||||
}
|
||||
|
||||
// Accessible protected
|
||||
void
|
||||
Accessible::NativeDescription(nsString& aDescription)
|
||||
{
|
||||
bool isXUL = mContent->IsXULElement();
|
||||
if (isXUL) {
|
||||
// Try XUL <description control="[id]">description text</description>
|
||||
XULDescriptionIterator iter(Document(), mContent);
|
||||
Accessible* descr = nullptr;
|
||||
while ((descr = iter.Next())) {
|
||||
nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
|
||||
&aDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Accessible protected
|
||||
void
|
||||
Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
|
||||
|
@ -955,6 +955,12 @@ protected:
|
||||
*/
|
||||
virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName);
|
||||
|
||||
/**
|
||||
* Return the accessible description provided by native markup. It doesn't take
|
||||
* into account ARIA markup used to specify the description.
|
||||
*/
|
||||
virtual void NativeDescription(nsString& aDescription);
|
||||
|
||||
/**
|
||||
* Return object attributes provided by native markup. It doesn't take into
|
||||
* account ARIA.
|
||||
|
@ -414,7 +414,7 @@
|
||||
@RESPATH@/components/PresentationDeviceInfoManager.manifest
|
||||
@RESPATH@/components/PresentationDeviceInfoManager.js
|
||||
@RESPATH@/components/BuiltinProviders.manifest
|
||||
@RESPATH@/components/TCPPresentationServer.js
|
||||
@RESPATH@/components/PresentationControlService.js
|
||||
@RESPATH@/components/PresentationDataChannelSessionTransport.js
|
||||
@RESPATH@/components/PresentationDataChannelSessionTransport.manifest
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1462472009000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1463504139000">
|
||||
<emItems>
|
||||
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
@ -1908,6 +1908,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1214" id="firefoxdav@icloud.com">
|
||||
<versionRange minVersion="0" maxVersion="1.4.22" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i434" id="afurladvisor@anchorfree.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
@ -2523,6 +2529,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i1213" id="unblocker20__web@unblocker.yt">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i342" id="lbmsrvfvxcblvpane@lpaezhjez.org">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
@ -3612,7 +3624,6 @@
|
||||
<device>0x0046</device>
|
||||
</devices>
|
||||
<featureStatus>BLOCKED_DRIVER_VERSION</featureStatus> <driverVersion>8.15.10.2086</driverVersion> <driverVersionComparator>EQUAL</driverVersionComparator> </gfxBlacklistEntry>
|
||||
<gfxBlacklistEntry blockID="g1208"> <os>All</os> <vendor>0x8086</vendor> <feature>HARDWARE_VIDEO_DECODING</feature> <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus> <driverVersion>10.18.10.3947</driverVersion> <driverVersionComparator>EQUAL</driverVersionComparator> </gfxBlacklistEntry>
|
||||
</gfxItems>
|
||||
|
||||
<certItems>
|
||||
|
@ -1277,7 +1277,7 @@ pref("media.eme.apiVisible", true);
|
||||
|
||||
// Decode using Gecko Media Plugins in <video>, if a system decoder is not
|
||||
// availble and the preferred GMP is available.
|
||||
pref("media.gmp.decoder.enabled", true);
|
||||
pref("media.gmp.decoder.enabled", false);
|
||||
|
||||
// If decoding-via-GMP is turned on for <video>, use Adobe's GMP for decoding,
|
||||
// if it's available. Note: We won't fallback to another GMP if Adobe's is not
|
||||
|
@ -1107,6 +1107,12 @@ var gBrowserInit = {
|
||||
"without the remote tabs load context.");
|
||||
}
|
||||
|
||||
// We must set usercontextid before updateBrowserRemoteness()
|
||||
// so that the newly created remote tab child has correct usercontextid
|
||||
if (tabToOpen.hasAttribute("usercontextid")) {
|
||||
let usercontextid = tabToOpen.getAttribute("usercontextid");
|
||||
gBrowser.selectedBrowser.setAttribute("usercontextid", usercontextid);
|
||||
}
|
||||
gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
|
||||
}
|
||||
gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
|
||||
|
@ -874,21 +874,21 @@ RefreshBlocker.init();
|
||||
|
||||
var UserContextIdNotifier = {
|
||||
init() {
|
||||
addEventListener("DOMContentLoaded", this);
|
||||
addEventListener("DOMWindowCreated", this);
|
||||
},
|
||||
|
||||
uninit() {
|
||||
removeEventListener("DOMContentLoaded", this);
|
||||
removeEventListener("DOMWindowCreated", this);
|
||||
},
|
||||
|
||||
handleEvent(aEvent) {
|
||||
// When the first content is loaded, we want to inform the tabbrowser about
|
||||
// When the window is created, we want to inform the tabbrowser about
|
||||
// the userContextId in use in order to update the UI correctly.
|
||||
// Just because we cannot change the userContextId from an active docShell,
|
||||
// we don't need to check DOMContentLoaded again.
|
||||
this.uninit();
|
||||
let userContextId = content.document.nodePrincipal.originAttributes.userContextId;
|
||||
sendAsyncMessage("Browser:FirstContentLoaded", { userContextId });
|
||||
sendAsyncMessage("Browser:WindowCreated", { userContextId });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3058,7 +3058,12 @@
|
||||
// Swap the dropped tab with a new one we create and then close
|
||||
// it in the other window (making it seem to have moved between
|
||||
// windows).
|
||||
let newTab = this.addTab("about:blank", { eventDetail: { adoptedTab: aTab } });
|
||||
let params = { eventDetail: { adoptedTab: aTab } };
|
||||
if (aTab.hasAttribute("usercontextid")) {
|
||||
// new tab must have the same usercontextid as the old one
|
||||
params.userContextId = aTab.getAttribute("usercontextid");
|
||||
}
|
||||
let newTab = this.addTab("about:blank", params);
|
||||
let newBrowser = this.getBrowserForTab(newTab);
|
||||
let newURL = aTab.linkedBrowser.currentURI.spec;
|
||||
|
||||
@ -4317,12 +4322,13 @@
|
||||
browser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned })
|
||||
break;
|
||||
}
|
||||
case "Browser:FirstContentLoaded": {
|
||||
case "Browser:WindowCreated": {
|
||||
let tab = this.getTabForBrowser(browser);
|
||||
if (tab && data.userContextId) {
|
||||
tab.setUserContextId(data.userContextId);
|
||||
}
|
||||
|
||||
updateUserContextUIIndicator(browser);
|
||||
break;
|
||||
}
|
||||
case "Findbar:Keypress": {
|
||||
@ -4471,7 +4477,7 @@
|
||||
messageManager.addMessageListener("DOMWebNotificationClicked", this);
|
||||
messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
|
||||
messageManager.addMessageListener("RefreshBlocker:Blocked", this);
|
||||
messageManager.addMessageListener("Browser:FirstContentLoaded", this);
|
||||
messageManager.addMessageListener("Browser:WindowCreated", this);
|
||||
|
||||
// To correctly handle keypresses for potential FindAsYouType, while
|
||||
// the tab's find bar is not yet initialized.
|
||||
|
@ -1,3 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
// For some about: URLs, they will take more time to load and cause timeout.
|
||||
// See Bug 1270998.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(function* () {
|
||||
let aboutURLs = [];
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
[DEFAULT]
|
||||
qemu = false
|
||||
b2g = false
|
||||
browser = true
|
||||
skip = false
|
||||
run-if = buildapp == 'browser'
|
||||
|
||||
[test_refresh_firefox.py]
|
||||
|
||||
|
@ -380,6 +380,11 @@ BrowserGlue.prototype = {
|
||||
if (win) {
|
||||
data = JSON.parse(data);
|
||||
let where = win.whereToOpenLink(data);
|
||||
// Preserve legacy behavior of non-modifier left-clicks
|
||||
// opening in a new selected tab.
|
||||
if (where == "current") {
|
||||
where = "tab";
|
||||
}
|
||||
win.openUILinkIn(data.href, where);
|
||||
linkHandled.data = true;
|
||||
}
|
||||
|
@ -13,9 +13,7 @@
|
||||
; seems to work.
|
||||
|
||||
[DEFAULT]
|
||||
b2g = false
|
||||
browser = true
|
||||
qemu = false
|
||||
run-if = buildapp == 'browser'
|
||||
|
||||
[chrome/content/shared/test/test_shared_all.py]
|
||||
[chrome/content/panels/test/test_desktop_all.py]
|
||||
|
@ -1,7 +1,5 @@
|
||||
[DEFAULT]
|
||||
b2g = false
|
||||
browser = true
|
||||
qemu = false
|
||||
run-if = buildapp == 'browser'
|
||||
|
||||
[test_1_browser_call.py]
|
||||
[test_2_linkclicker.py]
|
||||
|
@ -569,7 +569,7 @@
|
||||
@RESPATH@/components/PresentationDeviceInfoManager.manifest
|
||||
@RESPATH@/components/PresentationDeviceInfoManager.js
|
||||
@RESPATH@/components/BuiltinProviders.manifest
|
||||
@RESPATH@/components/TCPPresentationServer.js
|
||||
@RESPATH@/components/PresentationControlService.js
|
||||
@RESPATH@/components/PresentationDataChannelSessionTransport.js
|
||||
@RESPATH@/components/PresentationDataChannelSessionTransport.manifest
|
||||
|
||||
|
@ -293,6 +293,30 @@ window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
|
||||
background-color: var(--tab-selection-background-color);
|
||||
}
|
||||
|
||||
.tab-icon-sound[visuallyselected=true][soundplaying] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white");
|
||||
}
|
||||
|
||||
.tab-icon-sound[visuallyselected=true][soundplaying]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-hover");
|
||||
}
|
||||
|
||||
.tab-icon-sound[visuallyselected=true][soundplaying]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-pressed");
|
||||
}
|
||||
|
||||
.tab-icon-sound[visuallyselected=true][muted] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white");
|
||||
}
|
||||
|
||||
.tab-icon-sound[visuallyselected=true][muted]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-hover");
|
||||
}
|
||||
|
||||
.tab-icon-sound[visuallyselected=true][muted]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-pressed");
|
||||
}
|
||||
|
||||
/* Don't need space for the tab curves (66px - 30px) */
|
||||
.tabs-newtab-button {
|
||||
width: 36px;
|
||||
|
@ -66,7 +66,7 @@ p {
|
||||
}
|
||||
|
||||
.list-row > ul > li {
|
||||
float: left;
|
||||
float: inline-start;
|
||||
width: 220px;
|
||||
line-height: 1.5em;
|
||||
margin-inline-start: 1em;
|
||||
@ -84,6 +84,10 @@ p {
|
||||
padding-inline-start: calc(var(--icon-margin) + 24px);
|
||||
}
|
||||
|
||||
.title:dir(rtl) {
|
||||
background-position: right, center;
|
||||
}
|
||||
|
||||
.about-subheader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -97,6 +101,10 @@ p {
|
||||
padding-inline-start: 56px;
|
||||
}
|
||||
|
||||
.about-subheader:dir(rtl) {
|
||||
background-position: right;
|
||||
}
|
||||
|
||||
.about-subheader.tp-off {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing/tracking-protection-off.svg");
|
||||
}
|
||||
@ -106,7 +114,7 @@ p {
|
||||
}
|
||||
|
||||
.tpTitle {
|
||||
margin-right: 12px;
|
||||
margin-inline-end: 12px;
|
||||
}
|
||||
|
||||
.private strong {
|
||||
@ -153,7 +161,7 @@ a.button {
|
||||
}
|
||||
|
||||
.toggle + .toggle-btn::before {
|
||||
float: left;
|
||||
float: inline-start;
|
||||
left: 9px;
|
||||
visibility: hidden;
|
||||
background-size: 16px;
|
||||
@ -162,6 +170,17 @@ a.button {
|
||||
background-image: url("chrome://browser/skin/privatebrowsing/check.svg");
|
||||
}
|
||||
|
||||
.toggle + .toggle-btn:dir(rtl)::after {
|
||||
left: auto;
|
||||
right: 0;
|
||||
transition-property: right;
|
||||
}
|
||||
|
||||
.toggle + .toggle-btn:dir(rtl)::before {
|
||||
left: auto;
|
||||
right: 9px;
|
||||
}
|
||||
|
||||
.toggle:checked + .toggle-btn {
|
||||
background: #3fc455;
|
||||
border: 1px solid #269939;
|
||||
@ -171,6 +190,10 @@ a.button {
|
||||
left: 35px;
|
||||
}
|
||||
|
||||
.toggle:checked + .toggle-btn:dir(rtl)::after {
|
||||
right: 35px;
|
||||
}
|
||||
|
||||
.toggle:checked + .toggle-btn::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
@ -176,26 +176,32 @@
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted-pressed");
|
||||
}
|
||||
|
||||
.tab-icon-sound:-moz-lwtheme,
|
||||
.tab-icon-sound[visuallyselected=true][soundplaying] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
|
||||
}
|
||||
|
||||
.tab-icon-sound:hover:-moz-lwtheme,
|
||||
.tab-icon-sound[visuallyselected=true][soundplaying]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-hover");
|
||||
}
|
||||
|
||||
.tab-icon-sound:hover:active:-moz-lwtheme,
|
||||
.tab-icon-sound[visuallyselected=true][soundplaying]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-pressed");
|
||||
}
|
||||
|
||||
.tab-icon-sound[muted]:-moz-lwtheme,
|
||||
.tab-icon-sound[visuallyselected=true][muted] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
|
||||
}
|
||||
|
||||
.tab-icon-sound[muted]:hover:-moz-lwtheme,
|
||||
.tab-icon-sound[visuallyselected=true][muted]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-hover");
|
||||
}
|
||||
|
||||
.tab-icon-sound[muted]:hover:active:-moz-lwtheme,
|
||||
.tab-icon-sound[visuallyselected=true][muted]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-pressed");
|
||||
}
|
||||
|
@ -551,7 +551,6 @@ BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const PrincipalOriginAttrib
|
||||
bool inheritsPrincipal;
|
||||
nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
||||
&inheritsPrincipal);
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
if (NS_FAILED(rv) || inheritsPrincipal) {
|
||||
return nsNullPrincipal::Create(aAttrs);
|
||||
}
|
||||
|
@ -60,6 +60,3 @@ if CONFIG['ENABLE_TESTS']:
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
|
@ -672,7 +672,7 @@ EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase)
|
||||
}
|
||||
|
||||
nsAutoCString host, newHost;
|
||||
nsresult rv = probe->GetHost(host);
|
||||
rv = probe->GetHost(host);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
rv = tldService->GetNextSubDomain(host, newHost);
|
||||
@ -850,8 +850,6 @@ nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
|
||||
// as long as they don't represent null principals...
|
||||
// Or they don't require an special permission to do so
|
||||
// See bug#773886
|
||||
|
||||
bool hasFlags;
|
||||
rv = NS_URIChainHasFlags(targetBaseURI,
|
||||
nsIProtocolHandler::URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
|
||||
&hasFlags);
|
||||
|
@ -74,7 +74,7 @@ nsSystemPrincipal::EnsureCSP(nsIDOMDocument* aDocument,
|
||||
nsIContentSecurityPolicy** aCSP)
|
||||
{
|
||||
// CSP on a system principal makes no sense
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -76,7 +76,7 @@ nsChromeProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
// Chrome: URLs (currently) have no additional structure beyond that provided
|
||||
// by standard URLs, so there is no "outer" given to CreateInstance
|
||||
|
||||
RefPtr<nsStandardURL> surl = new nsStandardURL();
|
||||
RefPtr<mozilla::net::nsStandardURL> surl = new mozilla::net::nsStandardURL();
|
||||
|
||||
nsresult rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec,
|
||||
aCharset, aBaseURI);
|
||||
|
14
config/external/nss/Makefile.in
vendored
14
config/external/nss/Makefile.in
vendored
@ -245,7 +245,7 @@ ifdef MOZ_NO_WLZDEFS
|
||||
DEFAULT_GMAKE_FLAGS += ZDEFS_FLAG=
|
||||
endif
|
||||
ifdef MOZ_CFLAGS_NSS
|
||||
DEFAULT_GMAKE_FLAGS += XCFLAGS='$(filter-out -W%,$(CFLAGS))'
|
||||
XCFLAGS += $(filter-out -W%,$(CFLAGS))
|
||||
DEFAULT_GMAKE_FLAGS += DARWIN_DYLIB_VERSIONS='-compatibility_version 1 -current_version 1 $(LDFLAGS)'
|
||||
endif
|
||||
ifeq (1_1,$(CLANG_CL)_$(MOZ_ASAN))
|
||||
@ -268,9 +268,19 @@ DEFAULT_GMAKE_FLAGS += MAKE_OBJDIR='$$(INSTALL) -D $$(OBJDIR)'
|
||||
DEFAULT_GMAKE_FLAGS += TARGETS='$$(LIBRARY) $$(SHARED_LIBRARY) $$(PROGRAM)'
|
||||
|
||||
ifdef MOZ_FOLD_LIBS_FLAGS
|
||||
DEFAULT_GMAKE_FLAGS += XCFLAGS='$(MOZ_FOLD_LIBS_FLAGS)'
|
||||
XCFLAGS += $(MOZ_FOLD_LIBS_FLAGS)
|
||||
endif
|
||||
|
||||
# Pass on the MSVC target arch from the main build system.
|
||||
# Note this is case- and switch-character sensitive, while
|
||||
# the MSVC option is not.
|
||||
ifeq (WINNT,$(OS_TARGET))
|
||||
XCFLAGS += $(filter -arch:%,$(CFLAGS))
|
||||
endif
|
||||
|
||||
# Export accumulated XCFLAGS to modify nss defaults.
|
||||
DEFAULT_GMAKE_FLAGS += XCFLAGS='$(XCFLAGS)'
|
||||
|
||||
NSS_SRCDIR = $(topsrcdir)
|
||||
|
||||
NSS_DIRS =
|
||||
|
@ -17,21 +17,34 @@
|
||||
// Silence "warning: #include_next is a GCC extension"
|
||||
#pragma GCC system_header
|
||||
|
||||
#if defined(DEBUG) && !defined(_GLIBCXX_DEBUG)
|
||||
// Enable checked iterators and other goodies
|
||||
//
|
||||
// FIXME/bug 551254: gcc's debug STL implementation requires -frtti.
|
||||
// Figure out how to resolve this with -fno-rtti. Maybe build with
|
||||
// -frtti in DEBUG builds?
|
||||
//
|
||||
// # define _GLIBCXX_DEBUG 1
|
||||
#endif
|
||||
|
||||
// Don't include mozalloc for cstdlib. See bug 1245076.
|
||||
#ifndef moz_dont_include_mozalloc_for_cstdlib
|
||||
# define moz_dont_include_mozalloc_for_cstdlib
|
||||
#endif
|
||||
#ifndef moz_dont_include_mozalloc_for_${HEADER}
|
||||
// mozalloc.h wants <new>; break the cycle by always explicitly
|
||||
// including <new> here. NB: this is a tad sneaky. Sez the gcc docs:
|
||||
//
|
||||
// `#include_next' does not distinguish between <file> and "file"
|
||||
// inclusion, nor does it check that the file you specify has the
|
||||
// same name as the current file. It simply looks for the file
|
||||
// named, starting with the directory in the search path after the
|
||||
// one where the current file was found.
|
||||
# include_next <new>
|
||||
|
||||
// Include mozalloc after the STL header and all other headers it includes
|
||||
// have been preprocessed.
|
||||
#if !defined(MOZ_INCLUDE_MOZALLOC_H) && \
|
||||
!defined(moz_dont_include_mozalloc_for_${HEADER})
|
||||
# define MOZ_INCLUDE_MOZALLOC_H
|
||||
# define MOZ_INCLUDE_MOZALLOC_H_FROM_${HEADER}
|
||||
#endif
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
#include_next <${HEADER}>
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#ifdef MOZ_INCLUDE_MOZALLOC_H_FROM_${HEADER}
|
||||
// See if we're in code that can use mozalloc. NB: this duplicates
|
||||
// code in nscore.h because nscore.h pulls in prtypes.h, and chromium
|
||||
// can't build with that being included before base/basictypes.h.
|
||||
@ -40,21 +53,8 @@
|
||||
# else
|
||||
# error "STL code can only be used with infallible ::operator new()"
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
// Don't enable debug mode with the clang plugin; clang doesn't recognize
|
||||
// the debug/ versions of the stdlib headers as being system headers, leading
|
||||
// to complaints about code that's out of our control.
|
||||
#if defined(DEBUG) && !defined(_GLIBCXX_DEBUG) && !defined(MOZ_CLANG_PLUGIN)
|
||||
// Enable checked iterators and other goodies
|
||||
# define _GLIBCXX_DEBUG 1
|
||||
#endif
|
||||
|
||||
#pragma GCC visibility push(default)
|
||||
#include_next <${HEADER}>
|
||||
#pragma GCC visibility pop
|
||||
|
||||
// gcc calls a __throw_*() function from bits/functexcept.h when it
|
||||
// wants to "throw an exception". functexcept exists nominally to
|
||||
// support -fno-exceptions, but since we'll always use the system
|
||||
|
@ -30,7 +30,6 @@ def main(outdir, compiler, template_file, header_list_file):
|
||||
os.mkdir(outdir)
|
||||
|
||||
template = open(template_file, 'r').read()
|
||||
path_to_new = header_path('new', compiler)
|
||||
|
||||
for header in open(header_list_file, 'r'):
|
||||
header = header.rstrip()
|
||||
@ -40,8 +39,7 @@ def main(outdir, compiler, template_file, header_list_file):
|
||||
path = header_path(header, compiler)
|
||||
with FileAvoidWrite(os.path.join(outdir, header)) as f:
|
||||
f.write(string.Template(template).substitute(HEADER=header,
|
||||
HEADER_PATH=path,
|
||||
NEW_HEADER_PATH=path_to_new))
|
||||
HEADER_PATH=path))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -8,35 +8,23 @@
|
||||
#ifndef mozilla_${HEADER}_h
|
||||
#define mozilla_${HEADER}_h
|
||||
|
||||
#ifndef MOZ_HAVE_INCLUDED_ALLOC
|
||||
#define MOZ_HAVE_INCLUDED_ALLOC
|
||||
|
||||
#if _HAS_EXCEPTIONS
|
||||
# error "STL code can only be used with -fno-exceptions"
|
||||
#endif
|
||||
|
||||
// Include mozalloc after the STL header and all other headers it includes
|
||||
// have been preprocessed.
|
||||
#if !defined(MOZ_INCLUDE_MOZALLOC_H)
|
||||
# define MOZ_INCLUDE_MOZALLOC_H
|
||||
# define MOZ_INCLUDE_MOZALLOC_H_FROM_${HEADER}
|
||||
#endif
|
||||
|
||||
// Code built with !_HAS_EXCEPTIONS calls std::_Throw(), but the win2k
|
||||
// CRT doesn't export std::_Throw(). So we define it.
|
||||
#ifndef mozilla_Throw_h
|
||||
# include "mozilla/throw_msvc.h"
|
||||
#endif
|
||||
|
||||
// Code might include <new> before other wrapped headers, but <new>
|
||||
// includes <exception> and so we want to wrap it. But mozalloc.h
|
||||
// wants <new> also, so we break the cycle by always explicitly
|
||||
// including <new> here.
|
||||
#include <${NEW_HEADER_PATH}>
|
||||
|
||||
// See if we're in code that can use mozalloc. NB: this duplicates
|
||||
// code in nscore.h because nscore.h pulls in prtypes.h, and chromium
|
||||
// can't build with that being included before base/basictypes.h.
|
||||
#if !defined(XPCOM_GLUE) && !defined(NS_NO_XPCOM) && !defined(MOZ_NO_MOZALLOC)
|
||||
# include "mozilla/mozalloc.h"
|
||||
#else
|
||||
# error "STL code can only be used with infallible ::operator new()"
|
||||
#endif
|
||||
#endif /* MOZ_HAVE_INCLUDED_ALLOC */
|
||||
|
||||
#ifdef _DEBUG
|
||||
// From
|
||||
// http://msdn.microsoft.com/en-us/library/aa985982%28VS.80%29.aspx
|
||||
@ -75,4 +63,15 @@
|
||||
|
||||
#pragma warning( pop )
|
||||
|
||||
#ifdef MOZ_INCLUDE_MOZALLOC_H_FROM_${HEADER}
|
||||
// See if we're in code that can use mozalloc. NB: this duplicates
|
||||
// code in nscore.h because nscore.h pulls in prtypes.h, and chromium
|
||||
// can't build with that being included before base/basictypes.h.
|
||||
# if !defined(XPCOM_GLUE) && !defined(NS_NO_XPCOM) && !defined(MOZ_NO_MOZALLOC)
|
||||
# include "mozilla/mozalloc.h"
|
||||
# else
|
||||
# error "STL code can only be used with infallible ::operator new()"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif // if mozilla_${HEADER}_h
|
||||
|
@ -77,8 +77,9 @@ function hasExpectedProperties(containerEl) {
|
||||
function hasExpectedWarnings(containerEl) {
|
||||
let warnings = [...containerEl.querySelectorAll(".warning")];
|
||||
for (let warning of warnings) {
|
||||
if (warning.getAttribute("title") ==
|
||||
L10N.getStr("AnimationWarningTransformWithGeometricProperties")) {
|
||||
let warningID =
|
||||
"CompositorAnimationWarningTransformWithGeometricProperties";
|
||||
if (warning.getAttribute("title") == L10N.getStr(warningID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -131,11 +131,15 @@ function MarkupView(inspector, frame, controllerWindow) {
|
||||
this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
|
||||
this._onCollapseAttributesPrefChange =
|
||||
this._onCollapseAttributesPrefChange.bind(this);
|
||||
this._onBlur = this._onBlur.bind(this);
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
// Listening to various events.
|
||||
this._elt.addEventListener("click", this._onMouseClick, false);
|
||||
this._elt.addEventListener("mousemove", this._onMouseMove, false);
|
||||
this._elt.addEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._elt.addEventListener("blur", this._onBlur, true);
|
||||
this.win.addEventListener("mouseup", this._onMouseUp);
|
||||
this.win.addEventListener("copy", this._onCopy);
|
||||
this._frame.addEventListener("focus", this._onFocus, false);
|
||||
@ -154,8 +158,6 @@ function MarkupView(inspector, frame, controllerWindow) {
|
||||
this._onCollapseAttributesPrefChange);
|
||||
|
||||
this._initShortcuts();
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
MarkupView.prototype = {
|
||||
@ -220,6 +222,25 @@ MarkupView.prototype = {
|
||||
this.emit("node-hover");
|
||||
},
|
||||
|
||||
/**
|
||||
* If focus is moved outside of the markup view document and there is a
|
||||
* selected container, make its contents not focusable by a keyboard.
|
||||
*/
|
||||
_onBlur: function (event) {
|
||||
if (!this._selectedContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
let {relatedTarget} = event;
|
||||
if (relatedTarget && relatedTarget.ownerDocument === this.doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._selectedContainer) {
|
||||
this._selectedContainer.clearFocus();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Executed on each mouse-move while a node is being dragged in the view.
|
||||
* Auto-scrolls the view to reveal nodes below the fold to drop the dragged
|
||||
@ -541,8 +562,8 @@ MarkupView.prototype = {
|
||||
// Mark the node as selected.
|
||||
this.markNodeAsSelected(selection.nodeFront);
|
||||
|
||||
// Make sure the new selection receives focus so the keyboard can be used.
|
||||
this.maybeFocusNewSelection();
|
||||
// Make sure the new selection is navigated to.
|
||||
this.maybeNavigateToNewSelection();
|
||||
return undefined;
|
||||
}).catch(e => {
|
||||
if (!this._destroyer) {
|
||||
@ -557,14 +578,14 @@ MarkupView.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe focus the current node selection's MarkupContainer depending on why
|
||||
* the current node got selected.
|
||||
* Maybe make selected the current node selection's MarkupContainer depending
|
||||
* on why the current node got selected.
|
||||
*/
|
||||
maybeFocusNewSelection: function () {
|
||||
maybeNavigateToNewSelection: function () {
|
||||
let {reason, nodeFront} = this._inspector.selection;
|
||||
|
||||
// The list of reasons that should lead to focusing the node.
|
||||
let reasonsToFocus = [
|
||||
// The list of reasons that should lead to navigating to the node.
|
||||
let reasonsToNavigate = [
|
||||
// If the user picked an element with the element picker.
|
||||
"picker-node-picked",
|
||||
// If the user selected an element with the browser context menu.
|
||||
@ -573,8 +594,9 @@ MarkupView.prototype = {
|
||||
"node-inserted"
|
||||
];
|
||||
|
||||
if (reasonsToFocus.includes(reason)) {
|
||||
this.getContainer(nodeFront).focus();
|
||||
if (reasonsToNavigate.includes(reason)) {
|
||||
this.getContainer(this._rootNode).elt.focus();
|
||||
this.navigate(this.getContainer(nodeFront));
|
||||
}
|
||||
},
|
||||
|
||||
@ -633,7 +655,7 @@ MarkupView.prototype = {
|
||||
|
||||
// Process generic keys:
|
||||
["Delete", "Backspace", "Home", "Left", "Right", "Up", "Down", "PageUp",
|
||||
"PageDown", "Esc"].forEach(key => {
|
||||
"PageDown", "Esc", "Enter", "Space"].forEach(key => {
|
||||
shortcuts.on(key, this._onShortcut);
|
||||
});
|
||||
},
|
||||
@ -742,6 +764,17 @@ MarkupView.prototype = {
|
||||
this.navigate(selection);
|
||||
break;
|
||||
}
|
||||
case "Enter":
|
||||
case "Space": {
|
||||
if (!this._selectedContainer.canFocus) {
|
||||
this._selectedContainer.canFocus = true;
|
||||
this._selectedContainer.focus();
|
||||
} else {
|
||||
// Return early to prevent cancelling the event.
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Esc": {
|
||||
if (this.isDragging) {
|
||||
this.cancelDragging();
|
||||
@ -842,7 +875,7 @@ MarkupView.prototype = {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
if (parent) {
|
||||
this.navigate(parent.container, true);
|
||||
this.navigate(parent.container);
|
||||
}
|
||||
},
|
||||
|
||||
@ -852,20 +885,14 @@ MarkupView.prototype = {
|
||||
*
|
||||
* @param {MarkupContainer} container
|
||||
* The container we're navigating to.
|
||||
* @param {Boolean} ignoreFocus
|
||||
* If false, keyboard focus will be moved to the container too.
|
||||
*/
|
||||
navigate: function (container, ignoreFocus) {
|
||||
navigate: function (container) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
let node = container.node;
|
||||
this.markNodeAsSelected(node, "treepanel");
|
||||
|
||||
if (!ignoreFocus) {
|
||||
container.focus();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -944,8 +971,11 @@ MarkupView.prototype = {
|
||||
container.update();
|
||||
} else if (type === "childList" || type === "nativeAnonymousChildList") {
|
||||
container.childrenDirty = true;
|
||||
// Update the children to take care of changes in the markup view DOM.
|
||||
this._updateChildren(container, {flash: true});
|
||||
// Update the children to take care of changes in the markup view DOM
|
||||
// and update container (and its subtree) DOM tree depth level for
|
||||
// accessibility where necessary.
|
||||
this._updateChildren(container, {flash: true}).then(() =>
|
||||
container.updateLevel());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1387,13 +1417,15 @@ MarkupView.prototype = {
|
||||
*/
|
||||
markNodeAsSelected: function (node, reason) {
|
||||
let container = this.getContainer(node);
|
||||
|
||||
if (this._selectedContainer === container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Un-select the previous container.
|
||||
// Un-select and remove focus from the previous container.
|
||||
if (this._selectedContainer) {
|
||||
this._selectedContainer.selected = false;
|
||||
this._selectedContainer.clearFocus();
|
||||
}
|
||||
|
||||
// Select the new container.
|
||||
@ -1488,6 +1520,9 @@ MarkupView.prototype = {
|
||||
let flash = options && options.flash;
|
||||
|
||||
container.hasChildren = container.node.hasChildren;
|
||||
// Accessibility should either ignore empty children or semantically
|
||||
// consider them a group.
|
||||
container.setChildrenRole();
|
||||
|
||||
if (!this._queuedChildUpdates) {
|
||||
this._queuedChildUpdates = new Map();
|
||||
@ -1656,6 +1691,7 @@ MarkupView.prototype = {
|
||||
this._elt.removeEventListener("click", this._onMouseClick, false);
|
||||
this._elt.removeEventListener("mousemove", this._onMouseMove, false);
|
||||
this._elt.removeEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._elt.removeEventListener("blur", this._onBlur, true);
|
||||
this.win.removeEventListener("mouseup", this._onMouseUp);
|
||||
this.win.removeEventListener("copy", this._onCopy);
|
||||
this._frame.removeEventListener("focus", this._onFocus, false);
|
||||
@ -1793,6 +1829,12 @@ MarkupView.prototype = {
|
||||
*/
|
||||
function MarkupContainer() { }
|
||||
|
||||
/**
|
||||
* Unique identifier used to set markup container node id.
|
||||
* @type {Number}
|
||||
*/
|
||||
let markupContainerID = 0;
|
||||
|
||||
MarkupContainer.prototype = {
|
||||
/*
|
||||
* Initialize the MarkupContainer. Should be called while one
|
||||
@ -1810,6 +1852,7 @@ MarkupContainer.prototype = {
|
||||
this.node = node;
|
||||
this.undo = this.markup.undo;
|
||||
this.win = this.markup._frame.contentWindow;
|
||||
this.id = "treeitem-" + markupContainerID++;
|
||||
|
||||
// The template will fill the following properties
|
||||
this.elt = null;
|
||||
@ -1824,6 +1867,7 @@ MarkupContainer.prototype = {
|
||||
this._onToggle = this._onToggle.bind(this);
|
||||
this._onMouseUp = this._onMouseUp.bind(this);
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
this._onKeyDown = this._onKeyDown.bind(this);
|
||||
|
||||
// Binding event listeners
|
||||
this.elt.addEventListener("mousedown", this._onMouseDown, false);
|
||||
@ -1880,6 +1924,67 @@ MarkupContainer.prototype = {
|
||||
this.updateExpander();
|
||||
},
|
||||
|
||||
/**
|
||||
* A list of all elements with tabindex that are not in container's children.
|
||||
*/
|
||||
get focusableElms() {
|
||||
return [...this.tagLine.querySelectorAll("[tabindex]")];
|
||||
},
|
||||
|
||||
/**
|
||||
* An indicator that the container internals are focusable.
|
||||
*/
|
||||
get canFocus() {
|
||||
return this._canFocus;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle focusable state for container internals.
|
||||
*/
|
||||
set canFocus(value) {
|
||||
if (this._canFocus === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._canFocus = value;
|
||||
|
||||
if (value) {
|
||||
this.tagLine.addEventListener("keydown", this._onKeyDown, true);
|
||||
this.focusableElms.forEach(elm => elm.setAttribute("tabindex", "0"));
|
||||
} else {
|
||||
this.tagLine.removeEventListener("keydown", this._onKeyDown, true);
|
||||
// Exclude from tab order.
|
||||
this.focusableElms.forEach(elm => elm.setAttribute("tabindex", "-1"));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If conatiner and its contents are focusable, exclude them from tab order,
|
||||
* and, if necessary, remove focus.
|
||||
*/
|
||||
clearFocus: function () {
|
||||
if (!this.canFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.canFocus = false;
|
||||
let doc = this.markup.doc;
|
||||
|
||||
if (!doc.activeElement || doc.activeElement === doc.body) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parent = doc.activeElement;
|
||||
|
||||
while (parent && parent !== this.elt) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
doc.activeElement.blur();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* True if the current node can be expanded.
|
||||
*/
|
||||
@ -1894,15 +1999,55 @@ MarkupContainer.prototype = {
|
||||
return this.node._parent === this.markup.walker.rootNode;
|
||||
},
|
||||
|
||||
/**
|
||||
* True if current node can be expanded and collapsed.
|
||||
*/
|
||||
get showExpander() {
|
||||
return this.canExpand && !this.mustExpand;
|
||||
},
|
||||
|
||||
updateExpander: function () {
|
||||
if (!this.expander) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.canExpand && !this.mustExpand) {
|
||||
if (this.showExpander) {
|
||||
this.expander.style.visibility = "visible";
|
||||
// Update accessibility expanded state.
|
||||
this.tagLine.setAttribute("aria-expanded", this.expanded);
|
||||
} else {
|
||||
this.expander.style.visibility = "hidden";
|
||||
// No need for accessible expanded state indicator when expander is not
|
||||
// shown.
|
||||
this.tagLine.removeAttribute("aria-expanded");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If current node has no children, ignore them. Otherwise, consider them a
|
||||
* group from the accessibility point of view.
|
||||
*/
|
||||
setChildrenRole: function () {
|
||||
this.children.setAttribute("role",
|
||||
this.hasChildren ? "group" : "presentation");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set an appropriate DOM tree depth level for a node and its subtree.
|
||||
*/
|
||||
updateLevel: function () {
|
||||
// ARIA level should already be set when container template is rendered.
|
||||
let currentLevel = this.tagLine.getAttribute("aria-level");
|
||||
let newLevel = this.level;
|
||||
if (currentLevel === newLevel) {
|
||||
// If level did not change, ignore this node and its subtree.
|
||||
return;
|
||||
}
|
||||
|
||||
this.tagLine.setAttribute("aria-level", newLevel);
|
||||
let childContainers = this.getChildContainers();
|
||||
if (childContainers) {
|
||||
childContainers.forEach(container => container.updateLevel());
|
||||
}
|
||||
},
|
||||
|
||||
@ -1945,6 +2090,8 @@ MarkupContainer.prototype = {
|
||||
if (!this.closeTagLine) {
|
||||
let line = this.markup.doc.createElement("div");
|
||||
line.classList.add("tag-line");
|
||||
// Closing tag is not important for accessibility.
|
||||
line.setAttribute("role", "presentation");
|
||||
|
||||
let tagState = this.markup.doc.createElement("div");
|
||||
tagState.classList.add("tag-state");
|
||||
@ -1961,6 +2108,7 @@ MarkupContainer.prototype = {
|
||||
this.elt.classList.remove("collapsed");
|
||||
this.expander.setAttribute("open", "");
|
||||
this.hovered = false;
|
||||
this.markup.emit("expanded");
|
||||
} else if (!value) {
|
||||
if (this.closeTagLine) {
|
||||
this.elt.removeChild(this.closeTagLine);
|
||||
@ -1968,6 +2116,10 @@ MarkupContainer.prototype = {
|
||||
}
|
||||
this.elt.classList.add("collapsed");
|
||||
this.expander.removeAttribute("open");
|
||||
this.markup.emit("collapsed");
|
||||
}
|
||||
if (this.showExpander) {
|
||||
this.tagLine.setAttribute("aria-expanded", this.expanded);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1975,19 +2127,37 @@ MarkupContainer.prototype = {
|
||||
return this.elt.parentNode ? this.elt.parentNode.container : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine tree depth level of a given node. This is used to specify ARIA
|
||||
* level for node tree items and to give them better semantic context.
|
||||
*/
|
||||
get level() {
|
||||
let level = 1;
|
||||
let parent = this.node.parentNode();
|
||||
while (parent && parent !== this.markup.walker.rootNode) {
|
||||
level++;
|
||||
parent = parent.parentNode();
|
||||
}
|
||||
return level;
|
||||
},
|
||||
|
||||
_isDragging: false,
|
||||
_dragStartY: 0,
|
||||
|
||||
set isDragging(isDragging) {
|
||||
let rootElt = this.markup.getContainer(this.markup._rootNode).elt;
|
||||
this._isDragging = isDragging;
|
||||
this.markup.isDragging = isDragging;
|
||||
this.tagLine.setAttribute("aria-grabbed", isDragging);
|
||||
|
||||
if (isDragging) {
|
||||
this.elt.classList.add("dragging");
|
||||
this.markup.doc.body.classList.add("dragging");
|
||||
rootElt.setAttribute("aria-dropeffect", "move");
|
||||
} else {
|
||||
this.elt.classList.remove("dragging");
|
||||
this.markup.doc.body.classList.remove("dragging");
|
||||
rootElt.setAttribute("aria-dropeffect", "none");
|
||||
}
|
||||
},
|
||||
|
||||
@ -2010,6 +2180,77 @@ MarkupContainer.prototype = {
|
||||
this.node.parentNode().tagName !== null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Move keyboard focus to a next/previous focusable element inside container
|
||||
* that is not part of its children (only if current focus is on first or last
|
||||
* element).
|
||||
*
|
||||
* @param {DOMNode} current currently focused element
|
||||
* @param {Boolean} back direction
|
||||
* @return {DOMNode} newly focused element if any
|
||||
*/
|
||||
_wrapMoveFocus: function (current, back) {
|
||||
let elms = this.focusableElms;
|
||||
let next;
|
||||
if (back) {
|
||||
if (elms.indexOf(current) === 0) {
|
||||
next = elms[elms.length - 1];
|
||||
next.focus();
|
||||
}
|
||||
} else if (elms.indexOf(current) === elms.length - 1) {
|
||||
next = elms[0];
|
||||
next.focus();
|
||||
}
|
||||
return next;
|
||||
},
|
||||
|
||||
_onKeyDown: function (event) {
|
||||
let {target, keyCode, shiftKey} = event;
|
||||
let isInput = this.markup._isInputOrTextarea(target);
|
||||
|
||||
// Ignore all keystrokes that originated in editors except for when 'Tab' is
|
||||
// pressed.
|
||||
if (isInput && keyCode !== event.DOM_VK_TAB) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case event.DOM_VK_TAB:
|
||||
// Only handle 'Tab' if tabbable element is on the edge (first or last).
|
||||
if (isInput) {
|
||||
// Corresponding tabbable element is editor's next sibling.
|
||||
let next = this._wrapMoveFocus(target.nextSibling, shiftKey);
|
||||
if (next) {
|
||||
event.preventDefault();
|
||||
// Keep the editing state if possible.
|
||||
if (next._editable) {
|
||||
let e = this.markup.doc.createEvent("Event");
|
||||
e.initEvent(next._trigger, true, true);
|
||||
next.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let next = this._wrapMoveFocus(target, shiftKey);
|
||||
if (next) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case event.DOM_VK_ESCAPE:
|
||||
this.clearFocus();
|
||||
this.markup.getContainer(this.markup._rootNode).elt.focus();
|
||||
if (this.isDragging) {
|
||||
// Escape when dragging is handled by markup view itself.
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
_onMouseDown: function (event) {
|
||||
let {target, button, metaKey, ctrlKey} = event;
|
||||
let isLeftClick = button === 0;
|
||||
@ -2024,6 +2265,9 @@ MarkupContainer.prototype = {
|
||||
// target is the MarkupContainer itself.
|
||||
this.hovered = false;
|
||||
this.markup.navigate(this);
|
||||
// Make container tabbable descendants tabbable and focus in.
|
||||
this.canFocus = true;
|
||||
this.focus();
|
||||
event.stopPropagation();
|
||||
|
||||
// Preventing the default behavior will avoid the body to gain focus on
|
||||
@ -2039,6 +2283,8 @@ MarkupContainer.prototype = {
|
||||
if (isMiddleClick || isMetaClick) {
|
||||
let link = target.dataset.link;
|
||||
let type = target.dataset.type;
|
||||
// Make container tabbable descendants not tabbable (by default).
|
||||
this.canFocus = false;
|
||||
this.markup._inspector.followAttributeLink(type, link);
|
||||
return;
|
||||
}
|
||||
@ -2182,7 +2428,11 @@ MarkupContainer.prototype = {
|
||||
this.tagState.classList.remove("flash-out");
|
||||
this._selected = value;
|
||||
this.editor.selected = value;
|
||||
// Markup tree item should have accessible selected state.
|
||||
this.tagLine.setAttribute("aria-selected", value);
|
||||
if (this._selected) {
|
||||
this.markup.getContainer(this.markup._rootNode).elt.setAttribute(
|
||||
"aria-activedescendant", this.id);
|
||||
this.tagLine.setAttribute("selected", "");
|
||||
this.tagState.classList.add("theme-selected");
|
||||
} else {
|
||||
@ -2211,7 +2461,8 @@ MarkupContainer.prototype = {
|
||||
* Try to put keyboard focus on the current editor.
|
||||
*/
|
||||
focus: function () {
|
||||
let focusable = this.editor.elt.querySelector("[tabindex]");
|
||||
// Elements with tabindex of -1 are not focusable.
|
||||
let focusable = this.editor.elt.querySelector("[tabindex='0']");
|
||||
if (focusable) {
|
||||
focusable.focus();
|
||||
}
|
||||
@ -2233,6 +2484,7 @@ MarkupContainer.prototype = {
|
||||
// Remove event listeners
|
||||
this.elt.removeEventListener("mousedown", this._onMouseDown, false);
|
||||
this.elt.removeEventListener("dblclick", this._onToggle, false);
|
||||
this.tagLine.removeEventListener("keydown", this._onKeyDown, true);
|
||||
if (this.win) {
|
||||
this.win.removeEventListener("mouseup", this._onMouseUp, true);
|
||||
this.win.removeEventListener("mousemove", this._onMouseMove, true);
|
||||
@ -2490,6 +2742,10 @@ MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
|
||||
function RootContainer(markupView, node) {
|
||||
this.doc = markupView.doc;
|
||||
this.elt = this.doc.createElement("ul");
|
||||
// Root container has tree semantics for accessibility.
|
||||
this.elt.setAttribute("role", "tree");
|
||||
this.elt.setAttribute("tabindex", "0");
|
||||
this.elt.setAttribute("aria-dropeffect", "none");
|
||||
this.elt.container = this;
|
||||
this.children = this.elt;
|
||||
this.node = node;
|
||||
@ -2514,7 +2770,17 @@ RootContainer.prototype = {
|
||||
* Set the expanded state of the container node.
|
||||
* @param {Boolean} value
|
||||
*/
|
||||
setExpanded: function () {}
|
||||
setExpanded: function () {},
|
||||
|
||||
/**
|
||||
* Set an appropriate role of the container's children node.
|
||||
*/
|
||||
setChildrenRole: function () {},
|
||||
|
||||
/**
|
||||
* Set an appropriate DOM tree depth level for a node and its subtree.
|
||||
*/
|
||||
updateLevel: function () {}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2680,7 +2946,8 @@ function ElementEditor(container, node) {
|
||||
// Make the tag name editable (unless this is a remote node or
|
||||
// a document element)
|
||||
if (!node.isDocumentElement) {
|
||||
this.tag.setAttribute("tabindex", "0");
|
||||
// Make the tag optionally tabbable but not by default.
|
||||
this.tag.setAttribute("tabindex", "-1");
|
||||
editableField({
|
||||
element: this.tag,
|
||||
trigger: "dblclick",
|
||||
|
@ -19,31 +19,31 @@
|
||||
<!-- NOTE THAT WE MAKE EXTENSIVE USE OF HTML COMMENTS IN THIS FILE IN ORDER -->
|
||||
<!-- TO MAKE SPANS READABLE WHILST AVOIDING SIGNIFICANT WHITESPACE -->
|
||||
|
||||
<div id="root-wrapper">
|
||||
<div id="root"></div>
|
||||
<div id="root-wrapper" role="presentation">
|
||||
<div id="root" role="presentation"></div>
|
||||
</div>
|
||||
<div id="templates" style="display:none">
|
||||
|
||||
<ul class="children">
|
||||
<li id="template-elementcontainer" save="${elt}" class="child collapsed">
|
||||
<div save="${tagLine}" class="tag-line"><!--
|
||||
--><span save="${tagState}" class="tag-state"></span><!--
|
||||
--><span save="${expander}" class="theme-twisty expander"></span><!--
|
||||
<li id="template-elementcontainer" save="${elt}" class="child collapsed" role="presentation">
|
||||
<div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><!--
|
||||
--><span save="${tagState}" class="tag-state" role="presentation"></span><!--
|
||||
--><span save="${expander}" class="theme-twisty expander" role="presentation"></span><!--
|
||||
--></div>
|
||||
<ul save="${children}" class="children"></ul>
|
||||
<ul save="${children}" class="children" role="group"></ul>
|
||||
</li>
|
||||
|
||||
<li id="template-textcontainer" save="${elt}" class="child collapsed">
|
||||
<div save="${tagLine}" class="tag-line"><span save="${tagState}" class="tag-state"></span></div>
|
||||
<ul save="${children}" class="children"></ul>
|
||||
<li id="template-textcontainer" save="${elt}" class="child collapsed" role="presentation">
|
||||
<div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><span save="${tagState}" class="tag-state" role="presentation"></span></div>
|
||||
<ul save="${children}" class="children" role="group"></ul>
|
||||
</li>
|
||||
|
||||
<li id="template-readonlycontainer" save="${elt}" class="child collapsed">
|
||||
<div save="${tagLine}" class="tag-line"><!--
|
||||
--><span save="${tagState}" class="tag-state"></span><!--
|
||||
--><span save="${expander}" class="theme-twisty expander"></span><!--
|
||||
<li id="template-readonlycontainer" save="${elt}" class="child collapsed" role="presentation">
|
||||
<div save="${tagLine}" id="${id}" class="tag-line" role="treeitem" aria-level="${level}" aria-grabbed="${isDragging}"><!--
|
||||
--><span save="${tagState}" class="tag-state" role="presentation"></span><!--
|
||||
--><span save="${expander}" class="theme-twisty expander" role="presentation"></span><!--
|
||||
--></div>
|
||||
<ul save="${children}" class="children"></ul>
|
||||
<ul save="${children}" class="children" role="group"></ul>
|
||||
</li>
|
||||
|
||||
<li id="template-more-nodes"
|
||||
@ -58,9 +58,9 @@
|
||||
|
||||
<span id="template-element" save="${elt}" class="editor"><!--
|
||||
--><span class="open"><<!--
|
||||
--><span save="${tag}" class="tag theme-fg-color3" tabindex="0"></span><!--
|
||||
--><span save="${tag}" class="tag theme-fg-color3" tabindex="-1"></span><!--
|
||||
--><span save="${attrList}"></span><!--
|
||||
--><span save="${newAttr}" class="newattr" tabindex="0"></span><!--
|
||||
--><span save="${newAttr}" class="newattr" tabindex="-1"></span><!--
|
||||
--><span class="closing-bracket">></span><!--
|
||||
--></span><!--
|
||||
--><span class="close"></<!--
|
||||
@ -76,7 +76,7 @@
|
||||
data-value="${attrValue}"
|
||||
class="attreditor"
|
||||
style="display:none"> <!--
|
||||
--><span class="editable" save="${inner}" tabindex="0"><!--
|
||||
--><span class="editable" save="${inner}" tabindex="-1"><!--
|
||||
--><span save="${name}" class="attr-name theme-fg-color2"></span><!--
|
||||
-->="<!--
|
||||
--><span save="${val}" class="attr-value theme-fg-color6"></span><!--
|
||||
@ -85,14 +85,14 @@
|
||||
--></span>
|
||||
|
||||
<span id="template-text" save="${elt}" class="editor text"><!--
|
||||
--><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="0"></pre><!--
|
||||
--><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="-1"></pre><!--
|
||||
--></span>
|
||||
|
||||
<span id="template-comment"
|
||||
save="${elt}"
|
||||
class="editor comment theme-comment"><!--
|
||||
--><span><!--</span><!--
|
||||
--><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="0"></pre><!--
|
||||
--><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="-1"></pre><!--
|
||||
--><span>--></span><!--
|
||||
--></span>
|
||||
|
||||
|
@ -50,6 +50,11 @@ support-files =
|
||||
!/devtools/client/shared/test/test-actor.js
|
||||
!/devtools/client/shared/test/test-actor-registry.js
|
||||
|
||||
[browser_markup_accessibility_focus_blur.js]
|
||||
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
|
||||
[browser_markup_accessibility_navigation.js]
|
||||
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
|
||||
[browser_markup_accessibility_semantics.js]
|
||||
[browser_markup_anonymous_01.js]
|
||||
[browser_markup_anonymous_02.js]
|
||||
skip-if = e10s # scratchpad.xul is not loading in e10s window
|
||||
|
@ -0,0 +1,59 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test inspector markup view handling focus and blur when moving between markup
|
||||
// view, its root and other containers, and other parts of inspector.
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(
|
||||
"data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
|
||||
let markup = inspector.markup;
|
||||
let doc = markup.doc;
|
||||
let win = doc.defaultView;
|
||||
|
||||
let spanContainer = yield getContainerForSelector("span", inspector);
|
||||
let rootContainer = markup.getContainer(markup._rootNode);
|
||||
|
||||
is(doc.activeElement, doc.body,
|
||||
"Keyboard focus by default is on document body");
|
||||
|
||||
yield selectNode("span", inspector);
|
||||
|
||||
is(doc.activeElement, doc.body,
|
||||
"Keyboard focus is still on document body");
|
||||
|
||||
info("Focusing on the test span node using 'Return' key");
|
||||
// Focus on the tree element.
|
||||
rootContainer.elt.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, win);
|
||||
|
||||
is(doc.activeElement, spanContainer.editor.tag,
|
||||
"Keyboard focus should be on tag element of focused container");
|
||||
|
||||
info("Focusing on search box, external to markup view document");
|
||||
yield focusSearchBoxUsingShortcut(inspector.panelWin);
|
||||
|
||||
is(doc.activeElement, doc.body,
|
||||
"Keyboard focus should be removed from focused container");
|
||||
|
||||
info("Selecting the test span node again");
|
||||
yield selectNode("span", inspector);
|
||||
|
||||
is(doc.activeElement, doc.body,
|
||||
"Keyboard focus should again be on document body");
|
||||
|
||||
info("Focusing on the test span node using 'Space' key");
|
||||
// Focus on the tree element.
|
||||
rootContainer.elt.focus();
|
||||
EventUtils.synthesizeKey("VK_SPACE", {}, win);
|
||||
|
||||
is(doc.activeElement, spanContainer.editor.tag,
|
||||
"Keyboard focus should again be on tag element of focused container");
|
||||
|
||||
yield clickOnInspectMenuItem(testActor, "h1");
|
||||
is(doc.activeElement, rootContainer.elt,
|
||||
"When inspect menu item is used keyboard focus should move to tree.");
|
||||
});
|
@ -0,0 +1,301 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* global getContainerForSelector, openInspectorForURL */
|
||||
|
||||
// Test keyboard navigation accessibility of inspector's markup view.
|
||||
|
||||
/**
|
||||
* Test data has the format of:
|
||||
* {
|
||||
* desc {String} description for better logging
|
||||
* key {String} key event's key
|
||||
* options {?Object} optional event data such as shiftKey, etc
|
||||
* focused {String} path to expected focused element relative to
|
||||
* its container
|
||||
* activedescendant {String} path to expected aria-activedescendant element
|
||||
* relative to its container
|
||||
* waitFor {String} optional event to wait for if keyboard actions
|
||||
* result in asynchronous updates
|
||||
* }
|
||||
*/
|
||||
const TESTS = [
|
||||
{
|
||||
desc: "Collapse body container",
|
||||
focused: "root.elt",
|
||||
activedescendant: "body.tagLine",
|
||||
key: "VK_LEFT",
|
||||
options: { },
|
||||
waitFor: "collapsed"
|
||||
},
|
||||
{
|
||||
desc: "Expand body container",
|
||||
focused: "root.elt",
|
||||
activedescendant: "body.tagLine",
|
||||
key: "VK_RIGHT",
|
||||
options: { },
|
||||
waitFor: "expanded"
|
||||
},
|
||||
{
|
||||
desc: "Select header container",
|
||||
focused: "root.elt",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_DOWN",
|
||||
options: { },
|
||||
waitFor: "inspector-updated"
|
||||
},
|
||||
{
|
||||
desc: "Expand header container",
|
||||
focused: "root.elt",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_RIGHT",
|
||||
options: { },
|
||||
waitFor: "expanded"
|
||||
},
|
||||
{
|
||||
desc: "Select text container",
|
||||
focused: "root.elt",
|
||||
activedescendant: "container-0.tagLine",
|
||||
key: "VK_DOWN",
|
||||
options: { },
|
||||
waitFor: "inspector-updated"
|
||||
},
|
||||
{
|
||||
desc: "Select header container again",
|
||||
focused: "root.elt",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_UP",
|
||||
options: { },
|
||||
waitFor: "inspector-updated"
|
||||
},
|
||||
{
|
||||
desc: "Collapse header container",
|
||||
focused: "root.elt",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_LEFT",
|
||||
options: { },
|
||||
waitFor: "collapsed"
|
||||
},
|
||||
{
|
||||
desc: "Focus on header container tag",
|
||||
focused: "header.focusableElms.0",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_RETURN",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Remove focus from header container tag",
|
||||
focused: "root.elt",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_ESCAPE",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Focus on header container tag again",
|
||||
focused: "header.focusableElms.0",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_SPACE",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Focus on header id attribute",
|
||||
focused: "header.focusableElms.1",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Focus on header class attribute",
|
||||
focused: "header.focusableElms.2",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Focus on header new attribute",
|
||||
focused: "header.focusableElms.3",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Circle back and focus on header tag again",
|
||||
focused: "header.focusableElms.0",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Circle back and focus on header new attribute again",
|
||||
focused: "header.focusableElms.3",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { shiftKey: true }
|
||||
},
|
||||
{
|
||||
desc: "Tab back and focus on header class attribute",
|
||||
focused: "header.focusableElms.2",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { shiftKey: true }
|
||||
},
|
||||
{
|
||||
desc: "Tab back and focus on header id attribute",
|
||||
focused: "header.focusableElms.1",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { shiftKey: true }
|
||||
},
|
||||
{
|
||||
desc: "Tab back and focus on header tag",
|
||||
focused: "header.focusableElms.0",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { shiftKey: true }
|
||||
},
|
||||
{
|
||||
desc: "Expand header container, ensure that focus is still on header tag",
|
||||
focused: "header.focusableElms.0",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_RIGHT",
|
||||
options: { },
|
||||
waitFor: "expanded"
|
||||
},
|
||||
{
|
||||
desc: "Activate header tag editor",
|
||||
focused: "header.editor.tag.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_RETURN",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Activate header id attribute editor",
|
||||
focused: "header.editor.attrList.children.0.children.1.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Deselect text in header id attribute editor",
|
||||
focused: "header.editor.attrList.children.0.children.1.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Activate header class attribute editor",
|
||||
focused: "header.editor.attrList.children.1.children.1.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Deselect text in header class attribute editor",
|
||||
focused: "header.editor.attrList.children.1.children.1.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Activate header new attribute editor",
|
||||
focused: "header.editor.newAttr.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Circle back and activate header tag editor again",
|
||||
focused: "header.editor.tag.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Circle back and activate header new attribute editor again",
|
||||
focused: "header.editor.newAttr.inplaceEditor.input",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_TAB",
|
||||
options: { shiftKey: true }
|
||||
},
|
||||
{
|
||||
desc: "Exit edit mode and keep focus on header new attribute",
|
||||
focused: "header.focusableElms.3",
|
||||
activedescendant: "header.tagLine",
|
||||
key: "VK_ESCAPE",
|
||||
options: { }
|
||||
},
|
||||
{
|
||||
desc: "Move the selection to body and reset focus to container tree",
|
||||
focused: "docBody",
|
||||
activedescendant: "body.tagLine",
|
||||
key: "VK_UP",
|
||||
options: { },
|
||||
waitFor: "inspector-updated"
|
||||
},
|
||||
];
|
||||
|
||||
let elms = {};
|
||||
let containerID = 0;
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector } = yield openInspectorForURL(`data:text/html;charset=utf-8,
|
||||
<h1 id="some-id" class="some-class">foo<span>Child span<span></h1>`);
|
||||
let markup = inspector.markup;
|
||||
let doc = markup.doc;
|
||||
let win = doc.defaultView;
|
||||
|
||||
// Record containers that are created after inspector is initialized to be
|
||||
// useful in testing.
|
||||
inspector.on("container-created", memorizeContainer);
|
||||
registerCleanupFunction(() => {
|
||||
inspector.off("container-created", memorizeContainer);
|
||||
});
|
||||
|
||||
elms.docBody = doc.body;
|
||||
elms.root = markup.getContainer(markup._rootNode);
|
||||
elms.header = yield getContainerForSelector("h1", inspector);
|
||||
elms.body = yield getContainerForSelector("body", inspector);
|
||||
|
||||
// Initial focus is on root element and active descendant should be set on
|
||||
// body tag line.
|
||||
testNavigationState(doc, elms.docBody, elms.body.tagLine);
|
||||
|
||||
// Focus on the tree element.
|
||||
elms.root.elt.focus();
|
||||
|
||||
for (let {desc, waitFor, focused, activedescendant, key, options} of TESTS) {
|
||||
info(desc);
|
||||
let updated;
|
||||
if (waitFor) {
|
||||
updated = waitFor === "inspector-updated" ?
|
||||
inspector.once(waitFor) : markup.once(waitFor);
|
||||
} else {
|
||||
updated = Promise.resolve();
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey(key, options, win);
|
||||
yield updated;
|
||||
testNavigationState(doc, getElm(focused), getElm(activedescendant));
|
||||
}
|
||||
});
|
||||
|
||||
// Record all containers that are created dynamically into elms object.
|
||||
function memorizeContainer(event, container) {
|
||||
elms[`container-${containerID++}`] = container;
|
||||
}
|
||||
|
||||
// Parse and lookup an element from elms object based on dotted path.
|
||||
function getElm(path) {
|
||||
let segments = path.split(".");
|
||||
return segments.reduce((prev, current) => prev[current], elms);
|
||||
}
|
||||
|
||||
function testNavigationState(doc, focused, activedescendant) {
|
||||
let id = activedescendant.getAttribute("id");
|
||||
is(doc.activeElement, focused, `Keyboard focus should be set to ${focused}`);
|
||||
is(elms.root.elt.getAttribute("aria-activedescendant"), id,
|
||||
`Active descendant should be set to ${id}`);
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that inspector markup view has all expected ARIA properties set and
|
||||
// updated.
|
||||
|
||||
const TOP_CONTAINER_LEVEL = 3;
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL(`
|
||||
data:text/html;charset=utf-8,
|
||||
<h1>foo</h1>
|
||||
<span>bar</span>
|
||||
<ul>
|
||||
<li></li>
|
||||
</ul>`);
|
||||
let markup = inspector.markup;
|
||||
let doc = markup.doc;
|
||||
let win = doc.defaultView;
|
||||
|
||||
let rootElt = markup.getContainer(markup._rootNode).elt;
|
||||
let bodyContainer = yield getContainerForSelector("body", inspector);
|
||||
let spanContainer = yield getContainerForSelector("span", inspector);
|
||||
let headerContainer = yield getContainerForSelector("h1", inspector);
|
||||
let listContainer = yield getContainerForSelector("ul", inspector);
|
||||
|
||||
// Focus on the tree element.
|
||||
rootElt.focus();
|
||||
|
||||
// Test tree related semantics
|
||||
is(rootElt.getAttribute("role"), "tree",
|
||||
"Root container should have tree semantics");
|
||||
is(rootElt.getAttribute("aria-dropeffect"), "none",
|
||||
"By default root container's drop effect should be set to none");
|
||||
is(rootElt.getAttribute("aria-activedescendant"),
|
||||
bodyContainer.tagLine.getAttribute("id"),
|
||||
"Default active descendant should be set to body");
|
||||
is(bodyContainer.tagLine.getAttribute("aria-level"), TOP_CONTAINER_LEVEL - 1,
|
||||
"Body container tagLine should have nested level up to date");
|
||||
[spanContainer, headerContainer, listContainer].forEach(container => {
|
||||
let treeitem = container.tagLine;
|
||||
is(treeitem.getAttribute("role"), "treeitem",
|
||||
"Child container tagLine elements should have tree item semantics");
|
||||
is(treeitem.getAttribute("aria-level"), TOP_CONTAINER_LEVEL,
|
||||
"Child container tagLine should have nested level up to date");
|
||||
is(treeitem.getAttribute("aria-grabbed"), "false",
|
||||
"Child container should be draggable but not grabbed by default");
|
||||
is(container.children.getAttribute("role"), "group",
|
||||
"Container with children should have its children element have group " +
|
||||
"semantics");
|
||||
ok(treeitem.id, "Tree item should have id assigned");
|
||||
if (container.closeTagLine) {
|
||||
is(container.closeTagLine.getAttribute("role"), "presentation",
|
||||
"Ignore closing tag");
|
||||
}
|
||||
if (container.expander) {
|
||||
is(container.expander.getAttribute("role"), "presentation",
|
||||
"Ignore expander");
|
||||
}
|
||||
});
|
||||
|
||||
// Test expanding/expandable semantics
|
||||
ok(!spanContainer.tagLine.hasAttribute("aria-expanded"),
|
||||
"Non expandable tree items should not have aria-expanded attribute");
|
||||
ok(!headerContainer.tagLine.hasAttribute("aria-expanded"),
|
||||
"Non expandable tree items should not have aria-expanded attribute");
|
||||
is(listContainer.tagLine.getAttribute("aria-expanded"), "false",
|
||||
"Closed tree item should have aria-expanded unset");
|
||||
|
||||
info("Selecting and expanding list container");
|
||||
let updated = waitForMultipleChildrenUpdates(inspector);
|
||||
yield selectNode("ul", inspector);
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {}, win);
|
||||
yield updated;
|
||||
|
||||
is(rootElt.getAttribute("aria-activedescendant"),
|
||||
listContainer.tagLine.getAttribute("id"),
|
||||
"Active descendant should not be set to list container tagLine");
|
||||
is(listContainer.tagLine.getAttribute("aria-expanded"), "true",
|
||||
"Open tree item should have aria-expanded set");
|
||||
let listItemContainer = yield getContainerForSelector("li", inspector);
|
||||
is(listItemContainer.tagLine.getAttribute("aria-level"),
|
||||
TOP_CONTAINER_LEVEL + 1,
|
||||
"Grand child container tagLine should have nested level up to date");
|
||||
is(listItemContainer.children.getAttribute("role"), "presentation",
|
||||
"Container with no children should have its children element ignored by " +
|
||||
"accessibility");
|
||||
|
||||
info("Collapsing list container");
|
||||
updated = waitForMultipleChildrenUpdates(inspector);
|
||||
EventUtils.synthesizeKey("VK_LEFT", {}, win);
|
||||
yield updated;
|
||||
|
||||
is(listContainer.tagLine.getAttribute("aria-expanded"), "false",
|
||||
"Closed tree item should have aria-expanded unset");
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Focusing the tag editor of the test element");
|
||||
let {editor} = yield getContainerForSelector("div", inspector);
|
||||
let {editor} = yield focusNode("div", inspector);
|
||||
editor.tag.focus();
|
||||
|
||||
info("Pressing tab and expecting to focus the ID attribute, always first");
|
||||
|
@ -14,7 +14,7 @@ add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
info("Selecting the test node");
|
||||
yield selectNode("#test-div", inspector);
|
||||
yield focusNode("#test-div", inspector);
|
||||
|
||||
info("Verify attributes, only ID should be there for now");
|
||||
yield assertAttributes("#test-div", {
|
||||
|
@ -15,7 +15,7 @@ add_task(function* () {
|
||||
yield inspector.markup.expandAll();
|
||||
|
||||
info("Selecting the test node");
|
||||
yield selectNode("#retag-me", inspector);
|
||||
yield focusNode("#retag-me", inspector);
|
||||
|
||||
info("Getting the markup-container for the test node");
|
||||
let container = yield getContainerForSelector("#retag-me", inspector);
|
||||
|
@ -37,7 +37,7 @@ function* testCollapsedLongAttribute(inspector, testActor) {
|
||||
"data-long": LONG_ATTRIBUTE
|
||||
}, testActor);
|
||||
|
||||
let {editor} = yield getContainerForSelector("#node24", inspector);
|
||||
let {editor} = yield focusNode("#node24", inspector);
|
||||
let attr = editor.attrElements.get("data-long").querySelector(".editable");
|
||||
|
||||
// Check to make sure it has expanded after focus
|
||||
@ -71,7 +71,7 @@ function* testModifyInlineStyleWithQuotes(inspector, testActor) {
|
||||
}, testActor);
|
||||
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
let {editor} = yield getContainerForSelector("#node26", inspector);
|
||||
let {editor} = yield focusNode("#node26", inspector);
|
||||
let attr = editor.attrElements.get("style").querySelector(".editable");
|
||||
|
||||
attr.focus();
|
||||
@ -107,7 +107,7 @@ function* testEditingAttributeWithMixedQuotes(inspector, testActor) {
|
||||
}, testActor);
|
||||
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
let {editor} = yield getContainerForSelector("#node27", inspector);
|
||||
let {editor} = yield focusNode("#node27", inspector);
|
||||
let attr = editor.attrElements.get("class").querySelector(".editable");
|
||||
|
||||
attr.focus();
|
||||
|
@ -26,7 +26,7 @@ function* testWellformedMixedCase(inspector, testActor) {
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
|
||||
info("Focusing the viewBox attribute editor");
|
||||
let {editor} = yield getContainerForSelector("svg", inspector);
|
||||
let {editor} = yield focusNode("svg", inspector);
|
||||
let attr = editor.attrElements.get("viewBox").querySelector(".editable");
|
||||
attr.focus();
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
@ -52,7 +52,7 @@ function* testMalformedMixedCase(inspector, testActor) {
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
|
||||
info("Focusing the viewBox attribute editor");
|
||||
let {editor} = yield getContainerForSelector("svg", inspector);
|
||||
let {editor} = yield focusNode("svg", inspector);
|
||||
let attr = editor.attrElements.get("viewBox").querySelector(".editable");
|
||||
attr.focus();
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
|
@ -11,10 +11,9 @@ const TEST_URL = "data:text/html;charset=utf-8,<div></div>";
|
||||
add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
yield inspector.markup.expandAll();
|
||||
yield selectNode("div", inspector);
|
||||
|
||||
info("Updating the DIV tagname to an invalid value");
|
||||
let container = yield getContainerForSelector("div", inspector);
|
||||
let container = yield focusNode("div", inspector);
|
||||
let onCancelReselect = inspector.markup.once("canceledreselectonremoved");
|
||||
let tagEditor = container.editor.tag;
|
||||
setEditableFieldValue(tagEditor, "<<<", inspector);
|
||||
|
@ -20,8 +20,7 @@ add_task(function* () {
|
||||
isEditTagNameCalled = true;
|
||||
};
|
||||
|
||||
yield selectNode("div", inspector);
|
||||
let container = yield getContainerForSelector("div", inspector);
|
||||
let container = yield focusNode("div", inspector);
|
||||
let tagEditor = container.editor.tag;
|
||||
|
||||
info("Blurring the tagname field");
|
||||
@ -29,6 +28,7 @@ add_task(function* () {
|
||||
is(isEditTagNameCalled, false, "The editTagName method wasn't called");
|
||||
|
||||
info("Updating the tagname to uppercase");
|
||||
yield focusNode("div", inspector);
|
||||
setEditableFieldValue(tagEditor, "DIV", inspector);
|
||||
is(isEditTagNameCalled, false, "The editTagName method wasn't called");
|
||||
|
||||
|
@ -89,7 +89,7 @@ function* editAttributeAndTab(newValue, inspector, goPrevious) {
|
||||
* field.
|
||||
*/
|
||||
function* activateFirstAttribute(container, inspector) {
|
||||
let {editor} = yield getContainerForSelector(container, inspector);
|
||||
let {editor} = yield focusNode(container, inspector);
|
||||
editor.tag.focus();
|
||||
|
||||
// Go to "id" attribute and trigger edit mode.
|
||||
|
@ -53,7 +53,7 @@ function* editContainer(inspector, testActor,
|
||||
|
||||
info("Changing the text content");
|
||||
let onMutated = inspector.once("markupmutation");
|
||||
let container = yield getContainerForSelector(selector, inspector);
|
||||
let container = yield focusNode(selector, inspector);
|
||||
let field = container.elt.querySelector("pre");
|
||||
|
||||
if (shortValue) {
|
||||
|
@ -22,8 +22,7 @@ add_task(function* () {
|
||||
is(nodeValue, expectedValue, "The test node's text content is correct");
|
||||
|
||||
info("Open editable field for .node6");
|
||||
let nodeFront = yield getNodeFront(SELECTOR, inspector);
|
||||
let container = getContainerForNodeFront(nodeFront, inspector);
|
||||
let container = yield focusNode(SELECTOR, inspector);
|
||||
let field = container.elt.querySelector("pre");
|
||||
field.focus();
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
|
@ -160,7 +160,7 @@ function setEditableFieldValue(field, value, inspector) {
|
||||
var addNewAttributes = Task.async(function* (selector, text, inspector) {
|
||||
info(`Entering text "${text}" in new attribute field for node ${selector}`);
|
||||
|
||||
let container = yield getContainerForSelector(selector, inspector);
|
||||
let container = yield focusNode(selector, inspector);
|
||||
ok(container, "The container for '" + selector + "' was found");
|
||||
|
||||
info("Listening for the markupmutation event");
|
||||
|
@ -135,7 +135,7 @@ function* runEditAttributesTest(test, inspector, testActor) {
|
||||
|
||||
info("Editing attribute " + test.name + " with value " + test.value);
|
||||
|
||||
let container = yield getContainerForSelector(test.node, inspector);
|
||||
let container = yield focusNode(test.node, inspector);
|
||||
ok(container && container.editor, "The markup-container for " + test.node +
|
||||
" was found");
|
||||
|
||||
|
@ -31,8 +31,7 @@ function* runStyleAttributeAutocompleteTests(inspector, testData) {
|
||||
yield inspector.markup.expandAll();
|
||||
|
||||
info("Select #node14");
|
||||
let nodeFront = yield getNodeFront("#node14", inspector);
|
||||
let container = getContainerForNodeFront(nodeFront, inspector);
|
||||
let container = yield focusNode("#node14", inspector);
|
||||
|
||||
info("Focus and open the new attribute inplace-editor");
|
||||
let attr = container.editor.newAttr;
|
||||
|
@ -18,8 +18,8 @@
|
||||
// ]
|
||||
var testData = [
|
||||
["d", {}, "display", 1, 3, false],
|
||||
["VK_TAB", {}, "", -1, 41, true],
|
||||
["VK_DOWN", {}, "block", 0, 41, true],
|
||||
["VK_TAB", {}, "", -1, 43, true],
|
||||
["VK_DOWN", {}, "block", 0, 43, true],
|
||||
["n", {}, "none", -1, 0, true],
|
||||
["VK_TAB", {shiftKey: true}, "display", -1, 0, true],
|
||||
["VK_BACK_SPACE", {}, "", -1, 0, false],
|
||||
|
@ -5,9 +5,10 @@
|
||||
"use strict";
|
||||
|
||||
// Test that adding nodes does work as expected: the parent gets expanded, the
|
||||
// new node gets selected and the corresponding markup-container focused.
|
||||
// new node gets selected.
|
||||
|
||||
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
|
||||
const PARENT_TREE_LEVEL = 3;
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
@ -38,6 +39,10 @@ add_task(function* () {
|
||||
function* testAddNode(parentNode, inspector) {
|
||||
let btn = inspector.panelDoc.querySelector("#inspector-element-add-button");
|
||||
let markupWindow = inspector.markup.win;
|
||||
let parentContainer = inspector.markup.getContainer(parentNode);
|
||||
|
||||
is(parentContainer.tagLine.getAttribute("aria-level"), PARENT_TREE_LEVEL,
|
||||
"Parent level should be up to date.");
|
||||
|
||||
info("Clicking 'add node' and expecting a markup mutation and focus event");
|
||||
let onMutation = inspector.once("markupmutation");
|
||||
@ -56,18 +61,21 @@ function* testAddNode(parentNode, inspector) {
|
||||
is(newNode, inspector.selection.nodeFront,
|
||||
"The new node is selected");
|
||||
|
||||
ok(inspector.markup.getContainer(parentNode).expanded,
|
||||
"The parent node is now expanded");
|
||||
ok(parentContainer.expanded, "The parent node is now expanded");
|
||||
|
||||
is(inspector.selection.nodeFront.parentNode(), parentNode,
|
||||
"The new node is inside the right parent");
|
||||
|
||||
let focusedElement = markupWindow.document.activeElement;
|
||||
let focusedContainer = focusedElement.closest(".child").container;
|
||||
is(focusedContainer.node, inspector.selection.nodeFront,
|
||||
"The right container is focused in the markup-view");
|
||||
ok(focusedElement.classList.contains("tag"),
|
||||
"The tagName part of the container is focused");
|
||||
let focusedContainer = focusedElement.container;
|
||||
let selectedContainer = inspector.markup._selectedContainer;
|
||||
is(selectedContainer.tagLine.getAttribute("aria-level"),
|
||||
PARENT_TREE_LEVEL + 1, "Added container level should be up to date.");
|
||||
is(selectedContainer.node, inspector.selection.nodeFront,
|
||||
"The right container is selected in the markup-view");
|
||||
ok(selectedContainer.selected, "Selected container is set to selected");
|
||||
is(focusedContainer.toString(), "[root container]",
|
||||
"Root container is focused");
|
||||
}
|
||||
|
||||
function collapseNode(node, inspector) {
|
||||
|
@ -139,6 +139,22 @@ var selectNode = Task.async(function* (selector, inspector, reason = "test") {
|
||||
yield updated;
|
||||
});
|
||||
|
||||
/**
|
||||
* Select node for a given selector, make it focusable and set focus in its
|
||||
* container element.
|
||||
* @param {String|NodeFront} selector
|
||||
* @param {InspectorPanel} inspector The current inspector-panel instance.
|
||||
* @return {MarkupContainer}
|
||||
*/
|
||||
function* focusNode(selector, inspector) {
|
||||
getContainerForNodeFront(inspector.walker.rootNode, inspector).elt.focus();
|
||||
let nodeFront = yield getNodeFront(selector, inspector);
|
||||
let container = getContainerForNodeFront(nodeFront, inspector);
|
||||
yield selectNode(nodeFront, inspector);
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inspector's current selection to null so that no node is selected
|
||||
*
|
||||
|
@ -191,6 +191,9 @@ function editableItem(options, callback) {
|
||||
// Save the trigger type so we can dispatch this later
|
||||
element._trigger = trigger;
|
||||
|
||||
// Add button semantics to the element, to indicate that it can be activated.
|
||||
element.setAttribute("role", "button");
|
||||
|
||||
return function turnOnEditMode() {
|
||||
callback(element);
|
||||
};
|
||||
|
@ -151,6 +151,8 @@ function createInplaceEditorAndClick(options, doc) {
|
||||
|
||||
info("Creating an inplace-editor field");
|
||||
editableField(options);
|
||||
is(span.getAttribute("role"), "button",
|
||||
"Editable element should have button semantics");
|
||||
|
||||
info("Clicking on the inplace-editor field to turn to edit mode");
|
||||
span.click();
|
||||
|
@ -173,7 +173,7 @@ ul.children + .tag-line::before {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.child.collapsed .child {
|
||||
.child.collapsed .child, .child.collapsed .children {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -3307,11 +3307,12 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
|
||||
}
|
||||
SetAllowContentRetargeting(mAllowContentRetargeting &&
|
||||
parentAsDocShell->GetAllowContentRetargetingOnChildren());
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
|
||||
SetIsActive(value);
|
||||
}
|
||||
if (parentAsDocShell->GetIsPrerendered()) {
|
||||
SetIsPrerendered(true);
|
||||
SetIsPrerendered();
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
|
||||
// a prerendered docshell is not active yet
|
||||
SetIsActive(value && !mIsPrerendered);
|
||||
}
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
|
||||
!customUserAgent.IsEmpty()) {
|
||||
@ -6092,6 +6093,9 @@ nsDocShell::SetIsActiveInternal(bool aIsActive, bool aIsHidden)
|
||||
// Keep track ourselves.
|
||||
mIsActive = aIsActive;
|
||||
|
||||
// Clear prerender flag if necessary.
|
||||
mIsPrerendered &= !aIsActive;
|
||||
|
||||
// Tell the PresShell about it.
|
||||
nsCOMPtr<nsIPresShell> pshell = GetPresShell();
|
||||
if (pshell) {
|
||||
@ -6157,11 +6161,12 @@ nsDocShell::GetIsActive(bool* aIsActive)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetIsPrerendered(bool aPrerendered)
|
||||
nsDocShell::SetIsPrerendered()
|
||||
{
|
||||
MOZ_ASSERT(!aPrerendered || !mIsPrerendered,
|
||||
"SetIsPrerendered(true) called on already prerendered docshell");
|
||||
mIsPrerendered = aPrerendered;
|
||||
MOZ_ASSERT(!mIsPrerendered,
|
||||
"SetIsPrerendered() called on already prerendered docshell");
|
||||
SetIsActive(false);
|
||||
mIsPrerendered = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -7761,6 +7766,16 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
|
||||
doCreateAlternate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (doCreateAlternate) {
|
||||
// Skip doing this if our channel was redirected, because we
|
||||
// shouldn't be guessing things about the post-redirect URI.
|
||||
nsLoadFlags loadFlags = 0;
|
||||
if (NS_FAILED(aChannel->GetLoadFlags(&loadFlags)) ||
|
||||
(loadFlags & nsIChannel::LOAD_REPLACE)) {
|
||||
doCreateAlternate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doCreateAlternate) {
|
||||
newURI = nullptr;
|
||||
@ -13431,6 +13446,24 @@ nsDocShell::DoCommand(const char* aCommand)
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::DoCommandWithParams(const char* aCommand, nsICommandParams* aParams)
|
||||
{
|
||||
nsCOMPtr<nsIController> controller;
|
||||
nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICommandController> commandController =
|
||||
do_QueryInterface(controller, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return commandController->DoCommandWithParams(aCommand, aParams);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::EnsureCommandHandler()
|
||||
{
|
||||
|
@ -43,6 +43,7 @@ interface nsIScrollObserver;
|
||||
interface nsITabParent;
|
||||
interface nsITabChild;
|
||||
interface nsICommandManager;
|
||||
interface nsICommandParams;
|
||||
native TabChildRef(already_AddRefed<nsITabChild>);
|
||||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
@ -647,7 +648,11 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
* Puts the docshell in prerendering mode. noscript because we want only
|
||||
* native code to be able to put a docshell in prerendering.
|
||||
*/
|
||||
[noscript] void SetIsPrerendered(in boolean prerendered);
|
||||
[noscript] void SetIsPrerendered();
|
||||
|
||||
/**
|
||||
* Whether this docshell is in prerender mode.
|
||||
*/
|
||||
[infallible] readonly attribute boolean isPrerendered;
|
||||
|
||||
/**
|
||||
@ -1015,6 +1020,7 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
*/
|
||||
boolean isCommandEnabled(in string command);
|
||||
void doCommand(in string command);
|
||||
void doCommandWithParams(in string command, in nsICommandParams aParams);
|
||||
|
||||
/**
|
||||
* Invisible DocShell are dummy construct to simulate DOM windows
|
||||
|
@ -76,6 +76,9 @@ skip-if = buildapp == 'mulet'
|
||||
[browser_bug852909.js]
|
||||
[browser_bug92473.js]
|
||||
[browser_uriFixupIntegration.js]
|
||||
[browser_uriFixupAlternateRedirects.js]
|
||||
support-files =
|
||||
redirect_to_example.sjs
|
||||
[browser_loadDisallowInherit.js]
|
||||
[browser_loadURI.js]
|
||||
[browser_multiple_pushState.js]
|
||||
|
24
docshell/test/browser/browser_uriFixupAlternateRedirects.js
Normal file
24
docshell/test/browser/browser_uriFixupAlternateRedirects.js
Normal file
@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
const REDIRECTURL = "http://www.example.com/browser/docshell/test/browser/redirect_to_example.sjs"
|
||||
|
||||
add_task(function* () {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
|
||||
gURLBar.value = REDIRECTURL;
|
||||
gURLBar.select();
|
||||
let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
|
||||
EventUtils.sendKey("return");
|
||||
yield errorPageLoaded;
|
||||
let [contentURL, originalURL] = yield ContentTask.spawn(tab.linkedBrowser, null, () => {
|
||||
return [
|
||||
content.document.documentURI,
|
||||
content.document.mozDocumentURIIfNotForErrorPages.spec,
|
||||
];
|
||||
});
|
||||
info("Page that loaded: " + contentURL);
|
||||
ok(contentURL.startsWith("about:neterror?"), "Should be on an error page");
|
||||
originalURL = new URL(originalURL);
|
||||
is(originalURL.host, "example", "Should be an error for http://example, not http://www.example.com/");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
4
docshell/test/browser/redirect_to_example.sjs
Normal file
4
docshell/test/browser/redirect_to_example.sjs
Normal file
@ -0,0 +1,4 @@
|
||||
function handleRequest(request, response) {
|
||||
response.setStatusLine(request.httpVersion, 302, "Moved Permanently");
|
||||
response.setHeader("Location", "http://example");
|
||||
}
|
@ -98,11 +98,6 @@ Animation::Constructor(const GlobalObject& aGlobal,
|
||||
aRv.Throw(NS_ERROR_DOM_ANIM_NO_EFFECT_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
if (!aTimeline) {
|
||||
// Bug 1096776: We do not support null timeline yet.
|
||||
aRv.Throw(NS_ERROR_DOM_ANIM_NO_TIMELINE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
animation->SetTimeline(aTimeline);
|
||||
animation->SetEffect(aEffect);
|
||||
|
@ -38,32 +38,32 @@ AnimationPerformanceWarning::ToLocalizedString(
|
||||
"Parameter's length should be 2 for ContentTooSmall");
|
||||
|
||||
return NS_SUCCEEDED(
|
||||
ToLocalizedStringWithIntParams<2>("AnimationWarningContentTooSmall",
|
||||
aLocalizedString));
|
||||
ToLocalizedStringWithIntParams<2>(
|
||||
"CompositorAnimationWarningContentTooSmall", aLocalizedString));
|
||||
case Type::ContentTooLarge:
|
||||
MOZ_ASSERT(mParams && mParams->Length() == 7,
|
||||
"Parameter's length should be 7 for ContentTooLarge");
|
||||
|
||||
return NS_SUCCEEDED(
|
||||
ToLocalizedStringWithIntParams<7>("AnimationWarningContentTooLarge",
|
||||
aLocalizedString));
|
||||
ToLocalizedStringWithIntParams<7>(
|
||||
"CompositorAnimationWarningContentTooLarge", aLocalizedString));
|
||||
case Type::TransformBackfaceVisibilityHidden:
|
||||
key = "AnimationWarningTransformBackfaceVisibilityHidden";
|
||||
key = "CompositorAnimationWarningTransformBackfaceVisibilityHidden";
|
||||
break;
|
||||
case Type::TransformPreserve3D:
|
||||
key = "AnimationWarningTransformPreserve3D";
|
||||
key = "CompositorAnimationWarningTransformPreserve3D";
|
||||
break;
|
||||
case Type::TransformSVG:
|
||||
key = "AnimationWarningTransformSVG";
|
||||
key = "CompositorAnimationWarningTransformSVG";
|
||||
break;
|
||||
case Type::TransformWithGeometricProperties:
|
||||
key = "AnimationWarningTransformWithGeometricProperties";
|
||||
key = "CompositorAnimationWarningTransformWithGeometricProperties";
|
||||
break;
|
||||
case Type::TransformFrameInactive:
|
||||
key = "AnimationWarningTransformFrameInactive";
|
||||
key = "CompositorAnimationWarningTransformFrameInactive";
|
||||
break;
|
||||
case Type::OpacityFrameInactive:
|
||||
key = "AnimationWarningOpacityFrameInactive";
|
||||
key = "CompositorAnimationWarningOpacityFrameInactive";
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsString.h"
|
||||
#include "xpcpublic.h" // For xpc::NativeGlobal
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -46,4 +47,19 @@ AnimationUtils::GetCurrentRealmDocument(JSContext* aCx)
|
||||
return win->GetDoc();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
AnimationUtils::IsOffscreenThrottlingEnabled()
|
||||
{
|
||||
static bool sOffscreenThrottlingEnabled;
|
||||
static bool sPrefCached = false;
|
||||
|
||||
if (!sPrefCached) {
|
||||
sPrefCached = true;
|
||||
Preferences::AddBoolVarCache(&sOffscreenThrottlingEnabled,
|
||||
"dom.animations.offscreen-throttling");
|
||||
}
|
||||
|
||||
return sOffscreenThrottlingEnabled;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -54,6 +54,12 @@ public:
|
||||
*/
|
||||
static nsIDocument*
|
||||
GetCurrentRealmDocument(JSContext* aCx);
|
||||
|
||||
/**
|
||||
* Checks if offscreen animation throttling is enabled.
|
||||
*/
|
||||
static bool
|
||||
IsOffscreenThrottlingEnabled();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
|
||||
#include "nsCSSPseudoElements.h" // For CSSPseudoElementType
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
#include "nsIPresShell.h" // For nsIPresShell
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -91,6 +92,7 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
, mTarget(aTarget)
|
||||
, mTiming(aTiming)
|
||||
, mInEffectOnLastAnimationTimingUpdate(false)
|
||||
, mCumulativeChangeHint(nsChangeHint(0))
|
||||
{
|
||||
MOZ_ASSERT(aTiming);
|
||||
}
|
||||
@ -543,6 +545,8 @@ KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
|
||||
runningOnCompositorProperties.HasProperty(property.mProperty);
|
||||
}
|
||||
|
||||
CalculateCumulativeChangeHint();
|
||||
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
@ -1062,6 +1066,16 @@ KeyframeEffectReadOnly::CanThrottle() const
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can throttle the animation if the animation is paint only and
|
||||
// the target frame is out of view or the document is in background tabs.
|
||||
if (CanIgnoreIfNotVisible()) {
|
||||
nsIPresShell* presShell = GetPresShell();
|
||||
if ((presShell && !presShell->IsActive()) ||
|
||||
frame->IsScrolledOutOfView()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// First we need to check layer generation and transform overflow
|
||||
// prior to the property.mIsRunningOnCompositor check because we should
|
||||
// occasionally unthrottle these animations even if the animations are
|
||||
@ -1192,14 +1206,20 @@ KeyframeEffectReadOnly::GetRenderedDocument() const
|
||||
return mTarget->mElement->GetComposedDoc();
|
||||
}
|
||||
|
||||
nsPresContext*
|
||||
KeyframeEffectReadOnly::GetPresContext() const
|
||||
nsIPresShell*
|
||||
KeyframeEffectReadOnly::GetPresShell() const
|
||||
{
|
||||
nsIDocument* doc = GetRenderedDocument();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
return doc->GetShell();
|
||||
}
|
||||
|
||||
nsPresContext*
|
||||
KeyframeEffectReadOnly::GetPresContext() const
|
||||
{
|
||||
nsIPresShell* shell = GetPresShell();
|
||||
if (!shell) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1314,6 +1334,31 @@ KeyframeEffectReadOnly::SetPerformanceWarning(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::CalculateCumulativeChangeHint()
|
||||
{
|
||||
mCumulativeChangeHint = nsChangeHint(0);
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
for (const AnimationPropertySegment& segment : property.mSegments) {
|
||||
mCumulativeChangeHint |= segment.mChangeHint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::CanIgnoreIfNotVisible() const
|
||||
{
|
||||
if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: For further sophisticated optimization we need to check
|
||||
// change hint on the segment corresponding to computedTiming.progress.
|
||||
return NS_IsHintSubset(
|
||||
mCumulativeChangeHint, nsChangeHint_Hints_CanIgnoreIfNotVisible);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
// KeyframeEffect
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_KeyframeEffect_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsChangeHint.h"
|
||||
#include "nsCSSProperty.h"
|
||||
#include "nsCSSValue.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
@ -36,6 +37,7 @@ class nsCSSPropertySet;
|
||||
class nsIContent;
|
||||
class nsIDocument;
|
||||
class nsIFrame;
|
||||
class nsIPresShell;
|
||||
class nsPresContext;
|
||||
|
||||
namespace mozilla {
|
||||
@ -118,6 +120,8 @@ struct AnimationPropertySegment
|
||||
StyleAnimationValue mFromValue, mToValue;
|
||||
Maybe<ComputedTimingFunction> mTimingFunction;
|
||||
|
||||
nsChangeHint mChangeHint;
|
||||
|
||||
bool operator==(const AnimationPropertySegment& aOther) const {
|
||||
return mFromKey == aOther.mFromKey &&
|
||||
mToKey == aOther.mToKey &&
|
||||
@ -329,6 +333,7 @@ public:
|
||||
|
||||
nsIDocument* GetRenderedDocument() const;
|
||||
nsPresContext* GetPresContext() const;
|
||||
nsIPresShell* GetPresShell() const;
|
||||
|
||||
// Associates a warning with the animated property on the specified frame
|
||||
// indicating why, for example, the property could not be animated on the
|
||||
@ -338,6 +343,16 @@ public:
|
||||
nsCSSProperty aProperty,
|
||||
const AnimationPerformanceWarning& aWarning);
|
||||
|
||||
// Cumulative change hint on each segment for each property.
|
||||
// This is used for deciding the animation is paint-only.
|
||||
void CalculateCumulativeChangeHint();
|
||||
|
||||
// Returns true if all of animation properties' change hints
|
||||
// can ignore painting if the animation is not visible.
|
||||
// See nsChangeHint_Hints_CanIgnoreIfNotVisible in nsChangeHint.h
|
||||
// in detail which change hint can be ignored.
|
||||
bool CanIgnoreIfNotVisible() const;
|
||||
|
||||
protected:
|
||||
KeyframeEffectReadOnly(nsIDocument* aDocument,
|
||||
const Maybe<OwningAnimationTarget>& aTarget,
|
||||
@ -392,6 +407,8 @@ protected:
|
||||
bool mInEffectOnLastAnimationTimingUpdate;
|
||||
|
||||
private:
|
||||
nsChangeHint mCumulativeChangeHint;
|
||||
|
||||
nsIFrame* GetAnimationFrame() const;
|
||||
|
||||
bool CanThrottle() const;
|
||||
|
@ -373,7 +373,8 @@ static bool
|
||||
HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
|
||||
|
||||
static void
|
||||
BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
|
||||
BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||
nsTArray<KeyframeValueEntry>& aEntries,
|
||||
nsTArray<AnimationProperty>& aResult);
|
||||
|
||||
static void
|
||||
@ -550,7 +551,7 @@ KeyframeUtils::GetAnimationPropertiesFromKeyframes(
|
||||
}
|
||||
|
||||
nsTArray<AnimationProperty> result;
|
||||
BuildSegmentsFromValueEntries(entries, result);
|
||||
BuildSegmentsFromValueEntries(aStyleContext, entries, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -908,12 +909,43 @@ HasValidOffsets(const nsTArray<Keyframe>& aKeyframes)
|
||||
return true;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsStyleContext>
|
||||
CreateStyleContextForAnimationValue(nsCSSProperty aProperty,
|
||||
StyleAnimationValue aValue,
|
||||
nsStyleContext* aBaseStyleContext)
|
||||
{
|
||||
MOZ_ASSERT(aBaseStyleContext,
|
||||
"CreateStyleContextForAnimationValue needs to be called "
|
||||
"with a valid nsStyleContext");
|
||||
|
||||
RefPtr<AnimValuesStyleRule> styleRule = new AnimValuesStyleRule();
|
||||
styleRule->AddValue(aProperty, aValue);
|
||||
|
||||
nsCOMArray<nsIStyleRule> rules;
|
||||
rules.AppendObject(styleRule);
|
||||
|
||||
MOZ_ASSERT(aBaseStyleContext->PresContext()->StyleSet()->IsGecko(),
|
||||
"ServoStyleSet should not use StyleAnimationValue for animations");
|
||||
nsStyleSet* styleSet =
|
||||
aBaseStyleContext->PresContext()->StyleSet()->AsGecko();
|
||||
|
||||
RefPtr<nsStyleContext> styleContext =
|
||||
styleSet->ResolveStyleByAddingRules(aBaseStyleContext, rules);
|
||||
|
||||
// We need to call StyleData to generate cached data for the style context.
|
||||
// Otherwise CalcStyleDifference returns no meaningful result.
|
||||
styleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
|
||||
|
||||
return styleContext.forget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an array of AnimationProperty objects to represent the keyframe
|
||||
* animation segments in aEntries.
|
||||
*/
|
||||
static void
|
||||
BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
|
||||
BuildSegmentsFromValueEntries(nsStyleContext* aStyleContext,
|
||||
nsTArray<KeyframeValueEntry>& aEntries,
|
||||
nsTArray<AnimationProperty>& aResult)
|
||||
{
|
||||
if (aEntries.IsEmpty()) {
|
||||
@ -1004,6 +1036,22 @@ BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
|
||||
segment->mToValue = aEntries[j].mValue;
|
||||
segment->mTimingFunction = aEntries[i].mTimingFunction;
|
||||
|
||||
RefPtr<nsStyleContext> fromContext =
|
||||
CreateStyleContextForAnimationValue(animationProperty->mProperty,
|
||||
segment->mFromValue, aStyleContext);
|
||||
|
||||
RefPtr<nsStyleContext> toContext =
|
||||
CreateStyleContextForAnimationValue(animationProperty->mProperty,
|
||||
segment->mToValue, aStyleContext);
|
||||
|
||||
uint32_t equalStructs = 0;
|
||||
uint32_t samePointerStructs = 0;
|
||||
segment->mChangeHint =
|
||||
fromContext->CalcStyleDifference(toContext,
|
||||
nsChangeHint(0),
|
||||
&equalStructs,
|
||||
&samePointerStructs);
|
||||
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ var gAnimationWithGeometricKeyframeTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformWithGeometricProperties'
|
||||
warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -231,7 +231,7 @@ var gAnimationWithGeometricKeyframeTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformWithGeometricProperties'
|
||||
warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -249,7 +249,7 @@ var gPerformanceWarningTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformPreserve3D'
|
||||
warning: 'CompositorAnimationWarningTransformPreserve3D'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -263,7 +263,7 @@ var gPerformanceWarningTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
|
||||
warning: 'CompositorAnimationWarningTransformBackfaceVisibilityHidden'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -282,7 +282,7 @@ var gPerformanceWarningTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformPreserve3D'
|
||||
warning: 'CompositorAnimationWarningTransformPreserve3D'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -301,7 +301,7 @@ var gPerformanceWarningTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
|
||||
warning: 'CompositorAnimationWarningTransformBackfaceVisibilityHidden'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -320,7 +320,7 @@ var gMultipleAsyncAnimationsTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformPreserve3D'
|
||||
warning: 'CompositorAnimationWarningTransformPreserve3D'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -349,7 +349,7 @@ var gMultipleAsyncAnimationsTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformBackfaceVisibilityHidden'
|
||||
warning: 'CompositorAnimationWarningTransformBackfaceVisibilityHidden'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -391,7 +391,7 @@ var gMultipleAsyncAnimationsWithGeometricKeyframeTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformWithGeometricProperties'
|
||||
warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -462,7 +462,7 @@ var gMultipleAsyncAnimationsWithGeometricKeyframeTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformWithGeometricProperties'
|
||||
warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -485,7 +485,7 @@ var gMultipleAsyncAnimationsWithGeometricAnimationTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformWithGeometricProperties'
|
||||
warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -518,7 +518,7 @@ var gMultipleAsyncAnimationsWithGeometricAnimationTests = [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformWithGeometricProperties'
|
||||
warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -874,7 +874,7 @@ function start() {
|
||||
[ {
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: 'AnimationWarningTransformSVG'
|
||||
warning: 'CompositorAnimationWarningTransformSVG'
|
||||
} ]);
|
||||
svg.removeAttribute('transform');
|
||||
return waitForFrame();
|
||||
|
@ -68,6 +68,8 @@ const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations';
|
||||
var omtaEnabled = SpecialPowers.DOMWindowUtils.layerManagerRemote &&
|
||||
SpecialPowers.getBoolPref(OMTAPrefKey);
|
||||
|
||||
var isAndroid = !!navigator.userAgent.includes("Android");
|
||||
|
||||
function add_task_if_omta_enabled(test) {
|
||||
if (!omtaEnabled) {
|
||||
info(test.name + " is skipped because OMTA is disabled");
|
||||
@ -79,6 +81,20 @@ function add_task_if_omta_enabled(test) {
|
||||
// We need to wait for all paints before running tests to avoid contaminations
|
||||
// from styling of this document itself.
|
||||
waitForAllPaints(function() {
|
||||
add_task(function* restyling_for_main_thread_animations() {
|
||||
var div = addDiv(null, { style: 'animation: background-color 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
yield animation.ready;
|
||||
ok(!animation.isRunningOnCompositor);
|
||||
|
||||
var markers = yield observeStyling(5);
|
||||
is(markers.length, 5,
|
||||
'CSS animations running on the main-thread should update style ' +
|
||||
'on the main thread');
|
||||
yield ensureElementRemoval(div);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(function* no_restyling_for_compositor_animations() {
|
||||
var div = addDiv(null, { style: 'animation: opacity 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
@ -191,6 +207,10 @@ waitForAllPaints(function() {
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(function* no_restyling_compositor_animations_out_of_view_element() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: opacity 100s; transform: translateY(-400px);' });
|
||||
var animation = div.getAnimations()[0];
|
||||
@ -200,13 +220,17 @@ waitForAllPaints(function() {
|
||||
|
||||
var markers = yield observeStyling(5);
|
||||
|
||||
todo_is(markers.length, 0,
|
||||
'Bug 1166500: Animations running on the compositor in out of ' +
|
||||
'view element should never cause restyles');
|
||||
is(markers.length, 0,
|
||||
'Animations running on the compositor in an out-of-view element ' +
|
||||
'should never cause restyles');
|
||||
yield ensureElementRemoval(div);
|
||||
});
|
||||
|
||||
add_task(function* no_restyling_main_thread_animations_out_of_view_element() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: background-color 100s; transform: translateY(-400px);' });
|
||||
var animation = div.getAnimations()[0];
|
||||
@ -214,17 +238,24 @@ waitForAllPaints(function() {
|
||||
yield animation.ready;
|
||||
var markers = yield observeStyling(5);
|
||||
|
||||
todo_is(markers.length, 0,
|
||||
'Bug 1166500: Animations running on the main-thread in out of ' +
|
||||
'view element should never cause restyles');
|
||||
is(markers.length, 0,
|
||||
'Animations running on the main-thread in an out-of-view element ' +
|
||||
'should never cause restyles');
|
||||
yield ensureElementRemoval(div);
|
||||
});
|
||||
|
||||
/*
|
||||
Disabled for now since, on Android, the opacity animation runs on the
|
||||
compositor even if it is scrolled out of view.
|
||||
We will fix this in bug 1166500 or a follow-up bug
|
||||
add_task_if_omta_enabled(function* no_restyling_compositor_animations_in_scrolled_out_element() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
On Android the opacity animation runs on the compositor even if it is
|
||||
scrolled out of view. We will fix this in bug 1247800.
|
||||
*/
|
||||
if (isAndroid) {
|
||||
return;
|
||||
}
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 20px;' });
|
||||
var div = addDiv(null,
|
||||
@ -233,18 +264,29 @@ waitForAllPaints(function() {
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
yield animation.ready;
|
||||
ok(!animation.isRunningOnCompositor);
|
||||
|
||||
var markers = yield observeStyling(5);
|
||||
|
||||
todo_is(markers.length, 0,
|
||||
'Bug 1166500: Animations running on the compositor in elements ' +
|
||||
'which are scrolled out should never cause restyles');
|
||||
is(markers.length, 0,
|
||||
'Animations running on the compositor for elements ' +
|
||||
'which are scrolled out should never cause restyles');
|
||||
|
||||
yield ensureElementRemoval(parentElement);
|
||||
});
|
||||
*/
|
||||
|
||||
add_task(function* no_restyling_main_thread_animations_in_scrolled_out_element() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
On Android throttled animations are left behind on the main thread in some
|
||||
frames, We will fix this in bug 1247800.
|
||||
*/
|
||||
if (isAndroid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 20px;' });
|
||||
var div = addDiv(null,
|
||||
@ -255,16 +297,46 @@ waitForAllPaints(function() {
|
||||
yield animation.ready;
|
||||
var markers = yield observeStyling(5);
|
||||
|
||||
todo_is(markers.length, 0,
|
||||
'Bug 1166500: Animations running on the main-thread in elements ' +
|
||||
'which are scrolled out should never cause restyles');
|
||||
is(markers.length, 0,
|
||||
'Animations running on the main-thread for elements ' +
|
||||
'which are scrolled out should never cause restyles');
|
||||
|
||||
yield ensureElementRemoval(parentElement);
|
||||
});
|
||||
|
||||
/*
|
||||
Disabled for now since, on Android and B2G, the opacity animation runs on the
|
||||
compositor even if the associated element has visibility:hidden.
|
||||
We will fix this in bug 1237454 or a follow-up bug.
|
||||
add_task(function* no_restyling_main_thread_animations_in_nested_scrolled_out_element() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
On Android throttled animations are left behind on the main thread in some
|
||||
frames, We will fix this in bug 1247800.
|
||||
*/
|
||||
if (isAndroid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var grandParent = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 20px;' });
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 100px;' });
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: background-color 100s; position: relative; top: 100px;' });
|
||||
grandParent.appendChild(parentElement);
|
||||
parentElement.appendChild(div);
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
yield animation.ready;
|
||||
var markers = yield observeStyling(5);
|
||||
|
||||
is(markers.length, 0,
|
||||
'Animations running on the main-thread which are in nested elements ' +
|
||||
'which are scrolled out should never cause restyles');
|
||||
|
||||
yield ensureElementRemoval(grandParent);
|
||||
});
|
||||
|
||||
add_task_if_omta_enabled(function* no_restyling_compositor_animations_in_visiblily_hidden_element() {
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: opacity 100s; visibility: hidden' });
|
||||
@ -280,7 +352,157 @@ waitForAllPaints(function() {
|
||||
'visibility hidden element should never cause restyles');
|
||||
yield ensureElementRemoval(div);
|
||||
});
|
||||
*/
|
||||
|
||||
add_task(function* restyling_main_thread_animations_moved_in_view_by_scrolling() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
On Android throttled animations are left behind on the main thread in some
|
||||
frames, We will fix this in bug 1247800.
|
||||
*/
|
||||
if (isAndroid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 20px;' });
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: background-color 100s; position: relative; top: 100px;' });
|
||||
parentElement.appendChild(div);
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var parentRect = parentElement.getBoundingClientRect();
|
||||
var centerX = parentRect.left + parentRect.width / 2;
|
||||
var centerY = parentRect.top + parentRect.height / 2;
|
||||
|
||||
yield animation.ready;
|
||||
|
||||
var markers = yield observeStyling(1, function() {
|
||||
// We can't use synthesizeWheel here since synthesizeWheel causes
|
||||
// layout flush.
|
||||
synthesizeWheelAtPoint(centerX, centerY,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaY: 100 });
|
||||
});
|
||||
|
||||
is(markers.length, 1,
|
||||
'Animations running on the main-thread which were in scrolled out ' +
|
||||
'elements should update restyling soon after the element moved in ' +
|
||||
'view by scrolling');
|
||||
|
||||
yield ensureElementRemoval(parentElement);
|
||||
});
|
||||
|
||||
add_task(function* restyling_main_thread_animations_moved_in_view_by_scrolling() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var grandParent = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 20px;' });
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 200px;' });
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: background-color 100s; position: relative; top: 100px;' });
|
||||
grandParent.appendChild(parentElement);
|
||||
parentElement.appendChild(div);
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var parentRect = grandParent.getBoundingClientRect();
|
||||
var centerX = parentRect.left + parentRect.width / 2;
|
||||
var centerY = parentRect.top + parentRect.height / 2;
|
||||
|
||||
yield animation.ready;
|
||||
|
||||
var markers = yield observeStyling(1, function() {
|
||||
// We can't use synthesizeWheel here since synthesizeWheel causes
|
||||
// layout flush.
|
||||
synthesizeWheelAtPoint(centerX, centerY,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaY: 100 });
|
||||
});
|
||||
|
||||
// FIXME: We should reduce a redundant restyle here.
|
||||
ok(markers.length >= 1,
|
||||
'Animations running on the main-thread which were in nested scrolled ' +
|
||||
'out elements should update restyle soon after the element moved ' +
|
||||
'in view by scrolling');
|
||||
|
||||
yield ensureElementRemoval(grandParent);
|
||||
});
|
||||
|
||||
add_task(function* restyling_main_thread_animations_move_out_of_view_by_scrolling() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
On Android throttled animations are left behind on the main thread in some
|
||||
frames, We will fix this in bug 1247800.
|
||||
*/
|
||||
if (isAndroid) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 200px;' });
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: background-color 100s;' });
|
||||
var pad = addDiv(null,
|
||||
{ style: 'height: 400px;' });
|
||||
parentElement.appendChild(div);
|
||||
parentElement.appendChild(pad);
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var parentRect = parentElement.getBoundingClientRect();
|
||||
var centerX = parentRect.left + parentRect.width / 2;
|
||||
var centerY = parentRect.top + parentRect.height / 2;
|
||||
|
||||
yield animation.ready;
|
||||
|
||||
// We can't use synthesizeWheel here since synthesizeWheel causes
|
||||
// layout flush.
|
||||
synthesizeWheelAtPoint(centerX, centerY,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaY: 200 });
|
||||
|
||||
var markers = yield observeStyling(5);
|
||||
|
||||
// FIXME: We should reduce a redundant restyle here.
|
||||
ok(markers.length >= 0,
|
||||
'Animations running on the main-thread which are in scrolled out ' +
|
||||
'elements should throttle restyling');
|
||||
|
||||
yield ensureElementRemoval(parentElement);
|
||||
});
|
||||
|
||||
add_task(function* restyling_main_thread_animations_moved_in_view_by_resizing() {
|
||||
if (!SpecialPowers.getBoolPref('dom.animations.offscreen-throttling')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parentElement = addDiv(null,
|
||||
{ style: 'overflow-y: scroll; height: 20px;' });
|
||||
var div = addDiv(null,
|
||||
{ style: 'animation: background-color 100s; position: relative; top: 100px;' });
|
||||
parentElement.appendChild(div);
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
yield animation.ready;
|
||||
|
||||
var markers = yield observeStyling(1, function() {
|
||||
parentElement.style.height = '100px';
|
||||
});
|
||||
|
||||
is(markers.length, 1,
|
||||
'Animations running on the main-thread which was in scrolled out ' +
|
||||
'elements should update restyling soon after the element moved in ' +
|
||||
'view by resizing');
|
||||
|
||||
yield ensureElementRemoval(parentElement);
|
||||
});
|
||||
|
||||
add_task(function* no_restyling_main_thread_animations_in_visiblily_hidden_element() {
|
||||
var div = addDiv(null,
|
||||
|
@ -131,12 +131,12 @@ function waitForFrame() {
|
||||
function waitForAnimationFrames(frameCount, onFrame) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function handleFrame() {
|
||||
if (onFrame && typeof onFrame === 'function') {
|
||||
onFrame();
|
||||
}
|
||||
if (--frameCount <= 0) {
|
||||
resolve();
|
||||
} else {
|
||||
if (onFrame && typeof onFrame === 'function') {
|
||||
onFrame();
|
||||
}
|
||||
window.requestAnimationFrame(handleFrame); // wait another frame
|
||||
}
|
||||
}
|
||||
|
@ -618,20 +618,20 @@ this.AppsUtils = {
|
||||
isFirstRun: function isFirstRun(aPrefBranch) {
|
||||
let savedmstone = null;
|
||||
try {
|
||||
savedmstone = aPrefBranch.getCharPref("gecko.mstone");
|
||||
savedmstone = aPrefBranch.getCharPref("dom.apps.lastUpdate.mstone");
|
||||
} catch (e) {}
|
||||
|
||||
let mstone = Services.appinfo.platformVersion;
|
||||
|
||||
let savedBuildID = null;
|
||||
try {
|
||||
savedBuildID = aPrefBranch.getCharPref("gecko.buildID");
|
||||
savedBuildID = aPrefBranch.getCharPref("dom.apps.lastUpdate.buildID");
|
||||
} catch (e) {}
|
||||
|
||||
let buildID = Services.appinfo.platformBuildID;
|
||||
|
||||
aPrefBranch.setCharPref("gecko.mstone", mstone);
|
||||
aPrefBranch.setCharPref("gecko.buildID", buildID);
|
||||
aPrefBranch.setCharPref("dom.apps.lastUpdate.mstone", mstone);
|
||||
aPrefBranch.setCharPref("dom.apps.lastUpdate.buildID", buildID);
|
||||
|
||||
if ((mstone != savedmstone) || (buildID != savedBuildID)) {
|
||||
aPrefBranch.setBoolPref("dom.apps.reset-permissions", false);
|
||||
|
@ -495,7 +495,7 @@ Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri = bindingURL->GetURI();
|
||||
nsCOMPtr<nsIPrincipal> principal = bindingURL->mOriginPrincipal;
|
||||
nsCOMPtr<nsIPrincipal> principal = bindingURL->mOriginPrincipal.get();
|
||||
|
||||
// We have a binding that must be installed.
|
||||
bool dummy;
|
||||
@ -3077,10 +3077,6 @@ nsDOMTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty,
|
||||
|
||||
static nsIAtom** sPropertiesToTraverseAndUnlink[] =
|
||||
{
|
||||
&nsGkAtoms::microdataProperties,
|
||||
&nsGkAtoms::itemtype,
|
||||
&nsGkAtoms::itemref,
|
||||
&nsGkAtoms::itemprop,
|
||||
&nsGkAtoms::sandbox,
|
||||
&nsGkAtoms::sizes,
|
||||
nullptr
|
||||
@ -3122,26 +3118,6 @@ Element::GetTokenList(nsIAtom* aAtom,
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
Element::GetTokenList(nsIAtom* aAtom, nsIVariant** aResult)
|
||||
{
|
||||
nsISupports* itemType = GetTokenList(aAtom);
|
||||
nsCOMPtr<nsIWritableVariant> out = new nsVariant();
|
||||
out->SetAsInterface(NS_GET_IID(nsISupports), itemType);
|
||||
out.forget(aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Element::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue)
|
||||
{
|
||||
nsDOMTokenList* itemType = GetTokenList(aAtom);
|
||||
nsAutoString string;
|
||||
aValue->GetAsAString(string);
|
||||
ErrorResult rv;
|
||||
itemType->SetValue(string, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
Element*
|
||||
Element::Closest(const nsAString& aSelector, ErrorResult& aResult)
|
||||
{
|
||||
|
@ -1336,8 +1336,6 @@ protected:
|
||||
|
||||
nsDOMTokenList* GetTokenList(nsIAtom* aAtom,
|
||||
const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
|
||||
void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult);
|
||||
nsresult SetTokenList(nsIAtom* aAtom, nsIVariant* aValue);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
@ -147,7 +147,8 @@ ImportLoader::Updater::UpdateMainReferrer(uint32_t aNewIdx)
|
||||
if (mLoader->IsBlocking()) {
|
||||
// Our import parent is changed, let's block the new one and later unblock
|
||||
// the old one.
|
||||
newMainReferrer->OwnerDoc()->ScriptLoader()->AddExecuteBlocker();
|
||||
newMainReferrer->OwnerDoc()->
|
||||
ScriptLoader()->AddParserBlockingScriptExecutionBlocker();
|
||||
newMainReferrer->OwnerDoc()->BlockDOMContentLoaded();
|
||||
}
|
||||
|
||||
@ -167,7 +168,8 @@ ImportLoader::Updater::UpdateMainReferrer(uint32_t aNewIdx)
|
||||
}
|
||||
|
||||
if (mLoader->IsBlocking()) {
|
||||
mLoader->mImportParent->ScriptLoader()->RemoveExecuteBlocker();
|
||||
mLoader->mImportParent->
|
||||
ScriptLoader()->RemoveParserBlockingScriptExecutionBlocker();
|
||||
mLoader->mImportParent->UnblockDOMContentLoaded();
|
||||
}
|
||||
|
||||
@ -300,7 +302,7 @@ void
|
||||
ImportLoader::BlockScripts()
|
||||
{
|
||||
MOZ_ASSERT(!mBlockingScripts);
|
||||
mImportParent->ScriptLoader()->AddExecuteBlocker();
|
||||
mImportParent->ScriptLoader()->AddParserBlockingScriptExecutionBlocker();
|
||||
mImportParent->BlockDOMContentLoaded();
|
||||
mBlockingScripts = true;
|
||||
}
|
||||
@ -309,10 +311,10 @@ void
|
||||
ImportLoader::UnblockScripts()
|
||||
{
|
||||
MOZ_ASSERT(mBlockingScripts);
|
||||
mImportParent->ScriptLoader()->RemoveExecuteBlocker();
|
||||
mImportParent->ScriptLoader()->RemoveParserBlockingScriptExecutionBlocker();
|
||||
mImportParent->UnblockDOMContentLoaded();
|
||||
for (uint32_t i = 0; i < mBlockedScriptLoaders.Length(); i++) {
|
||||
mBlockedScriptLoaders[i]->RemoveExecuteBlocker();
|
||||
mBlockedScriptLoaders[i]->RemoveParserBlockingScriptExecutionBlocker();
|
||||
}
|
||||
mBlockedScriptLoaders.Clear();
|
||||
mBlockingScripts = false;
|
||||
@ -343,7 +345,7 @@ ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader)
|
||||
return;
|
||||
}
|
||||
|
||||
aScriptLoader->AddExecuteBlocker();
|
||||
aScriptLoader->AddParserBlockingScriptExecutionBlocker();
|
||||
|
||||
// Let's keep track of the pending script loaders.
|
||||
mBlockedScriptLoaders.AppendElement(aScriptLoader);
|
||||
@ -352,7 +354,7 @@ ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader)
|
||||
bool
|
||||
ImportLoader::RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader)
|
||||
{
|
||||
aScriptLoader->RemoveExecuteBlocker();
|
||||
aScriptLoader->RemoveParserBlockingScriptExecutionBlocker();
|
||||
return mBlockedScriptLoaders.RemoveElement(aScriptLoader);
|
||||
}
|
||||
|
||||
|
@ -1610,6 +1610,11 @@ Navigator::GetDeprecatedBattery(ErrorResult& aRv)
|
||||
mBatteryManager->Init();
|
||||
}
|
||||
|
||||
nsIDocument* doc = mWindow->GetDoc();
|
||||
if (doc) {
|
||||
doc->WarnOnceAbout(nsIDocument::eNavigatorBattery);
|
||||
}
|
||||
|
||||
return mBatteryManager;
|
||||
}
|
||||
|
||||
@ -2403,40 +2408,38 @@ Navigator::HasPresentationSupport(JSContext* aCx, JSObject* aGlobal)
|
||||
|
||||
// Grant access to browser receiving pages and their same-origin iframes. (App
|
||||
// pages should be controlled by "presentation" permission in app manifests.)
|
||||
mozilla::dom::ContentChild* cc =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
if (!cc || !cc->IsForBrowser()) {
|
||||
nsCOMPtr<nsIDocShell> docshell = inner->GetDocShell();
|
||||
if (!docshell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> win = inner->GetOuterWindow();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop();
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(win);
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> topSop = do_QueryInterface(top);
|
||||
if (!sop || !topSop) {
|
||||
if (!docshell->GetIsInMozBrowserOrApp()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIPrincipal* principal = sop->GetPrincipal();
|
||||
nsIPrincipal* topPrincipal = topSop->GetPrincipal();
|
||||
if (!principal || !topPrincipal || !principal->Subsumes(topPrincipal)) {
|
||||
nsAutoString presentationURL;
|
||||
nsContentUtils::GetPresentationURL(docshell, presentationURL);
|
||||
|
||||
if (presentationURL.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> topInner;
|
||||
if (!(topInner = top->GetCurrentInnerWindow())) {
|
||||
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
if (!securityManager) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresentationService> presentationService =
|
||||
do_GetService(PRESENTATION_SERVICE_CONTRACTID);
|
||||
if (NS_WARN_IF(!presentationService)) {
|
||||
nsCOMPtr<nsIURI> presentationURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(presentationURI), presentationURL);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString sessionId;
|
||||
presentationService->GetExistentSessionIdAtLaunch(topInner->WindowID(), sessionId);
|
||||
return !sessionId.IsEmpty();
|
||||
nsCOMPtr<nsIURI> docURI = inner->GetDocumentURI();
|
||||
return NS_SUCCEEDED(securityManager->CheckSameOriginURI(presentationURI,
|
||||
docURI,
|
||||
false));
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -118,7 +118,6 @@ DOM4_MSG_DEF(BtAuthRejectedError, "Authentication rejected", NS_ERROR_DOM_BLUET
|
||||
/* Web Animations errors */
|
||||
|
||||
DOM4_MSG_DEF(NotSupportedError, "Animation to or from an underlying value is not yet supported.", NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR)
|
||||
DOM4_MSG_DEF(NotSupportedError, "Animation with no timeline is not yet supported.", NS_ERROR_DOM_ANIM_NO_TIMELINE_ERR)
|
||||
DOM4_MSG_DEF(NotSupportedError, "Animation with no effect is not yet supported.", NS_ERROR_DOM_ANIM_NO_EFFECT_ERR)
|
||||
|
||||
/* common global codes (from nsError.h) */
|
||||
|
@ -235,7 +235,7 @@ nsContentSink::StyleSheetLoaded(StyleSheetHandle aSheet,
|
||||
ScrollToRef();
|
||||
}
|
||||
|
||||
mScriptLoader->RemoveExecuteBlocker();
|
||||
mScriptLoader->RemoveParserBlockingScriptExecutionBlocker();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -772,7 +772,7 @@ nsContentSink::ProcessStyleLink(nsIContent* aElement,
|
||||
|
||||
if (!isAlternate && !mRunsToCompletion) {
|
||||
++mPendingSheetCount;
|
||||
mScriptLoader->AddExecuteBlocker();
|
||||
mScriptLoader->AddParserBlockingScriptExecutionBlocker();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -115,6 +115,7 @@
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMDocumentType.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMHTMLElement.h"
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
@ -203,6 +204,7 @@
|
||||
#include "nsICookieService.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
#include "mozilla/BloomFilter.h"
|
||||
#include "TabChild.h"
|
||||
|
||||
#include "nsIBidiKeyboard.h"
|
||||
|
||||
@ -717,7 +719,7 @@ nsContentUtils::InitializeEventTable() {
|
||||
|
||||
static const EventNameMapping eventArray[] = {
|
||||
#define EVENT(name_, _message, _type, _class) \
|
||||
{ nsGkAtoms::on##name_, _type, _message, _class },
|
||||
{ nsGkAtoms::on##name_, _type, _message, _class, false },
|
||||
#define WINDOW_ONLY_EVENT EVENT
|
||||
#define NON_IDL_EVENT EVENT
|
||||
#include "mozilla/EventNameList.h"
|
||||
@ -3687,10 +3689,51 @@ nsContentUtils::GetEventMessageAndAtom(const nsAString& aName,
|
||||
mapping.mMessage = eUnidentifiedEvent;
|
||||
mapping.mType = EventNameType_None;
|
||||
mapping.mEventClassID = eBasicEventClass;
|
||||
// This is a slow hashtable call, but at least we cache the result for the
|
||||
// following calls. Because GetEventMessageAndAtomForListener utilizes
|
||||
// sStringEventTable, it needs to know in which cases sStringEventTable
|
||||
// doesn't contain the information it needs so that it can use
|
||||
// sAtomEventTable instead.
|
||||
mapping.mMaybeSpecialSVGorSMILEvent =
|
||||
GetEventMessage(atom) != eUnidentifiedEvent;
|
||||
sStringEventTable->Put(aName, mapping);
|
||||
return mapping.mAtom;
|
||||
}
|
||||
|
||||
// static
|
||||
EventMessage
|
||||
nsContentUtils::GetEventMessageAndAtomForListener(const nsAString& aName,
|
||||
nsIAtom** aOnName)
|
||||
{
|
||||
// Because of SVG/SMIL sStringEventTable contains a subset of the event names
|
||||
// comparing to the sAtomEventTable. However, usually sStringEventTable
|
||||
// contains the information we need, so in order to reduce hashtable
|
||||
// lookups, start from it.
|
||||
EventNameMapping mapping;
|
||||
EventMessage msg = eUnidentifiedEvent;
|
||||
nsCOMPtr<nsIAtom> atom;
|
||||
if (sStringEventTable->Get(aName, &mapping)) {
|
||||
if (mapping.mMaybeSpecialSVGorSMILEvent) {
|
||||
// Try the atom version so that we should get the right message for
|
||||
// SVG/SMIL.
|
||||
atom = NS_Atomize(NS_LITERAL_STRING("on") + aName);
|
||||
msg = GetEventMessage(atom);
|
||||
} else {
|
||||
atom = mapping.mAtom;
|
||||
msg = mapping.mMessage;
|
||||
}
|
||||
atom.forget(aOnName);
|
||||
return msg;
|
||||
}
|
||||
|
||||
// GetEventMessageAndAtom will cache the event type for the future usage...
|
||||
GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
|
||||
|
||||
// ...and then call this method recursively to get the message and atom from
|
||||
// now updated sStringEventTable.
|
||||
return GetEventMessageAndAtomForListener(aName, aOnName);
|
||||
}
|
||||
|
||||
static
|
||||
nsresult GetEventAndTarget(nsIDocument* aDoc, nsISupports* aTarget,
|
||||
const nsAString& aEventName,
|
||||
@ -8836,3 +8879,31 @@ nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell, bool aVisible)
|
||||
nsIScrollable::ScrollOrientation_X, prefValue);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::GetPresentationURL(nsIDocShell* aDocShell, nsAString& aPresentationUrl)
|
||||
{
|
||||
MOZ_ASSERT(aDocShell);
|
||||
|
||||
if (XRE_IsContentProcess()) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
||||
aDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
||||
nsCOMPtr<nsIDocShellTreeItem> root;
|
||||
aDocShell->GetRootTreeItem(getter_AddRefs(root));
|
||||
if (sameTypeRoot.get() == root.get()) {
|
||||
// presentation URL is stored in TabChild for the top most
|
||||
// <iframe mozbrowser> in content process.
|
||||
TabChild* tabChild = TabChild::GetFrom(aDocShell);
|
||||
if (tabChild) {
|
||||
aPresentationUrl = tabChild->PresentationURL();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(aDocShell));
|
||||
nsCOMPtr<nsIDOMElement> topFrameElement;
|
||||
loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
|
||||
|
||||
topFrameElement->GetAttribute(NS_LITERAL_STRING("mozpresentation"), aPresentationUrl);
|
||||
}
|
||||
|
@ -169,6 +169,9 @@ struct EventNameMapping
|
||||
int32_t mType;
|
||||
mozilla::EventMessage mMessage;
|
||||
mozilla::EventClassID mEventClassID;
|
||||
// True if mAtom is possibly used by special SVG/SMIL events, but
|
||||
// mMessage is eUnidentifiedEvent. See EventNameList.h
|
||||
bool mMaybeSpecialSVGorSMILEvent;
|
||||
};
|
||||
|
||||
typedef bool (*CallOnRemoteChildFunction) (mozilla::dom::TabParent* aTabParent,
|
||||
@ -1171,6 +1174,13 @@ public:
|
||||
*/
|
||||
static mozilla::EventMessage GetEventMessage(nsIAtom* aName);
|
||||
|
||||
/**
|
||||
* Returns the EventMessage and nsIAtom to be used for event listener
|
||||
* registration.
|
||||
*/
|
||||
static mozilla::EventMessage
|
||||
GetEventMessageAndAtomForListener(const nsAString& aName, nsIAtom** aOnName);
|
||||
|
||||
/**
|
||||
* Return the EventClassID for the event with the given name. The name is the
|
||||
* event name *without* the 'on' prefix. Returns eBasicEventClass if the event
|
||||
@ -2553,6 +2563,12 @@ public:
|
||||
|
||||
static void SetScrollbarsVisibility(nsIDocShell* aDocShell, bool aVisible);
|
||||
|
||||
/*
|
||||
* Return the associated presentation URL of the presented content.
|
||||
* Will return empty string if the docshell is not in a presented content.
|
||||
*/
|
||||
static void GetPresentationURL(nsIDocShell* aDocShell, nsAString& aPresentationUrl);
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
|
@ -42,6 +42,8 @@ DEPRECATED_OPERATION(ImportXULIntoContent)
|
||||
DEPRECATED_OPERATION(PannerNodeDoppler)
|
||||
DEPRECATED_OPERATION(NavigatorGetUserMedia)
|
||||
DEPRECATED_OPERATION(WebrtcDeprecatedPrefix)
|
||||
DEPRECATED_OPERATION(RTCPeerConnectionGetStreams)
|
||||
DEPRECATED_OPERATION(AppCache)
|
||||
DEPRECATED_OPERATION(PrefixedFullscreenAPI)
|
||||
DEPRECATED_OPERATION(LenientSetter)
|
||||
DEPRECATED_OPERATION(NavigatorBattery)
|
||||
|
@ -2752,7 +2752,6 @@ nsDocument::ApplySettingsFromCSP(bool aSpeculative)
|
||||
nsresult
|
||||
nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
{
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
if (!CSPService::sCSPEnabled) {
|
||||
MOZ_LOG(gCspPRLog, LogLevel::Debug,
|
||||
("CSP is disabled, skipping CSP init for document %p", this));
|
||||
@ -2872,6 +2871,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->EnsureCSP(this, getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -9765,6 +9765,9 @@ nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
|
||||
mAnimationsPaused += aIncrease;
|
||||
} else {
|
||||
mEventsSuppressed += aIncrease;
|
||||
for (uint32_t i = 0; i < aIncrease; ++i) {
|
||||
ScriptLoader()->AddExecuteBlocker();
|
||||
}
|
||||
}
|
||||
|
||||
SuppressArgs args = { aWhat, aIncrease };
|
||||
@ -10093,6 +10096,7 @@ GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
|
||||
if (args->mWhat != nsIDocument::eAnimationsOnly &&
|
||||
aDocument->EventHandlingSuppressed() > 0) {
|
||||
static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
|
||||
aDocument->ScriptLoader()->RemoveExecuteBlocker();
|
||||
} else if (args->mWhat == nsIDocument::eAnimationsOnly &&
|
||||
aDocument->AnimationsPaused()) {
|
||||
static_cast<nsDocument*>(aDocument)->ResumeAnimations();
|
||||
|
@ -343,6 +343,32 @@ nsFrameLoader::SetIsPrerendered()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::MakePrerenderedLoaderActive()
|
||||
{
|
||||
MOZ_ASSERT(mIsPrerendered, "This frameloader was not in prerendered mode.");
|
||||
|
||||
mIsPrerendered = false;
|
||||
if (IsRemoteFrame()) {
|
||||
if (!mRemoteBrowser) {
|
||||
NS_WARNING("Missing remote browser.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mRemoteBrowser->SetDocShellIsActive(true);
|
||||
} else {
|
||||
if (!mDocShell) {
|
||||
NS_WARNING("Missing docshell.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = mDocShell->SetIsActive(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameLoader::ReallyStartLoading()
|
||||
{
|
||||
@ -936,25 +962,8 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// When we swap docShells, maybe we have to deal with a new page created just
|
||||
// for this operation. In this case, the browser code should already have set
|
||||
// the correct userContextId attribute value in the owning XULElement, but our
|
||||
// docShell, that has been created way before) doesn't know that that
|
||||
// happened.
|
||||
// This is the reason why now we must retrieve the correct value from the
|
||||
// usercontextid attribute before comparing our originAttributes with the
|
||||
// other one.
|
||||
DocShellOriginAttributes ourOriginAttributes =
|
||||
mRemoteBrowser->OriginAttributesRef();
|
||||
rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
DocShellOriginAttributes otherOriginAttributes =
|
||||
aOther->mRemoteBrowser->OriginAttributesRef();
|
||||
rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
if (ourOriginAttributes != otherOriginAttributes) {
|
||||
if (mRemoteBrowser->OriginAttributesRef() !=
|
||||
aOther->mRemoteBrowser->OriginAttributesRef()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
@ -1334,25 +1343,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// When we swap docShells, maybe we have to deal with a new page created just
|
||||
// for this operation. In this case, the browser code should already have set
|
||||
// the correct userContextId attribute value in the owning XULElement, but our
|
||||
// docShell, that has been created way before) doesn't know that that
|
||||
// happened.
|
||||
// This is the reason why now we must retrieve the correct value from the
|
||||
// usercontextid attribute before comparing our originAttributes with the
|
||||
// other one.
|
||||
DocShellOriginAttributes ourOriginAttributes =
|
||||
ourDocshell->GetOriginAttributes();
|
||||
rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
DocShellOriginAttributes otherOriginAttributes =
|
||||
otherDocshell->GetOriginAttributes();
|
||||
rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
if (ourOriginAttributes != otherOriginAttributes) {
|
||||
if (ourDocshell->GetOriginAttributes() !=
|
||||
otherDocshell->GetOriginAttributes()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
@ -1966,7 +1958,7 @@ nsFrameLoader::MaybeCreateDocShell()
|
||||
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
|
||||
|
||||
if (mIsPrerendered) {
|
||||
nsresult rv = mDocShell->SetIsPrerendered(true);
|
||||
nsresult rv = mDocShell->SetIsPrerendered();
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
}
|
||||
|
||||
@ -2115,9 +2107,13 @@ nsFrameLoader::MaybeCreateDocShell()
|
||||
}
|
||||
|
||||
// Grab the userContextId from owner if XUL
|
||||
nsresult rv = PopulateUserContextIdFromAttribute(attrs);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
nsAutoString userContextIdStr;
|
||||
if ((namespaceID == kNameSpaceID_XUL) &&
|
||||
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, userContextIdStr) &&
|
||||
!userContextIdStr.IsEmpty()) {
|
||||
nsresult rv;
|
||||
attrs.mUserContextId = userContextIdStr.ToInteger(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
|
||||
@ -3361,34 +3357,20 @@ nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
|
||||
attrs.mUserContextId = userContextId;
|
||||
}
|
||||
|
||||
nsAutoString presentationURLStr;
|
||||
mOwnerContent->GetAttr(kNameSpaceID_None,
|
||||
nsGkAtoms::mozpresentation,
|
||||
presentationURLStr);
|
||||
|
||||
bool tabContextUpdated =
|
||||
aTabContext->SetTabContext(OwnerIsMozBrowserFrame(),
|
||||
mIsPrerendered,
|
||||
ownApp,
|
||||
containingApp,
|
||||
attrs,
|
||||
signedPkgOrigin);
|
||||
signedPkgOrigin,
|
||||
presentationURLStr);
|
||||
NS_ENSURE_STATE(tabContextUpdated);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameLoader::PopulateUserContextIdFromAttribute(DocShellOriginAttributes& aAttr)
|
||||
{
|
||||
if (aAttr.mUserContextId ==
|
||||
nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
|
||||
// Grab the userContextId from owner if XUL
|
||||
nsAutoString userContextIdStr;
|
||||
int32_t namespaceID = mOwnerContent->GetNameSpaceID();
|
||||
if ((namespaceID == kNameSpaceID_XUL) &&
|
||||
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid,
|
||||
userContextIdStr) &&
|
||||
!userContextIdStr.IsEmpty()) {
|
||||
nsresult rv;
|
||||
aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -39,9 +39,6 @@ class nsIDocShellTreeOwner;
|
||||
class mozIApplication;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DocShellOriginAttributes;
|
||||
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
class PBrowserParent;
|
||||
@ -343,9 +340,6 @@ private:
|
||||
};
|
||||
void MaybeUpdatePrimaryTabParent(TabParentChange aChange);
|
||||
|
||||
nsresult
|
||||
PopulateUserContextIdFromAttribute(mozilla::DocShellOriginAttributes& aAttr);
|
||||
|
||||
nsCOMPtr<nsIDocShell> mDocShell;
|
||||
nsCOMPtr<nsIURI> mURIToLoad;
|
||||
mozilla::dom::Element* mOwnerContent; // WEAK
|
||||
|
@ -602,7 +602,6 @@ GK_ATOM(referrer, "referrer")
|
||||
GK_ATOM(referrerpolicy, "referrerpolicy")
|
||||
GK_ATOM(meter, "meter")
|
||||
GK_ATOM(method, "method")
|
||||
GK_ATOM(microdataProperties, "microdataProperties")
|
||||
GK_ATOM(middle, "middle")
|
||||
GK_ATOM(min, "min")
|
||||
GK_ATOM(minheight, "minheight")
|
||||
@ -1014,6 +1013,7 @@ GK_ATOM(predicate, "predicate")
|
||||
GK_ATOM(prefix, "prefix")
|
||||
GK_ATOM(preload, "preload")
|
||||
GK_ATOM(prerendered, "prerendered")
|
||||
GK_ATOM(mozpresentation, "mozpresentation")
|
||||
GK_ATOM(preserve, "preserve")
|
||||
GK_ATOM(preserveSpace, "preserve-space")
|
||||
GK_ATOM(preventdefault, "preventdefault")
|
||||
|
@ -29,8 +29,12 @@
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsCopySupport.h"
|
||||
#include "nsIClipboard.h"
|
||||
#include "ContentEventHandler.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIWordBreaker.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
|
||||
#include "nsIClipboardDragDropHooks.h"
|
||||
@ -986,6 +990,163 @@ nsClipboardDragDropHookCommand::GetCommandStateParams(const char *aCommandName,
|
||||
return aParams->SetBooleanValue("state_enabled", true);
|
||||
}
|
||||
|
||||
class nsLookUpDictionaryCommand final : public nsIControllerCommand
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTROLLERCOMMAND
|
||||
|
||||
private:
|
||||
virtual ~nsLookUpDictionaryCommand()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsLookUpDictionaryCommand, nsIControllerCommand)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLookUpDictionaryCommand::IsCommandEnabled(
|
||||
const char* aCommandName,
|
||||
nsISupports* aCommandContext,
|
||||
bool* aRetval)
|
||||
{
|
||||
*aRetval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLookUpDictionaryCommand::GetCommandStateParams(const char* aCommandName,
|
||||
nsICommandParams* aParams,
|
||||
nsISupports* aCommandContext)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLookUpDictionaryCommand::DoCommand(const char* aCommandName,
|
||||
nsISupports *aCommandContext)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLookUpDictionaryCommand::DoCommandParams(const char* aCommandName,
|
||||
nsICommandParams* aParams,
|
||||
nsISupports* aCommandContext)
|
||||
{
|
||||
if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
|
||||
nsresult rv = aParams->GetLongValue("x", &x);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = aParams->GetLongValue("y", &y);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
LayoutDeviceIntPoint point(x, y);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aCommandContext);
|
||||
if (NS_WARN_IF(!window)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = window->GetDocShell();
|
||||
if (NS_WARN_IF(!docShell)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
||||
if (NS_WARN_IF(!presShell)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
if (NS_WARN_IF(!presContext)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
|
||||
if (NS_WARN_IF(!widget)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
WidgetQueryContentEvent charAt(true, eQueryCharacterAtPoint, widget);
|
||||
charAt.mRefPoint.x = x;
|
||||
charAt.mRefPoint.y = y;
|
||||
ContentEventHandler handler(presContext);
|
||||
handler.OnQueryCharacterAtPoint(&charAt);
|
||||
|
||||
if (NS_WARN_IF(!charAt.mSucceeded) ||
|
||||
charAt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
WidgetQueryContentEvent textContent(true, eQueryTextContent, widget);
|
||||
// OSX 10.7 queries 50 characters before/after current point. So we fetch
|
||||
// same length.
|
||||
uint32_t offset = charAt.mReply.mOffset;
|
||||
if (offset > 50) {
|
||||
offset -= 50;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
textContent.InitForQueryTextContent(offset, 100);
|
||||
handler.OnQueryTextContent(&textContent);
|
||||
if (NS_WARN_IF(!textContent.mSucceeded ||
|
||||
textContent.mReply.mString.IsEmpty())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// XXX nsIWordBreaker doesn't use contextual breaker.
|
||||
// If OS provides it, widget should use it if contextual breaker is needed.
|
||||
nsCOMPtr<nsIWordBreaker> wordBreaker = nsContentUtils::WordBreaker();
|
||||
if (NS_WARN_IF(!wordBreaker)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsWordRange range =
|
||||
wordBreaker->FindWord(textContent.mReply.mString.get(),
|
||||
textContent.mReply.mString.Length(),
|
||||
charAt.mReply.mOffset - offset);
|
||||
if (range.mEnd == range.mBegin) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
range.mBegin += offset;
|
||||
range.mEnd += offset;
|
||||
|
||||
WidgetQueryContentEvent lookUpContent(true, eQueryTextContent, widget);
|
||||
lookUpContent.InitForQueryTextContent(range.mBegin,
|
||||
range.mEnd - range.mBegin);
|
||||
lookUpContent.RequestFontRanges();
|
||||
handler.OnQueryTextContent(&lookUpContent);
|
||||
if (NS_WARN_IF(!lookUpContent.mSucceeded ||
|
||||
lookUpContent.mReply.mString.IsEmpty())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
WidgetQueryContentEvent charRect(true, eQueryTextRect, widget);
|
||||
charRect.InitForQueryTextRect(range.mBegin, range.mEnd - range.mBegin);
|
||||
handler.OnQueryTextRect(&charRect);
|
||||
if (NS_WARN_IF(!charRect.mSucceeded)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
widget->LookUpDictionary(lookUpContent.mReply.mString,
|
||||
lookUpContent.mReply.mFontRanges,
|
||||
charRect.mReply.mWritingMode.IsVertical(),
|
||||
charRect.mReply.mRect.TopLeft());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
|
||||
RegisterWindowCommands
|
||||
@ -1097,5 +1258,7 @@ nsWindowCommandRegistration::RegisterWindowCommands(
|
||||
|
||||
NS_REGISTER_ONE_COMMAND(nsClipboardDragDropHookCommand, "cmd_clipboardDragDropHook");
|
||||
|
||||
NS_REGISTER_ONE_COMMAND(nsLookUpDictionaryCommand, "cmd_lookUpDictionary");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
using mozilla::dom::BlobImpl;
|
||||
using mozilla::ErrorResult;
|
||||
using mozilla::LoadInfo;
|
||||
using mozilla::net::LoadInfo;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Hash table
|
||||
@ -720,7 +720,7 @@ nsFontTableProtocolHandler::NewURI(const nsACString& aSpec,
|
||||
// fonttable: scheme.
|
||||
// If aSpec is a relative URI -other- than a bare #ref,
|
||||
// this will leave uri empty, and we'll return a failure code below.
|
||||
uri = new nsSimpleURI();
|
||||
uri = new mozilla::net::nsSimpleURI();
|
||||
uri->SetSpec(aSpec);
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,8 @@ static NS_DEFINE_CID(kHOSTOBJECTURICID, NS_HOSTOBJECTURI_CID);
|
||||
static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
|
||||
NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHostObjectURI, nsSimpleURI)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHostObjectURI, nsSimpleURI)
|
||||
NS_IMPL_ADDREF_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsHostObjectURI)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
|
||||
@ -33,7 +33,7 @@ NS_INTERFACE_MAP_BEGIN(nsHostObjectURI)
|
||||
return NS_NOINTERFACE;
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
|
||||
NS_INTERFACE_MAP_END_INHERITING(mozilla::net::nsSimpleURI)
|
||||
|
||||
// nsIURIWithPrincipal methods:
|
||||
|
||||
@ -63,7 +63,7 @@ nsHostObjectURI::GetPrincipalUri(nsIURI** aUri)
|
||||
NS_IMETHODIMP
|
||||
nsHostObjectURI::Read(nsIObjectInputStream* aStream)
|
||||
{
|
||||
nsresult rv = nsSimpleURI::Read(aStream);
|
||||
nsresult rv = mozilla::net::nsSimpleURI::Read(aStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
@ -77,7 +77,7 @@ nsHostObjectURI::Read(nsIObjectInputStream* aStream)
|
||||
NS_IMETHODIMP
|
||||
nsHostObjectURI::Write(nsIObjectOutputStream* aStream)
|
||||
{
|
||||
nsresult rv = nsSimpleURI::Write(aStream);
|
||||
nsresult rv = mozilla::net::nsSimpleURI::Write(aStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_WriteOptionalCompoundObject(aStream, mPrincipal,
|
||||
@ -94,7 +94,7 @@ nsHostObjectURI::Serialize(mozilla::ipc::URIParams& aParams)
|
||||
HostObjectURIParams hostParams;
|
||||
URIParams simpleParams;
|
||||
|
||||
nsSimpleURI::Serialize(simpleParams);
|
||||
mozilla::net::nsSimpleURI::Serialize(simpleParams);
|
||||
hostParams.simpleParams() = simpleParams;
|
||||
|
||||
if (mPrincipal) {
|
||||
@ -124,7 +124,7 @@ nsHostObjectURI::Deserialize(const mozilla::ipc::URIParams& aParams)
|
||||
|
||||
const HostObjectURIParams& hostParams = aParams.get_HostObjectURIParams();
|
||||
|
||||
if (!nsSimpleURI::Deserialize(hostParams.simpleParams())) {
|
||||
if (!mozilla::net::nsSimpleURI::Deserialize(hostParams.simpleParams())) {
|
||||
return false;
|
||||
}
|
||||
if (hostParams.principal().type() == OptionalPrincipalInfo::Tvoid_t) {
|
||||
@ -146,12 +146,12 @@ nsHostObjectURI::SetScheme(const nsACString& aScheme)
|
||||
|
||||
// nsIURI methods:
|
||||
nsresult
|
||||
nsHostObjectURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
|
||||
nsHostObjectURI::CloneInternal(mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
|
||||
nsIURI** aClone)
|
||||
{
|
||||
nsCOMPtr<nsIURI> simpleClone;
|
||||
nsresult rv =
|
||||
nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
|
||||
mozilla::net::nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -170,7 +170,7 @@ nsHostObjectURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
|
||||
|
||||
/* virtual */ nsresult
|
||||
nsHostObjectURI::EqualsInternal(nsIURI* aOther,
|
||||
nsSimpleURI::RefHandlingEnum aRefHandlingMode,
|
||||
mozilla::net::nsSimpleURI::RefHandlingEnum aRefHandlingMode,
|
||||
bool* aResult)
|
||||
{
|
||||
if (!aOther) {
|
||||
@ -186,7 +186,7 @@ nsHostObjectURI::EqualsInternal(nsIURI* aOther,
|
||||
}
|
||||
|
||||
// Compare the member data that our base class knows about.
|
||||
if (!nsSimpleURI::EqualsInternal(otherUri, aRefHandlingMode)) {
|
||||
if (!mozilla::net::nsSimpleURI::EqualsInternal(otherUri, aRefHandlingMode)) {
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -21,16 +21,16 @@
|
||||
* MediaStreams, with scheme "mediastream", and MediaSources, with scheme
|
||||
* "mediasource".
|
||||
*/
|
||||
class nsHostObjectURI : public nsSimpleURI,
|
||||
class nsHostObjectURI : public mozilla::net::nsSimpleURI,
|
||||
public nsIURIWithPrincipal
|
||||
{
|
||||
public:
|
||||
explicit nsHostObjectURI(nsIPrincipal* aPrincipal) :
|
||||
nsSimpleURI(), mPrincipal(aPrincipal)
|
||||
mozilla::net::nsSimpleURI(), mPrincipal(aPrincipal)
|
||||
{}
|
||||
|
||||
// For use only from deserialization
|
||||
nsHostObjectURI() : nsSimpleURI() {}
|
||||
nsHostObjectURI() : mozilla::net::nsSimpleURI() {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIURIWITHPRINCIPAL
|
||||
@ -48,7 +48,7 @@ public:
|
||||
bool* aResult) override;
|
||||
|
||||
// Override StartClone to hand back a nsHostObjectURI
|
||||
virtual nsSimpleURI* StartClone(RefHandlingEnum /* unused */) override
|
||||
virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum /* unused */) override
|
||||
{ return new nsHostObjectURI(); }
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
@ -66,6 +66,11 @@ interface nsIFrameLoader : nsISupports
|
||||
*/
|
||||
void setIsPrerendered();
|
||||
|
||||
/**
|
||||
* Make the prerendered frameloader being active (and clear isPrerendered flag).
|
||||
*/
|
||||
void makePrerenderedLoaderActive();
|
||||
|
||||
/**
|
||||
* Destroy the frame loader and everything inside it. This will
|
||||
* clear the weak owner content reference.
|
||||
|
@ -112,6 +112,7 @@ static const char *kPrefBlockURIs = "browser.safebrowsing.blockedURIs.enabled";
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::net;
|
||||
|
||||
static LogModule*
|
||||
GetObjectLog()
|
||||
@ -3120,7 +3121,8 @@ nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
|
||||
thisContent->IsHTMLElement(nsGkAtoms::applet)) &&
|
||||
(aType == eFallbackUnsupported ||
|
||||
aType == eFallbackDisabled ||
|
||||
aType == eFallbackBlocklisted))
|
||||
aType == eFallbackBlocklisted ||
|
||||
aType == eFallbackAlternate))
|
||||
{
|
||||
for (nsIContent* child = thisContent->GetFirstChild(); child;
|
||||
child = child->GetNextNode(thisContent)) {
|
||||
@ -3129,7 +3131,8 @@ nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
|
||||
nsStyleUtil::IsSignificantChild(child, true, false)) {
|
||||
aType = eFallbackAlternate;
|
||||
}
|
||||
if (child->IsHTMLElement(nsGkAtoms::embed)) {
|
||||
if (child->IsHTMLElement(nsGkAtoms::embed) &&
|
||||
thisContent->IsHTMLElement(nsGkAtoms::object)) {
|
||||
HTMLSharedObjectElement* object = static_cast<HTMLSharedObjectElement*>(child);
|
||||
if (object) {
|
||||
object->StartObjectLoad(true, true);
|
||||
|
@ -1319,16 +1319,20 @@ nsPlainTextSerializer::AddToLine(const char16_t * aLineFragment,
|
||||
}
|
||||
// fallback if the line breaker is unavailable or failed
|
||||
if (!mLineBreaker) {
|
||||
goodSpace = mWrapColumn-prefixwidth;
|
||||
while (goodSpace >= 0 &&
|
||||
!nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) {
|
||||
goodSpace--;
|
||||
if (mCurrentLine.IsEmpty() || mWrapColumn < prefixwidth) {
|
||||
goodSpace = NS_LINEBREAKER_NEED_MORE_TEXT;
|
||||
} else {
|
||||
goodSpace = std::min(mWrapColumn - prefixwidth, mCurrentLine.Length() - 1);
|
||||
while (goodSpace >= 0 &&
|
||||
!nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) {
|
||||
goodSpace--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString restOfLine;
|
||||
if (goodSpace == NS_LINEBREAKER_NEED_MORE_TEXT) {
|
||||
// If we don't found a good place to break, accept long line and
|
||||
// If we didn't find a good place to break, accept long line and
|
||||
// try to find another place to break
|
||||
goodSpace=(prefixwidth>mWrapColumn+1)?1:mWrapColumn-prefixwidth+1;
|
||||
if (mLineBreaker) {
|
||||
|
@ -433,6 +433,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptLoader)
|
||||
|
||||
nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
|
||||
: mDocument(aDocument),
|
||||
mParserBlockingBlockerCount(0),
|
||||
mBlockerCount(0),
|
||||
mNumberOfProcessors(0),
|
||||
mEnabled(true),
|
||||
@ -479,7 +480,7 @@ nsScriptLoader::~nsScriptLoader()
|
||||
// Unblock the kids, in case any of them moved to a different document
|
||||
// subtree in the meantime and therefore aren't actually going away.
|
||||
for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
|
||||
mPendingChildLoaders[j]->RemoveExecuteBlocker();
|
||||
mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1459,7 +1460,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request->IsReadyToRun() && ReadyToExecuteScripts()) {
|
||||
if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) {
|
||||
// The request has already been loaded and there are no pending style
|
||||
// sheets. If the script comes from the network stream, cheat for
|
||||
// performance reasons and avoid a trip through the event loop.
|
||||
@ -1522,7 +1523,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
}
|
||||
request->mProgress = nsScriptLoadRequest::Progress::Ready;
|
||||
if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
|
||||
(!ReadyToExecuteScripts() || !mXSLTRequests.isEmpty())) {
|
||||
(!ReadyToExecuteParserBlockingScripts() || !mXSLTRequests.isEmpty())) {
|
||||
// Need to maintain order for XSLT-inserted scripts
|
||||
NS_ASSERTION(!mParserBlockingRequest,
|
||||
"Parser-blocking scripts and XSLT scripts in the same doc!");
|
||||
@ -1537,7 +1538,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
return false;
|
||||
}
|
||||
if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
|
||||
!ReadyToExecuteScripts()) {
|
||||
!ReadyToExecuteParserBlockingScripts()) {
|
||||
NS_ASSERTION(!mParserBlockingRequest,
|
||||
"There can be only one parser-blocking script at a time");
|
||||
mParserBlockingRequest = request;
|
||||
@ -1605,7 +1606,7 @@ nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest)
|
||||
aRequest->SetReady();
|
||||
|
||||
if (aRequest == mParserBlockingRequest) {
|
||||
if (!ReadyToExecuteScripts()) {
|
||||
if (!ReadyToExecuteParserBlockingScripts()) {
|
||||
// If not ready to execute scripts, schedule an async call to
|
||||
// ProcessPendingRequests to handle it.
|
||||
ProcessPendingRequestsAsync();
|
||||
@ -2045,7 +2046,7 @@ nsScriptLoader::ProcessPendingRequests()
|
||||
|
||||
if (mParserBlockingRequest &&
|
||||
mParserBlockingRequest->IsReadyToRun() &&
|
||||
ReadyToExecuteScripts()) {
|
||||
ReadyToExecuteParserBlockingScripts()) {
|
||||
request.swap(mParserBlockingRequest);
|
||||
UnblockParser(request);
|
||||
ProcessRequest(request);
|
||||
@ -2055,14 +2056,14 @@ nsScriptLoader::ProcessPendingRequests()
|
||||
ContinueParserAsync(request);
|
||||
}
|
||||
|
||||
while (ReadyToExecuteScripts() &&
|
||||
while (ReadyToExecuteParserBlockingScripts() &&
|
||||
!mXSLTRequests.isEmpty() &&
|
||||
mXSLTRequests.getFirst()->IsReadyToRun()) {
|
||||
request = mXSLTRequests.StealFirst();
|
||||
ProcessRequest(request);
|
||||
}
|
||||
|
||||
while (mEnabled && !mLoadedAsyncRequests.isEmpty()) {
|
||||
while (ReadyToExecuteScripts() && !mLoadedAsyncRequests.isEmpty()) {
|
||||
request = mLoadedAsyncRequests.StealFirst();
|
||||
if (request->IsModuleRequest()) {
|
||||
ProcessRequest(request);
|
||||
@ -2071,7 +2072,8 @@ nsScriptLoader::ProcessPendingRequests()
|
||||
}
|
||||
}
|
||||
|
||||
while (mEnabled && !mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
|
||||
while (ReadyToExecuteScripts() &&
|
||||
!mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
|
||||
mNonAsyncExternalScriptInsertedRequests.getFirst()->IsReadyToRun()) {
|
||||
// Violate the HTML5 spec and execute these in the insertion order in
|
||||
// order to make LABjs and the "order" plug-in for RequireJS work with
|
||||
@ -2082,16 +2084,19 @@ nsScriptLoader::ProcessPendingRequests()
|
||||
}
|
||||
|
||||
if (mDocumentParsingDone && mXSLTRequests.isEmpty()) {
|
||||
while (!mDeferRequests.isEmpty() && mDeferRequests.getFirst()->IsReadyToRun()) {
|
||||
while (ReadyToExecuteScripts() &&
|
||||
!mDeferRequests.isEmpty() &&
|
||||
mDeferRequests.getFirst()->IsReadyToRun()) {
|
||||
request = mDeferRequests.StealFirst();
|
||||
ProcessRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
|
||||
while (!mPendingChildLoaders.IsEmpty() &&
|
||||
ReadyToExecuteParserBlockingScripts()) {
|
||||
RefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
|
||||
mPendingChildLoaders.RemoveElementAt(0);
|
||||
child->RemoveExecuteBlocker();
|
||||
child->RemoveParserBlockingScriptExecutionBlocker();
|
||||
}
|
||||
|
||||
if (mDocumentParsingDone && mDocument && !mParserBlockingRequest &&
|
||||
@ -2115,19 +2120,19 @@ nsScriptLoader::ProcessPendingRequests()
|
||||
}
|
||||
|
||||
bool
|
||||
nsScriptLoader::ReadyToExecuteScripts()
|
||||
nsScriptLoader::ReadyToExecuteParserBlockingScripts()
|
||||
{
|
||||
// Make sure the SelfReadyToExecuteScripts check is first, so that
|
||||
// we don't block twice on an ancestor.
|
||||
if (!SelfReadyToExecuteScripts()) {
|
||||
// Make sure the SelfReadyToExecuteParserBlockingScripts check is first, so
|
||||
// that we don't block twice on an ancestor.
|
||||
if (!SelfReadyToExecuteParserBlockingScripts()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) {
|
||||
nsScriptLoader* ancestor = doc->ScriptLoader();
|
||||
if (!ancestor->SelfReadyToExecuteScripts() &&
|
||||
if (!ancestor->SelfReadyToExecuteParserBlockingScripts() &&
|
||||
ancestor->AddPendingChildLoader(this)) {
|
||||
AddExecuteBlocker();
|
||||
AddParserBlockingScriptExecutionBlocker();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2153,7 +2158,8 @@ nsScriptLoader::ReadyToExecuteScripts()
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = lastPred->GetDocument();
|
||||
if (lastPred->IsBlocking() || !doc || (doc && !doc->ScriptLoader()->SelfReadyToExecuteScripts())) {
|
||||
if (lastPred->IsBlocking() || !doc ||
|
||||
!doc->ScriptLoader()->SelfReadyToExecuteParserBlockingScripts()) {
|
||||
// Document has not been created yet or it was created but not ready.
|
||||
// Either case we are blocked by it. The ImportLoader will take care
|
||||
// of blocking us, and adding the pending child loader to the blocking
|
||||
|
@ -342,8 +342,24 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/remove blocker. Blockers will stop scripts from executing, but not
|
||||
* from loading.
|
||||
* Add/remove a blocker for parser-blocking scripts (and XSLT
|
||||
* scripts). Blockers will stop such scripts from executing, but not from
|
||||
* loading.
|
||||
*/
|
||||
void AddParserBlockingScriptExecutionBlocker()
|
||||
{
|
||||
++mParserBlockingBlockerCount;
|
||||
}
|
||||
void RemoveParserBlockingScriptExecutionBlocker()
|
||||
{
|
||||
if (!--mParserBlockingBlockerCount && ReadyToExecuteScripts()) {
|
||||
ProcessPendingRequestsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/remove a blocker for execution of all scripts. Blockers will stop
|
||||
* scripts from executing, but not from loading.
|
||||
*/
|
||||
void AddExecuteBlocker()
|
||||
{
|
||||
@ -351,6 +367,7 @@ public:
|
||||
}
|
||||
void RemoveExecuteBlocker()
|
||||
{
|
||||
MOZ_ASSERT(mBlockerCount);
|
||||
if (!--mBlockerCount) {
|
||||
ProcessPendingRequestsAsync();
|
||||
}
|
||||
@ -499,17 +516,26 @@ private:
|
||||
virtual void ProcessPendingRequestsAsync();
|
||||
|
||||
/**
|
||||
* If true, the loader is ready to execute scripts, and so are all its
|
||||
* ancestors. If the loader itself is ready but some ancestor is not, this
|
||||
* function will add an execute blocker and ask the ancestor to remove it
|
||||
* If true, the loader is ready to execute parser-blocking scripts, and so are
|
||||
* all its ancestors. If the loader itself is ready but some ancestor is not,
|
||||
* this function will add an execute blocker and ask the ancestor to remove it
|
||||
* once it becomes ready.
|
||||
*/
|
||||
bool ReadyToExecuteScripts();
|
||||
bool ReadyToExecuteParserBlockingScripts();
|
||||
|
||||
/**
|
||||
* Return whether just this loader is ready to execute scripts.
|
||||
* Return whether just this loader is ready to execute parser-blocking
|
||||
* scripts.
|
||||
*/
|
||||
bool SelfReadyToExecuteScripts()
|
||||
bool SelfReadyToExecuteParserBlockingScripts()
|
||||
{
|
||||
return ReadyToExecuteScripts() && !mParserBlockingBlockerCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this loader is ready to execute scripts in general.
|
||||
*/
|
||||
bool ReadyToExecuteScripts()
|
||||
{
|
||||
return mEnabled && !mBlockerCount;
|
||||
}
|
||||
@ -601,6 +627,7 @@ private:
|
||||
nsCOMPtr<nsIScriptElement> mCurrentScript;
|
||||
nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript;
|
||||
nsTArray< RefPtr<nsScriptLoader> > mPendingChildLoaders;
|
||||
uint32_t mParserBlockingBlockerCount;
|
||||
uint32_t mBlockerCount;
|
||||
uint32_t mNumberOfProcessors;
|
||||
bool mEnabled;
|
||||
|
@ -1,7 +1,5 @@
|
||||
[DEFAULT]
|
||||
b2g = true
|
||||
browser = false
|
||||
qemu = true
|
||||
run-if = buildapp == 'b2g'
|
||||
|
||||
[test_battery_level.js]
|
||||
[test_battery_status_charging.js]
|
||||
|
@ -17,11 +17,11 @@
|
||||
#include "js/Value.h"
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "mozilla/RootedOwningNonNull.h"
|
||||
#include "mozilla/RootedRefPtr.h"
|
||||
|
||||
#include "mozilla/dom/DOMString.h"
|
||||
|
||||
#include "nsAutoPtr.h" // for nsRefPtr member variables
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
|
@ -574,10 +574,6 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::dom::HTMLSharedElement'
|
||||
},
|
||||
|
||||
'HTMLPropertiesCollection': {
|
||||
'headerFile': 'HTMLPropertiesCollection.h',
|
||||
},
|
||||
|
||||
'HTMLQuoteElement': {
|
||||
'nativeType': 'mozilla::dom::HTMLSharedElement'
|
||||
},
|
||||
@ -929,10 +925,6 @@ DOMInterfaces = {
|
||||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'PropertyNodeList': {
|
||||
'headerFile': 'HTMLPropertiesCollection.h',
|
||||
},
|
||||
|
||||
'PushEvent': {
|
||||
'headerFile': 'ServiceWorkerEvents.h',
|
||||
'nativeType': 'mozilla::dom::workers::PushEvent',
|
||||
|
@ -56,6 +56,15 @@ protected:
|
||||
: CallbackObject(aCallbackFunction)
|
||||
{
|
||||
}
|
||||
|
||||
// See CallbackObject for an explanation of the arguments.
|
||||
CallbackFunction(JSContext* aCx, JS::Handle<JSObject*> aCallable,
|
||||
nsIGlobalObject* aIncumbentGlobal,
|
||||
const FastCallbackConstructor&)
|
||||
: CallbackObject(aCx, aCallable, aIncumbentGlobal,
|
||||
FastCallbackConstructor())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -26,7 +26,7 @@ class CallbackInterface : public CallbackObject
|
||||
public:
|
||||
// See CallbackObject for an explanation of the arguments.
|
||||
explicit CallbackInterface(JSContext* aCx, JS::Handle<JSObject*> aCallback,
|
||||
nsIGlobalObject *aIncumbentGlobal)
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
: CallbackObject(aCx, aCallback, aIncumbentGlobal)
|
||||
{
|
||||
}
|
||||
@ -43,6 +43,14 @@ protected:
|
||||
bool GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
|
||||
JS::MutableHandle<JS::Value> aCallable);
|
||||
|
||||
// See CallbackObject for an explanation of the arguments.
|
||||
CallbackInterface(JSContext* aCx, JS::Handle<JSObject*> aCallable,
|
||||
nsIGlobalObject* aIncumbentGlobal,
|
||||
const FastCallbackConstructor&)
|
||||
: CallbackObject(aCx, aCallable, aIncumbentGlobal,
|
||||
FastCallbackConstructor())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -47,6 +47,27 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mIncumbentJSGlobal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
void
|
||||
CallbackObject::Trace(JSTracer* aTracer)
|
||||
{
|
||||
JS::TraceEdge(aTracer, &mCallback, "CallbackObject.mCallback");
|
||||
JS::TraceEdge(aTracer, &mCreationStack, "CallbackObject.mCreationStack");
|
||||
JS::TraceEdge(aTracer, &mIncumbentJSGlobal,
|
||||
"CallbackObject.mIncumbentJSGlobal");
|
||||
}
|
||||
|
||||
void
|
||||
CallbackObject::HoldJSObjectsIfMoreThanOneOwner()
|
||||
{
|
||||
MOZ_ASSERT(mRefCnt.get() > 0);
|
||||
if (mRefCnt.get() > 1) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
} else {
|
||||
// We can just forget all our stuff.
|
||||
ClearJSReferences();
|
||||
}
|
||||
}
|
||||
|
||||
CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
||||
ErrorResult& aRv,
|
||||
const char* aExecutionReason,
|
||||
|
@ -25,11 +25,13 @@
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "jsapi.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -54,7 +56,7 @@ public:
|
||||
// is invoked. aCx can be nullptr, in which case no stack is
|
||||
// captured.
|
||||
explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
|
||||
nsIGlobalObject *aIncumbentGlobal)
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
{
|
||||
if (aCx && JS::RuntimeOptionsRef(aCx).asyncStack()) {
|
||||
JS::RootedObject stack(aCx);
|
||||
@ -72,7 +74,7 @@ public:
|
||||
// for that purpose.
|
||||
explicit CallbackObject(JS::Handle<JSObject*> aCallback,
|
||||
JS::Handle<JSObject*> aAsyncStack,
|
||||
nsIGlobalObject *aIncumbentGlobal)
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
{
|
||||
Init(aCallback, aAsyncStack, aIncumbentGlobal);
|
||||
}
|
||||
@ -163,8 +165,8 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
inline void Init(JSObject* aCallback, JSObject* aCreationStack,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
inline void InitNoHold(JSObject* aCallback, JSObject* aCreationStack,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
{
|
||||
MOZ_ASSERT(aCallback && !mCallback);
|
||||
// Set script objects before we hold, on the off chance that a GC could
|
||||
@ -175,9 +177,22 @@ private:
|
||||
mIncumbentGlobal = aIncumbentGlobal;
|
||||
mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObject();
|
||||
}
|
||||
}
|
||||
|
||||
inline void Init(JSObject* aCallback, JSObject* aCreationStack,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
{
|
||||
InitNoHold(aCallback, aCreationStack, aIncumbentGlobal);
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
inline void ClearJSReferences()
|
||||
{
|
||||
mCallback = nullptr;
|
||||
mCreationStack = nullptr;
|
||||
mIncumbentJSGlobal = nullptr;
|
||||
}
|
||||
|
||||
CallbackObject(const CallbackObject&) = delete;
|
||||
CallbackObject& operator =(const CallbackObject&) = delete;
|
||||
|
||||
@ -186,13 +201,46 @@ protected:
|
||||
{
|
||||
MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback);
|
||||
if (mCallback) {
|
||||
mCallback = nullptr;
|
||||
mCreationStack = nullptr;
|
||||
mIncumbentJSGlobal = nullptr;
|
||||
ClearJSReferences();
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
}
|
||||
|
||||
// For use from subclasses that want to be usable with Rooted.
|
||||
void Trace(JSTracer* aTracer);
|
||||
|
||||
// For use from subclasses that want to be traced for a bit then possibly
|
||||
// switch to HoldJSObjects. If we have more than one owner, this will
|
||||
// HoldJSObjects; otherwise it will just forget all our JS references.
|
||||
void HoldJSObjectsIfMoreThanOneOwner();
|
||||
|
||||
// Struct used as a way to force a CallbackObject constructor to not call
|
||||
// HoldJSObjects. We're putting it here so that CallbackObject subclasses will
|
||||
// have access to it, but outside code will not.
|
||||
//
|
||||
// Places that use this need to ensure that the callback is traced (e.g. via a
|
||||
// Rooted) until the HoldJSObjects call happens.
|
||||
struct FastCallbackConstructor {
|
||||
};
|
||||
|
||||
// Just like the public version without the FastCallbackConstructor argument,
|
||||
// except for not calling HoldJSObjects. If you use this, you MUST ensure
|
||||
// that the object is traced until the HoldJSObjects happens!
|
||||
CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
|
||||
nsIGlobalObject* aIncumbentGlobal,
|
||||
const FastCallbackConstructor&)
|
||||
{
|
||||
if (aCx && JS::RuntimeOptionsRef(aCx).asyncStack()) {
|
||||
JS::RootedObject stack(aCx);
|
||||
if (!JS::CaptureCurrentStack(aCx, &stack)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
InitNoHold(aCallback, stack, aIncumbentGlobal);
|
||||
} else {
|
||||
InitNoHold(aCallback, nullptr, aIncumbentGlobal);
|
||||
}
|
||||
}
|
||||
|
||||
// mCallback is not unwrapped, so it can be a cross-compartment-wrapper.
|
||||
// This is done to ensure that, if JS code can't call a callback f(), or get
|
||||
// its members, directly itself, this code won't call f(), or get its members,
|
||||
@ -473,6 +521,67 @@ ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField)
|
||||
aField.UnlinkSelf();
|
||||
}
|
||||
|
||||
// T is expected to be a RefPtr or OwningNonNull around a CallbackObject
|
||||
// subclass. This class is used in bindings to safely handle Fast* callbacks;
|
||||
// it ensures that the callback is traced, and that if something is holding onto
|
||||
// the callback when we're done with it HoldJSObjects is called.
|
||||
template<typename T>
|
||||
class RootedCallback : public JS::Rooted<T>
|
||||
{
|
||||
public:
|
||||
explicit RootedCallback(JSContext* cx)
|
||||
: JS::Rooted<T>(cx)
|
||||
{}
|
||||
|
||||
// We need a way to make assignment from pointers (how we're normally used)
|
||||
// work.
|
||||
template<typename S>
|
||||
void operator=(S* arg)
|
||||
{
|
||||
this->get().operator=(arg);
|
||||
}
|
||||
|
||||
// But nullptr can't use the above template, because it doesn't know which S
|
||||
// to select. So we need a special overload for nullptr.
|
||||
void operator=(decltype(nullptr) arg)
|
||||
{
|
||||
this->get().operator=(arg);
|
||||
}
|
||||
|
||||
// Codegen relies on being able to do Callback() on us.
|
||||
JS::Handle<JSObject*> Callback() const
|
||||
{
|
||||
return this->get()->Callback();
|
||||
}
|
||||
|
||||
~RootedCallback()
|
||||
{
|
||||
// Ensure that our callback starts holding on to its own JS objects as
|
||||
// needed. Having to null-check here when T is OwningNonNull is a bit
|
||||
// silly, but it's simpler than creating two separate RootedCallback
|
||||
// instantiations for OwningNonNull and RefPtr.
|
||||
if (IsInitialized(this->get())) {
|
||||
this->get()->HoldJSObjectsIfMoreThanOneOwner();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename U>
|
||||
static bool IsInitialized(U& aArg); // Not implemented
|
||||
|
||||
template<typename U>
|
||||
static bool IsInitialized(RefPtr<U>& aRefPtr)
|
||||
{
|
||||
return aRefPtr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
static bool IsInitialized(OwningNonNull<U>& aOwningNonNull)
|
||||
{
|
||||
return aOwningNonNull.isInitialized();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -4186,6 +4186,37 @@ class CGCallbackTempRoot(CGGeneric):
|
||||
CGGeneric.__init__(self, define=define)
|
||||
|
||||
|
||||
def getCallbackConversionInfo(type, idlObject, isMember, isCallbackReturnValue,
|
||||
isOptional):
|
||||
"""
|
||||
Returns a tuple containing the declType, declArgs, and basic
|
||||
conversion for the given callback type, with the given callback
|
||||
idl object in the given context (isMember/isCallbackReturnValue/isOptional).
|
||||
"""
|
||||
name = idlObject.identifier.name
|
||||
|
||||
# We can't use fast callbacks if isOptional because then we get an
|
||||
# Optional<RootedCallback> thing, which is not transparent to consumers.
|
||||
useFastCallback = (not isMember and not isCallbackReturnValue and
|
||||
not isOptional)
|
||||
if useFastCallback:
|
||||
name = "binding_detail::Fast%s" % name
|
||||
|
||||
if type.nullable() or isCallbackReturnValue:
|
||||
declType = CGGeneric("RefPtr<%s>" % name)
|
||||
else:
|
||||
declType = CGGeneric("OwningNonNull<%s>" % name)
|
||||
|
||||
if useFastCallback:
|
||||
declType = CGTemplatedType("RootedCallback", declType)
|
||||
declArgs = "cx"
|
||||
else:
|
||||
declArgs = None
|
||||
|
||||
conversion = indent(CGCallbackTempRoot(name).define())
|
||||
return (declType, declArgs, conversion)
|
||||
|
||||
|
||||
class JSToNativeConversionInfo():
|
||||
"""
|
||||
An object representing information about a JS-to-native conversion.
|
||||
@ -5088,17 +5119,16 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
assert descriptor.nativeType != 'JSObject'
|
||||
|
||||
if descriptor.interface.isCallback():
|
||||
name = descriptor.interface.identifier.name
|
||||
if type.nullable() or isCallbackReturnValue:
|
||||
declType = CGGeneric("RefPtr<%s>" % name)
|
||||
else:
|
||||
declType = CGGeneric("OwningNonNull<%s>" % name)
|
||||
conversion = indent(CGCallbackTempRoot(name).define())
|
||||
|
||||
(declType, declArgs,
|
||||
conversion) = getCallbackConversionInfo(type, descriptor.interface,
|
||||
isMember,
|
||||
isCallbackReturnValue,
|
||||
isOptional)
|
||||
template = wrapObjectTemplate(conversion, type,
|
||||
"${declName} = nullptr;\n",
|
||||
failureCode)
|
||||
return JSToNativeConversionInfo(template, declType=declType,
|
||||
declArgs=declArgs,
|
||||
dealWithOptional=isOptional)
|
||||
|
||||
# This is an interface that we implement as a concrete class
|
||||
@ -5578,11 +5608,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
|
||||
callback = type.unroll().callback
|
||||
name = callback.identifier.name
|
||||
if type.nullable():
|
||||
declType = CGGeneric("RefPtr<%s>" % name)
|
||||
else:
|
||||
declType = CGGeneric("OwningNonNull<%s>" % name)
|
||||
conversion = indent(CGCallbackTempRoot(name).define())
|
||||
(declType, declArgs,
|
||||
conversion) = getCallbackConversionInfo(type, callback, isMember,
|
||||
isCallbackReturnValue,
|
||||
isOptional)
|
||||
|
||||
if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
|
||||
haveCallable = "JS::IsCallable(&${val}.toObject())"
|
||||
@ -5619,6 +5648,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
"${declName} = nullptr;\n",
|
||||
failureCode)
|
||||
return JSToNativeConversionInfo(template, declType=declType,
|
||||
declArgs=declArgs,
|
||||
dealWithOptional=isOptional)
|
||||
|
||||
if type.isAny():
|
||||
@ -13536,9 +13566,15 @@ class CGBindingRoot(CGThing):
|
||||
cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False))
|
||||
for c in mainCallbacks)
|
||||
|
||||
cgthings.extend([CGNamespace('binding_detail', CGFastCallback(c))
|
||||
for c in mainCallbacks])
|
||||
|
||||
cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(True))
|
||||
for c in workerCallbacks if c not in mainCallbacks)
|
||||
|
||||
cgthings.extend([CGNamespace('binding_detail', CGFastCallback(c))
|
||||
for c in workerCallbacks if c not in mainCallbacks])
|
||||
|
||||
# Do codegen for all the descriptors
|
||||
cgthings.extend([CGDescriptor(x) for x in descriptors])
|
||||
|
||||
@ -13546,6 +13582,10 @@ class CGBindingRoot(CGThing):
|
||||
cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors if
|
||||
not x.workers])
|
||||
|
||||
cgthings.extend([CGNamespace('binding_detail',
|
||||
CGFastCallback(x.interface))
|
||||
for x in callbackDescriptors if not x.workers])
|
||||
|
||||
# Do codegen for JS implemented classes
|
||||
def getParentDescriptor(desc):
|
||||
if not desc.interface.parent:
|
||||
@ -14819,6 +14859,18 @@ class CGCallback(CGClass):
|
||||
"%s(aCx, aCallback, aIncumbentGlobal)" % self.baseName,
|
||||
],
|
||||
body=body),
|
||||
ClassConstructor(
|
||||
[Argument("JSContext*", "aCx"),
|
||||
Argument("JS::Handle<JSObject*>", "aCallback"),
|
||||
Argument("nsIGlobalObject*", "aIncumbentGlobal"),
|
||||
Argument("const FastCallbackConstructor&", "")],
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
explicit=True,
|
||||
baseConstructors=[
|
||||
"%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" % self.baseName,
|
||||
],
|
||||
body=body),
|
||||
ClassConstructor(
|
||||
[Argument("JS::Handle<JSObject*>", "aCallback"),
|
||||
Argument("JS::Handle<JSObject*>", "aAsyncStack"),
|
||||
@ -14984,6 +15036,47 @@ class CGCallbackFunction(CGCallback):
|
||||
baseConstructors=["CallbackFunction(aOther)"])]
|
||||
|
||||
|
||||
class CGFastCallback(CGClass):
|
||||
def __init__(self, idlObject):
|
||||
self._deps = idlObject.getDeps()
|
||||
baseName = idlObject.identifier.name
|
||||
constructor = ClassConstructor(
|
||||
[Argument("JSContext*", "aCx"),
|
||||
Argument("JS::Handle<JSObject*>", "aCallback"),
|
||||
Argument("nsIGlobalObject*", "aIncumbentGlobal")],
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
explicit=True,
|
||||
baseConstructors=[
|
||||
"%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" %
|
||||
baseName,
|
||||
],
|
||||
body="")
|
||||
|
||||
traceMethod = ClassMethod("Trace", "void",
|
||||
[Argument("JSTracer*", "aTracer")],
|
||||
inline=True,
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
body="%s::Trace(aTracer);\n" % baseName)
|
||||
holdMethod = ClassMethod("HoldJSObjectsIfMoreThanOneOwner", "void",
|
||||
[],
|
||||
inline=True,
|
||||
bodyInHeader=True,
|
||||
visibility="public",
|
||||
body=(
|
||||
"%s::HoldJSObjectsIfMoreThanOneOwner();\n" %
|
||||
baseName))
|
||||
|
||||
CGClass.__init__(self, "Fast%s" % baseName,
|
||||
bases=[ClassBase(baseName)],
|
||||
constructors=[constructor],
|
||||
methods=[traceMethod, holdMethod])
|
||||
|
||||
def deps(self):
|
||||
return self._deps
|
||||
|
||||
|
||||
class CGCallbackInterface(CGCallback):
|
||||
def __init__(self, descriptor, typedArraysAreStructs=False):
|
||||
iface = descriptor.interface
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user