Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2016-05-24 15:15:55 +02:00
commit 3cab03a461
834 changed files with 24332 additions and 16922 deletions

View File

@ -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}}'

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1273625 - Update win32 rustc target to i686
Bug 1271829 - Compile nss with SSE2 optimizations

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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 });
}
};

View File

@ -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.

View File

@ -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 = [];

View File

@ -1,8 +1,5 @@
[DEFAULT]
qemu = false
b2g = false
browser = true
skip = false
run-if = buildapp == 'browser'
[test_refresh_firefox.py]

View File

@ -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;
}

View File

@ -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]

View File

@ -1,7 +1,5 @@
[DEFAULT]
b2g = false
browser = true
qemu = false
run-if = buildapp == 'browser'
[test_1_browser_call.py]
[test_2_linkclicker.py]

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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']

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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 =

View File

@ -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

View File

@ -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__':

View File

@ -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

View File

@ -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;
}
}

View File

@ -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",

View File

@ -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">&lt;<!--
--><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">&gt;</span><!--
--></span><!--
--><span class="close">&lt;/<!--
@ -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><!--
-->=&quot;<!--
--><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>&lt;!--</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>--&gt;</span><!--
--></span>

View File

@ -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

View File

@ -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.");
});

View File

@ -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}`);
}

View File

@ -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");
});

View File

@ -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");

View File

@ -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", {

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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.

View File

@ -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) {

View File

@ -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);

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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],

View File

@ -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) {

View File

@ -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
*

View File

@ -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);
};

View File

@ -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();

View File

@ -173,7 +173,7 @@ ul.children + .tag-line::before {
z-index: 1;
}
.child.collapsed .child {
.child.collapsed .child, .child.collapsed .children {
display: none;
}

View File

@ -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()
{

View File

@ -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

View File

@ -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]

View 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);
});

View File

@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setStatusLine(request.httpVersion, 302, "Moved Permanently");
response.setHeader("Location", "http://example");
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -54,6 +54,12 @@ public:
*/
static nsIDocument*
GetCurrentRealmDocument(JSContext* aCx);
/**
* Checks if offscreen animation throttling is enabled.
*/
static bool
IsOffscreenThrottlingEnabled();
};
} // namespace mozilla

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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,

View File

@ -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
}
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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:
/**

View File

@ -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);
}

View File

@ -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 */

View File

@ -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) */

View File

@ -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;

View File

@ -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);
}

View File

@ -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();

View File

@ -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)

View File

@ -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();

View File

@ -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;
}

View File

@ -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

View File

@ -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")

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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.

View File

@ -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);

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -1,7 +1,5 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
run-if = buildapp == 'b2g'
[test_battery_level.js]
[test_battery_status_charging.js]

View File

@ -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"

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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