Merge mozilla-central to inbound a=merge on a CLOSED TREE

This commit is contained in:
Coroiu Cristina 2018-12-22 00:01:43 +02:00
Родитель fa1f40ea6a bbc7fc4e7c
Коммит 36018a1b0d
76 изменённых файлов: 1099 добавлений и 297 удалений

Просмотреть файл

@ -338,6 +338,7 @@ xmlns="http://www.w3.org/1999/xhtml"
oncommand="StarUI.onShowForNewBookmarksCheckboxCommand();"/>
</vbox>
<hbox id="editBookmarkPanelBottomButtons"
class="panel-footer"
style="min-width: &editBookmark.panel.width;;">
#ifndef XP_UNIX
<button id="editBookmarkPanelDoneButton"

Просмотреть файл

@ -218,7 +218,7 @@ var tests = [
},
onShown(popup) {
let notification = popup.children[0];
is(notification.getAttribute("buttonhighlight"), "true", "default action is highlighted");
ok(notification.hasAttribute("buttonhighlight"), "default action is highlighted");
triggerMainCommand(popup);
},
onHidden(popup) {
@ -239,7 +239,7 @@ var tests = [
onShown(popup) {
let notification = popup.children[0];
is(notification.getAttribute("secondarybuttonhidden"), "true", "secondary button is hidden");
is(notification.getAttribute("buttonhighlight"), "true", "default action is highlighted");
ok(notification.hasAttribute("buttonhighlight"), "default action is highlighted");
triggerMainCommand(popup);
},
onHidden(popup) {

Просмотреть файл

@ -205,8 +205,8 @@ function checkPopup(popup, notifyObj) {
"main action label matches");
is(notification.getAttribute("buttonaccesskey"),
notifyObj.mainAction.accessKey, "main action accesskey matches");
is(notification.getAttribute("buttonhighlight"),
(!notifyObj.mainAction.disableHighlight).toString(),
is(notification.hasAttribute("buttonhighlight"),
!notifyObj.mainAction.disableHighlight,
"main action highlight matches");
}
if (notifyObj.secondaryActions && notifyObj.secondaryActions.length > 0) {

Просмотреть файл

@ -98,11 +98,14 @@ var AttributionCode = {
let referrer = attributionSvc.getReferrerUrl(appPath);
let params = new URL(referrer).searchParams;
for (let key of ATTR_CODE_KEYS) {
let utm_key = `utm_${key}`;
if (params.has(utm_key)) {
let value = params.get(utm_key);
if (value && ATTR_CODE_VALUE_REGEX.test(value)) {
gCachedAttrData[key] = value;
// We support the key prefixed with utm_ or not, but intentionally
// choose non-utm params over utm params.
for (let paramKey of [`utm_${key}`, key]) {
if (params.has(paramKey)) {
let value = params.get(paramKey);
if (value && ATTR_CODE_VALUE_REGEX.test(value)) {
gCachedAttrData[key] = value;
}
}
}
}

Просмотреть файл

@ -5,6 +5,7 @@
"use strict";
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource:///modules/AttributionCode.jsm");
add_task(async function test_attribution() {
let appPath = Services.dirsvc.get("GreD", Ci.nsIFile).parent.parent.path;
@ -15,8 +16,8 @@ add_task(async function test_attribution() {
let referrer = attributionSvc.getReferrerUrl(appPath);
equal(referrer, "", "force an empty referrer url");
// Set a url referrer
let url = "http://example.com";
// Set a url referrer, testing both utm and non-utm codes
let url = "http://example.com?content=foo&utm_source=bar&utm_content=baz";
attributionSvc.setReferrerUrl(appPath, url, true);
referrer = attributionSvc.getReferrerUrl(appPath);
equal(referrer, url, "overwrite referrer url");
@ -25,4 +26,7 @@ add_task(async function test_attribution() {
attributionSvc.setReferrerUrl(appPath, "http://test.com", false);
referrer = attributionSvc.getReferrerUrl(appPath);
equal(referrer, url, "referrer url is not changed");
let result = await AttributionCode.getAttrDataAsync();
Assert.deepEqual(result, {content: "foo", source: "bar"}, "parsed attributes match");
});

Просмотреть файл

@ -136,7 +136,7 @@
<!-- Clear Site Data Button -->
<vbox hidden="true"
id="identity-popup-clear-sitedata-footer"
class="identity-popup-footer">
class="panel-footer identity-popup-footer">
<button id="identity-popup-clear-sitedata-button"
label="&identity.clearSiteData;"
oncommand="gIdentityHandler.clearSiteData(event); gIdentityHandler.recordClick('clear_sitedata');"/>
@ -225,7 +225,7 @@
oncommand="gIdentityHandler.enableMixedContentProtection()"/>
</vbox>
<vbox id="identity-popup-more-info-footer" class="identity-popup-footer">
<vbox id="identity-popup-more-info-footer" class="panel-footer identity-popup-footer">
<!-- More Security Information -->
<button id="identity-popup-more-info"
label="&identity.moreInfoLinkText2;"
@ -245,7 +245,7 @@
<image/>
<label>&contentBlocking.trackersView.strictInfo.label;</label>
</hbox>
<vbox class="identity-popup-footer">
<vbox class="panel-footer identity-popup-footer">
<button id="identity-popup-trackersView-settings-button"
label="&contentBlocking.manageSettings.label;"
accesskey="&contentBlocking.manageSettings.accesskey;"
@ -260,7 +260,7 @@
descriptionheightworkaround="true">
<vbox id="identity-popup-cookiesView-list" class="identity-popup-content-blocking-list">
</vbox>
<vbox class="identity-popup-footer">
<vbox class="panel-footer identity-popup-footer">
<button id="identity-popup-cookiesView-settings-button"
label="&contentBlocking.manageSettings.label;"
accesskey="&contentBlocking.manageSettings.accesskey;"
@ -287,7 +287,8 @@
<textbox multiline="true" id="identity-popup-breakageReportView-collection-comments"/>
</vbox>
</vbox>
<vbox id="identity-popup-breakageReportView-footer" class="identity-popup-footer">
<vbox id="identity-popup-breakageReportView-footer"
class="panel-footer identity-popup-footer">
<button id="identity-popup-breakageReportView-cancel"
label="&contentBlocking.breakageReportView.cancel.label;"
oncommand="ContentBlocking.backToMainView();"/>

Просмотреть файл

@ -135,7 +135,8 @@
crop="end"/>
</vbox>
</hbox>
<hbox id="downloadsFooterButtons">
<hbox id="downloadsFooterButtons"
class="panel-footer">
<button id="downloadsHistory"
class="downloadsPanelFooterButton"
label="&downloadsHistory.label;"
@ -158,7 +159,7 @@
<description id="downloadsPanel-blockedSubview-details2"/>
</vbox>
<hbox id="downloadsPanel-blockedSubview-buttons"
class="downloadsPanelFooter"
class="panel-footer downloadsPanelFooter"
align="stretch">
<button id="downloadsPanel-blockedSubview-openButton"
class="downloadsPanelFooterButton"

Просмотреть файл

@ -234,23 +234,21 @@ class UrlbarInput {
try {
new URL(url);
} catch (ex) {
// TODO: Figure out why we need lastLocationChange here.
// let lastLocationChange = browser.lastLocationChange;
// UrlbarUtils.getShortcutOrURIAndPostData(text).then(data => {
// if (where != "current" ||
// browser.lastLocationChange == lastLocationChange) {
// params.postData = data.postData;
// params.allowInheritPrincipal = data.mayInheritPrincipal;
// this._loadURL(data.url, browser, where,
// openUILinkParams);
// }
// });
let browser = this.window.gBrowser.selectedBrowser;
let lastLocationChange = browser.lastLocationChange;
UrlbarUtils.getShortcutOrURIAndPostData(url).then(data => {
if (where != "current" ||
browser.lastLocationChange == lastLocationChange) {
openParams.postData = data.postData;
openParams.allowInheritPrincipal = data.mayInheritPrincipal;
this._loadURL(data.url, where, openParams);
}
});
return;
}
this._loadURL(url, where, openParams);
this.view.close();
}
/**
@ -560,7 +558,7 @@ class UrlbarInput {
browser.initialPageLoadedFromURLBar = url;
}
try {
UrlbarUtils.addToUrlbarHistory(url);
UrlbarUtils.addToUrlbarHistory(url, this.window);
} catch (ex) {
// Things may go wrong when adding url to session history,
// but don't let that interfere with the loading of the url.
@ -602,6 +600,8 @@ class UrlbarInput {
// TODO This should probably be handed via input.
// Ensure the start of the URL is visible for usability reasons.
// this.selectionStart = this.selectionEnd = 0;
this.closePopup();
}
/**

Просмотреть файл

@ -13,3 +13,4 @@ support-files =
subsuite = clipboard
[browser_UrlbarInput_unit.js]
support-files = empty.xul
[browser_UrlbarLoadRace.js]

Просмотреть файл

@ -0,0 +1,72 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This test is for testing races of loading the Urlbar when loading shortcuts.
// For example, ensuring that if a search query is entered, but something causes
// a page load whilst we're getting the search url, then we don't handle the
// original search query.
add_task(async function setup() {
sandbox = sinon.sandbox.create();
registerCleanupFunction(async () => {
sandbox.restore();
});
});
async function checkShortcutLoading(modifierKeys) {
let deferred = PromiseUtils.defer();
let tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: "about:robots",
});
// We stub getShortcutOrURIAndPostData so that we can guarentee it doesn't resolve
// until after we've loaded a new page.
sandbox.stub(UrlbarUtils, "getShortcutOrURIAndPostData").callsFake(() => deferred.promise);
gURLBar.focus();
gURLBar.value = "search";
gURLBar.userTypedValue = true;
EventUtils.synthesizeKey("KEY_Enter", modifierKeys);
Assert.ok(UrlbarUtils.getShortcutOrURIAndPostData.calledOnce,
"should have called getShortcutOrURIAndPostData");
BrowserTestUtils.loadURI(tab.linkedBrowser, "about:license");
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
let openedTab;
function listener(event) {
openedTab = event.target;
}
window.addEventListener("TabOpen", listener);
deferred.resolve({
url: "https://example.com/1/",
postData: {},
mayInheritPrincipal: true,
});
await deferred.promise;
if (modifierKeys) {
Assert.ok(openedTab, "Should have attempted to open the shortcut page");
BrowserTestUtils.removeTab(openedTab);
} else {
Assert.ok(!openedTab, "Should have not attempted to open the shortcut page");
}
window.removeEventListener("TabOpen", listener);
BrowserTestUtils.removeTab(tab);
sandbox.restore();
}
add_task(async function test_location_change_stops_load() {
await checkShortcutLoading();
});
add_task(async function test_opening_different_tab_with_location_change() {
await checkShortcutLoading({altKey: true});
});

Просмотреть файл

@ -11,6 +11,7 @@ let sandbox;
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
Services: "resource://gre/modules/Services.jsm",
QueryContext: "resource:///modules/UrlbarUtils.jsm",
UrlbarController: "resource:///modules/UrlbarController.jsm",

Просмотреть файл

@ -313,26 +313,10 @@
margin-top: 1em;
}
.identity-popup-footer {
background-color: var(--arrowpanel-dimmed);
}
.identity-popup-footer > button {
-moz-appearance: none;
margin: 0;
border-top: 1px solid var(--panel-separator-color);
padding: 8px 20px;
/* !important overrides :hover and :active colors from button.css: */
color: inherit !important;
}
.identity-popup-footer > button:hover,
.identity-popup-footer > button:focus {
background-color: var(--arrowpanel-dimmed);
}
.identity-popup-footer > button:hover:active {
background-color: var(--arrowpanel-dimmed-further);
}
#identity-popup-content-verifier ~ description {
@ -362,19 +346,6 @@ description#identity-popup-content-verifier,
flex: 1;
}
#identity-popup-breakageReportView-footer > button[default] {
color: white;
background-color: #0996f8;
}
#identity-popup-breakageReportView-footer > button[default]:hover {
background-color: #0675d3;
}
#identity-popup-breakageReportView-footer > button[default]:hover:active {
background-color: #0568ba;
}
#identity-popup-breakageReportView-heading,
#identity-popup-breakageReportView-body {
padding: 16px;

Просмотреть файл

@ -31,7 +31,6 @@
}
.downloadsPanelFooter {
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
}
@ -45,41 +44,22 @@
}
.downloadsPanelFooterButton {
-moz-appearance: none;
background-color: transparent;
color: inherit;
margin: 0;
padding: 0;
min-width: 0;
min-height: 40px;
border: none;
}
.downloadsPanelFooterButton:hover {
outline: 1px solid var(--arrowpanel-dimmed);
background-color: var(--arrowpanel-dimmed);
}
.downloadsPanelFooterButton:hover:active,
.downloadsPanelFooterButton[open="true"] {
outline: 1px solid var(--arrowpanel-dimmed-further);
background-color: var(--arrowpanel-dimmed-further);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
.downloadsPanelFooterButton[default] {
background-color: #0996f8;
color: white;
}
.downloadsPanelFooterButton[default]:hover {
background-color: #0675d3;
}
.downloadsPanelFooterButton[default]:hover:active {
background-color: #0568ba;
}
.downloadsPanelFooterButton > .button-box {
padding: 0;
}

Просмотреть файл

@ -57,35 +57,11 @@ html|img#editBookmarkPanelFavicon[src] {
.editBookmarkPanelBottomButton {
flex: 1;
-moz-appearance: none;
margin: 0;
padding: .8em 0;
color: inherit;
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
}
.editBookmarkPanelBottomButton:not(:last-child) {
border-inline-end: 1px solid var(--panel-separator-color);
}
.editBookmarkPanelBottomButton:hover {
background-color: var(--arrowpanel-dimmed-further);
}
.editBookmarkPanelBottomButton:hover:active {
background-color: var(--arrowpanel-dimmed-even-further);
}
.editBookmarkPanelBottomButton[default] {
color: white;
background-color: #0996f8;
}
.editBookmarkPanelBottomButton[default]:hover {
background-color: #0675d3;
}
.editBookmarkPanelBottomButton[default]:hover:active {
background-color: #0568ba;
}

Просмотреть файл

@ -5,8 +5,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VisualViewport.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/ToString.h"
#include "nsIScrollableFrame.h"
#include "nsIDocShell.h"
#include "nsPresContext.h"
#include "nsRefreshDriver.h"
#define VVP_LOG(...)
// #define VVP_LOG(...) printf_stderr("VVP: " __VA_ARGS__)
using namespace mozilla;
using namespace mozilla::dom;
@ -14,7 +22,15 @@ using namespace mozilla::dom;
VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow)
: DOMEventTargetHelper(aWindow) {}
VisualViewport::~VisualViewport() {}
VisualViewport::~VisualViewport() {
if (mResizeEvent) {
mResizeEvent->Revoke();
}
if (mScrollEvent) {
mScrollEvent->Revoke();
}
}
/* virtual */
JSObject* VisualViewport::WrapObject(JSContext* aCx,
@ -22,6 +38,24 @@ JSObject* VisualViewport::WrapObject(JSContext* aCx,
return VisualViewport_Binding::Wrap(aCx, this, aGivenProto);
}
/* virtual */
void VisualViewport::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
EventMessage msg = aVisitor.mEvent->mMessage;
aVisitor.mCanHandle = true;
EventTarget* parentTarget = nullptr;
// Only our special internal events are allowed to escape the
// Visual Viewport and be dispatched further up the DOM tree.
if (msg == eMozVisualScroll || msg == eMozVisualResize) {
if (nsPIDOMWindowInner* win = GetOwner()) {
if (nsIDocument* doc = win->GetExtantDoc()) {
parentTarget = doc;
}
}
}
aVisitor.SetParentTarget(parentTarget, false);
}
CSSSize VisualViewport::VisualViewportSize() const {
CSSSize size = CSSSize(0, 0);
@ -71,12 +105,8 @@ CSSPoint VisualViewport::VisualViewportOffset() const {
CSSPoint VisualViewport::LayoutViewportOffset() const {
CSSPoint offset = CSSPoint(0, 0);
nsIPresShell* presShell = GetPresShell();
if (presShell) {
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
if (sf) {
offset = CSSPoint::FromAppUnits(sf->GetScrollPosition());
}
if (nsIPresShell* presShell = GetPresShell()) {
offset = CSSPoint::FromAppUnits(presShell->GetLayoutViewportOffset());
}
return offset;
}
@ -106,3 +136,133 @@ nsIPresShell* VisualViewport::GetPresShell() const {
return docShell->GetPresShell();
}
nsPresContext* VisualViewport::GetPresContext() const {
nsIPresShell* presShell = GetPresShell();
if (!presShell) {
return nullptr;
}
return presShell->GetPresContext();
}
/* ================= Resize event handling ================= */
void VisualViewport::PostResizeEvent() {
VVP_LOG("%p: PostResizeEvent\n", this);
if (mResizeEvent) {
return;
}
// The event constructor will register itself with the refresh driver.
if (nsPresContext* presContext = GetPresContext()) {
mResizeEvent = new VisualViewportResizeEvent(this, presContext);
VVP_LOG("%p: PostResizeEvent, created new event\n", this);
}
}
VisualViewport::VisualViewportResizeEvent::VisualViewportResizeEvent(
VisualViewport* aViewport, nsPresContext* aPresContext)
: Runnable("VisualViewport::VisualViewportResizeEvent"),
mViewport(aViewport) {
aPresContext->RefreshDriver()->PostVisualViewportResizeEvent(this);
}
NS_IMETHODIMP
VisualViewport::VisualViewportResizeEvent::Run() {
if (mViewport) {
mViewport->FireResizeEvent();
}
return NS_OK;
}
void VisualViewport::FireResizeEvent() {
MOZ_ASSERT(mResizeEvent);
mResizeEvent->Revoke();
mResizeEvent = nullptr;
VVP_LOG("%p, FireResizeEvent, fire mozvisualresize\n", this);
WidgetEvent mozEvent(true, eMozVisualResize);
mozEvent.mFlags.mOnlySystemGroupDispatch = true;
EventDispatcher::Dispatch(this, GetPresContext(), &mozEvent);
VVP_LOG("%p, FireResizeEvent, fire VisualViewport resize\n", this);
WidgetEvent event(true, eResize);
event.mFlags.mBubbles = false;
event.mFlags.mCancelable = false;
EventDispatcher::Dispatch(this, GetPresContext(), &event);
}
/* ================= Scroll event handling ================= */
void VisualViewport::PostScrollEvent(const nsPoint& aPrevVisualOffset,
const nsPoint& aPrevLayoutOffset) {
VVP_LOG("%p: PostScrollEvent, prevRelativeOffset=%s\n", this,
ToString(aPrevVisualOffset - aPrevLayoutOffset).c_str());
if (mScrollEvent) {
return;
}
// The event constructor will register itself with the refresh driver.
if (nsPresContext* presContext = GetPresContext()) {
mScrollEvent = new VisualViewportScrollEvent(
this, presContext, aPrevVisualOffset, aPrevLayoutOffset);
VVP_LOG("%p: PostScrollEvent, created new event\n", this);
}
}
VisualViewport::VisualViewportScrollEvent::VisualViewportScrollEvent(
VisualViewport* aViewport, nsPresContext* aPresContext,
const nsPoint& aPrevVisualOffset, const nsPoint& aPrevLayoutOffset)
: Runnable("VisualViewport::VisualViewportScrollEvent"),
mViewport(aViewport),
mPrevVisualOffset(aPrevVisualOffset),
mPrevLayoutOffset(aPrevLayoutOffset) {
aPresContext->RefreshDriver()->PostVisualViewportScrollEvent(this);
}
NS_IMETHODIMP
VisualViewport::VisualViewportScrollEvent::Run() {
if (mViewport) {
mViewport->FireScrollEvent();
}
return NS_OK;
}
void VisualViewport::FireScrollEvent() {
MOZ_ASSERT(mScrollEvent);
nsPoint prevVisualOffset = mScrollEvent->PrevVisualOffset();
nsPoint prevLayoutOffset = mScrollEvent->PrevLayoutOffset();
mScrollEvent->Revoke();
mScrollEvent = nullptr;
if (nsIPresShell* presShell = GetPresShell()) {
if (presShell->GetVisualViewportOffset() != prevVisualOffset) {
// The internal event will be fired whenever the visual viewport's
// *absolute* offset changed, i.e. relative to the page.
VVP_LOG("%p: FireScrollEvent, fire mozvisualscroll\n", this);
WidgetEvent mozEvent(true, eMozVisualScroll);
mozEvent.mFlags.mOnlySystemGroupDispatch = true;
EventDispatcher::Dispatch(this, GetPresContext(), &mozEvent);
}
// Check whether the relative visual viewport offset actually changed -
// maybe both visual and layout viewport scrolled together and there was no
// change after all.
nsPoint curRelativeOffset =
presShell->GetVisualViewportOffsetRelativeToLayoutViewport();
nsPoint prevRelativeOffset = prevVisualOffset - prevLayoutOffset;
VVP_LOG(
"%p: FireScrollEvent, curRelativeOffset %s, "
"prevRelativeOffset %s\n",
this, ToString(curRelativeOffset).c_str(),
ToString(prevRelativeOffset).c_str());
if (curRelativeOffset != prevRelativeOffset) {
VVP_LOG("%p, FireScrollEvent, fire VisualViewport scroll\n", this);
WidgetGUIEvent event(true, eScroll, nullptr);
event.mFlags.mBubbles = false;
event.mFlags.mCancelable = false;
EventDispatcher::Dispatch(this, GetPresContext(), &event);
}
}
}

Просмотреть файл

@ -28,9 +28,55 @@ class VisualViewport final : public mozilla::DOMEventTargetHelper {
double Width() const;
double Height() const;
double Scale() const;
IMPL_EVENT_HANDLER(resize)
IMPL_EVENT_HANDLER(scroll)
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
void PostResizeEvent();
void PostScrollEvent(const nsPoint& aPrevVisualOffset,
const nsPoint& aPrevLayoutOffset);
// These two events are modelled after the ScrollEvent class in
// nsGfxScrollFrame.h.
class VisualViewportResizeEvent : public Runnable {
public:
NS_DECL_NSIRUNNABLE
VisualViewportResizeEvent(VisualViewport* aViewport,
nsPresContext* aPresContext);
void Revoke() { mViewport = nullptr; }
private:
VisualViewport* mViewport;
};
class VisualViewportScrollEvent : public Runnable {
public:
NS_DECL_NSIRUNNABLE
VisualViewportScrollEvent(VisualViewport* aViewport,
nsPresContext* aPresContext,
const nsPoint& aPrevVisualOffset,
const nsPoint& aPrevLayoutOffset);
void Revoke() { mViewport = nullptr; }
nsPoint PrevVisualOffset() const { return mPrevVisualOffset; }
nsPoint PrevLayoutOffset() const { return mPrevLayoutOffset; }
private:
VisualViewport* mViewport;
// The VisualViewport "scroll" event is supposed to be fired only when the
// *relative* offset between visual and layout viewport changes. The two
// viewports are updated independently from each other, though, so the only
// thing we can do is note the fact that one of the inputs into the relative
// visual viewport offset changed and then check the offset again at the
// next refresh driver tick, just before the event is going to fire.
// Hopefully, at this point both visual and layout viewport positions have
// been updated, so that we're able to tell whether the relative offset did
// in fact change or not.
const nsPoint mPrevVisualOffset;
const nsPoint mPrevLayoutOffset;
};
private:
virtual ~VisualViewport();
@ -39,6 +85,13 @@ class VisualViewport final : public mozilla::DOMEventTargetHelper {
CSSPoint VisualViewportOffset() const;
CSSPoint LayoutViewportOffset() const;
nsIPresShell* GetPresShell() const;
nsPresContext* GetPresContext() const;
void FireResizeEvent();
void FireScrollEvent();
RefPtr<VisualViewportResizeEvent> mResizeEvent;
RefPtr<VisualViewportScrollEvent> mScrollEvent;
};
} // namespace dom

Просмотреть файл

@ -330,6 +330,10 @@ class EventTargetChainItem {
if (aVisitor.mEvent->PropagationStopped()) {
return;
}
if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatch &&
!aVisitor.mEvent->mFlags.mInSystemGroup) {
return;
}
if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatchInContent &&
!aVisitor.mEvent->mFlags.mInSystemGroup && !IsCurrentTargetChrome()) {
return;

Просмотреть файл

@ -265,6 +265,10 @@ FORWARDED_EVENT(load, eLoad, EventNameType_All, eBasicEventClass)
FORWARDED_EVENT(resize, eResize, EventNameType_All, eBasicEventClass)
FORWARDED_EVENT(scroll, eScroll, (EventNameType_HTMLXUL | EventNameType_SVGSVG),
eBasicEventClass)
NON_IDL_EVENT(mozvisualresize, eMozVisualResize, EventNameType_None,
eBasicEventClass)
NON_IDL_EVENT(mozvisualscroll, eMozVisualScroll, EventNameType_None,
eBasicEventClass)
WINDOW_EVENT(afterprint, eAfterPrint,
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,

Просмотреть файл

@ -1631,6 +1631,14 @@ void HTMLMediaElement::SetVisible(bool aVisible) {
}
}
bool HTMLMediaElement::IsVideoDecodingSuspended() const {
return mDecoder && mDecoder->IsVideoDecodingSuspended();
}
bool HTMLMediaElement::IsVisible() const {
return mVisibilityState == Visibility::APPROXIMATELY_VISIBLE;
}
already_AddRefed<layers::Image> HTMLMediaElement::GetCurrentImage() {
MarkAsTainted();
@ -6281,6 +6289,9 @@ void HTMLMediaElement::OnVisibilityChange(Visibility aNewVisibility) {
("OnVisibilityChange(): %s\n", VisibilityString(aNewVisibility)));
mVisibilityState = aNewVisibility;
if (StaticPrefs::MediaTestVideoSuspend()) {
DispatchAsyncEvent(NS_LITERAL_STRING("visibilitychanged"));
}
if (!mDecoder) {
return;

Просмотреть файл

@ -560,6 +560,12 @@ class HTMLMediaElement : public nsGenericHTMLElement,
// For use by mochitests. Enabling pref "media.test.video-suspend"
bool HasSuspendTaint() const;
// For use by mochitests.
bool IsVideoDecodingSuspended() const;
// For use by mochitests only.
bool IsVisible() const;
// Synchronously, return the next video frame and mark the element unable to
// participate in decode suspending.
//

Просмотреть файл

@ -434,9 +434,11 @@ void MediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) {
break;
case MediaPlaybackEvent::EnterVideoSuspend:
GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozentervideosuspend"));
mIsVideoDecodingSuspended = true;
break;
case MediaPlaybackEvent::ExitVideoSuspend:
GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozexitvideosuspend"));
mIsVideoDecodingSuspended = false;
break;
case MediaPlaybackEvent::StartVideoSuspendTimer:
GetOwner()->DispatchAsyncEvent(
@ -459,6 +461,10 @@ void MediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) {
}
}
bool MediaDecoder::IsVideoDecodingSuspended() const {
return mIsVideoDecodingSuspended;
}
void MediaDecoder::OnPlaybackErrorEvent(const MediaResult& aError) {
DecodeError(aError);
}
@ -966,14 +972,6 @@ void MediaDecoder::UpdateVideoDecodeMode() {
return;
}
// If an element is in-tree with UNTRACKED visibility, the visibility is
// incomplete and don't update the video decode mode.
if (mIsElementInTree && mElementVisibility == Visibility::UNTRACKED) {
LOG("UpdateVideoDecodeMode(), early return because we have incomplete "
"visibility states.");
return;
}
// Seeking is required when leaving suspend mode.
if (!mMediaSeekable) {
LOG("UpdateVideoDecodeMode(), set Normal because the media is not "
@ -1014,6 +1012,16 @@ void MediaDecoder::UpdateVideoDecodeMode() {
return;
}
// If the element is in-tree with UNTRACKED visibility, that means the element
// is not close enough to the viewport so we have not start to update its
// visibility. In this case, it's equals to invisible.
if (mIsElementInTree && mElementVisibility == Visibility::UNTRACKED) {
LOG("UpdateVideoDecodeMode(), set Suspend because element hasn't be "
"updated visibility state.");
mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
return;
}
// Otherwise, depends on the owner's visibility state.
// A element is visible only if its document is visible and the element
// itself is visible.

Просмотреть файл

@ -312,6 +312,8 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
void SetIsBackgroundVideoDecodingAllowed(bool aAllowed);
bool IsVideoDecodingSuspended() const;
/******
* The following methods must only be called on the main
* thread.
@ -578,6 +580,9 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
MediaEventListener mOnDecodeWarning;
MediaEventListener mOnNextFrameStatus;
// True if we have suspended video decoding.
bool mIsVideoDecodingSuspended = false;
protected:
// PlaybackRate and pitch preservation status we should start at.
double mPlaybackRate;

Просмотреть файл

@ -61,42 +61,40 @@ class DecodedStreamGraphListener {
TrackID aVideoTrackID,
MozPromiseHolder<DecodedStream::EndedPromise>&& aVideoEndedHolder,
AbstractThread* aMainThread)
: mMutex("DecodedStreamGraphListener::mMutex"),
mAudioTrackListener(IsTrackIDExplicit(aAudioTrackID)
: mAudioTrackListener(IsTrackIDExplicit(aAudioTrackID)
? MakeRefPtr<DecodedStreamTrackListener>(
this, aStream, aAudioTrackID)
: nullptr),
mAudioTrackID(aAudioTrackID),
mAudioEndedHolder(std::move(aAudioEndedHolder)),
mVideoTrackListener(IsTrackIDExplicit(aVideoTrackID)
? MakeRefPtr<DecodedStreamTrackListener>(
this, aStream, aVideoTrackID)
: nullptr),
mAudioTrackID(aAudioTrackID),
mAudioEndedHolder(std::move(aAudioEndedHolder)),
mVideoTrackID(aVideoTrackID),
mVideoEndedHolder(std::move(aVideoEndedHolder)),
mStream(aStream),
mAbstractMainThread(aMainThread) {
MOZ_ASSERT(NS_IsMainThread());
if (mAudioTrackListener) {
aStream->AddTrackListener(mAudioTrackListener, mAudioTrackID);
mStream->AddTrackListener(mAudioTrackListener, mAudioTrackID);
} else {
mAudioEndedHolder.ResolveIfExists(true, __func__);
}
if (mVideoTrackListener) {
aStream->AddTrackListener(mVideoTrackListener, mVideoTrackID);
mStream->AddTrackListener(mVideoTrackListener, mVideoTrackID);
} else {
mVideoEndedHolder.ResolveIfExists(true, __func__);
}
}
void NotifyOutput(const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
StreamTime aCurrentTrackTime) {
void NotifyOutput(TrackID aTrackID, StreamTime aCurrentTrackTime) {
if (aTrackID != mAudioTrackID && mAudioTrackID != TRACK_NONE) {
// Only audio playout drives the clock forward, if present.
return;
}
if (aStream) {
mOnOutput.Notify(aStream->StreamTimeToMicroseconds(aCurrentTrackTime));
}
mOnOutput.Notify(mStream->StreamTimeToMicroseconds(aCurrentTrackTime));
}
TrackID AudioTrackID() const { return mAudioTrackID; }
@ -115,16 +113,21 @@ class DecodedStreamGraphListener {
}
void Forget() {
RefPtr<DecodedStreamGraphListener> self = this;
mAbstractMainThread->Dispatch(
NS_NewRunnableFunction("DecodedStreamGraphListener::Forget", [self]() {
MOZ_ASSERT(NS_IsMainThread());
self->mAudioEndedHolder.ResolveIfExists(false, __func__);
self->mVideoEndedHolder.ResolveIfExists(false, __func__);
}));
MutexAutoLock lock(mMutex);
MOZ_ASSERT(NS_IsMainThread());
if (mAudioTrackListener && !mStream->IsDestroyed()) {
mStream->EndTrack(mAudioTrackID);
mStream->RemoveTrackListener(mAudioTrackListener, mAudioTrackID);
}
mAudioTrackListener = nullptr;
mAudioEndedHolder.ResolveIfExists(false, __func__);
if (mVideoTrackListener && !mStream->IsDestroyed()) {
mStream->EndTrack(mVideoTrackID);
mStream->RemoveTrackListener(mVideoTrackListener, mVideoTrackID);
}
mVideoTrackListener = nullptr;
mVideoEndedHolder.ResolveIfExists(false, __func__);
}
MediaEventSource<int64_t>& OnOutput() { return mOnOutput; }
@ -137,16 +140,15 @@ class DecodedStreamGraphListener {
MediaEventProducer<int64_t> mOnOutput;
Mutex mMutex;
// Members below are protected by mMutex.
RefPtr<DecodedStreamTrackListener> mAudioTrackListener;
RefPtr<DecodedStreamTrackListener> mVideoTrackListener;
// Main thread only.
RefPtr<DecodedStreamTrackListener> mAudioTrackListener;
const TrackID mAudioTrackID;
MozPromiseHolder<DecodedStream::EndedPromise> mAudioEndedHolder;
RefPtr<DecodedStreamTrackListener> mVideoTrackListener;
const TrackID mVideoTrackID;
MozPromiseHolder<DecodedStream::EndedPromise> mVideoEndedHolder;
const RefPtr<SourceMediaStream> mStream;
const RefPtr<AbstractThread> mAbstractMainThread;
};
@ -157,7 +159,7 @@ DecodedStreamTrackListener::DecodedStreamTrackListener(
void DecodedStreamTrackListener::NotifyOutput(MediaStreamGraph* aGraph,
StreamTime aCurrentTrackTime) {
mGraphListener->NotifyOutput(mStream, mTrackID, aCurrentTrackTime);
mGraphListener->NotifyOutput(mTrackID, aCurrentTrackTime);
}
void DecodedStreamTrackListener::NotifyEnded() {
@ -450,7 +452,7 @@ void DecodedStream::Shutdown() {
mWatchManager.Shutdown();
}
void DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData) {
void DecodedStream::DestroyData(UniquePtr<DecodedStreamData>&& aData) {
AssertOwnerThread();
if (!aData) {
@ -459,11 +461,9 @@ void DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData) {
mOutputListener.Disconnect();
DecodedStreamData* data = aData.release();
data->Forget();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("DecodedStream::DestroyData",
[=]() { delete data; });
NS_DispatchToMainThread(r.forget());
NS_DispatchToMainThread(
NS_NewRunnableFunction("DecodedStream::DestroyData",
[data = std::move(aData)]() { data->Forget(); }));
}
void DecodedStream::SetPlaying(bool aPlaying) {

Просмотреть файл

@ -76,7 +76,7 @@ class DecodedStream : public MediaSink {
media::TimeUnit FromMicroseconds(int64_t aTime) {
return media::TimeUnit::FromMicroseconds(aTime);
}
void DestroyData(UniquePtr<DecodedStreamData> aData);
void DestroyData(UniquePtr<DecodedStreamData>&& aData);
void SendAudio(double aVolume, bool aIsSameOrigin,
const PrincipalHandle& aPrincipalHandle);
void SendVideo(bool aIsSameOrigin, const PrincipalHandle& aPrincipalHandle);

Просмотреть файл

@ -48,6 +48,28 @@ function appendVideoToDoc(url, token, width, height) {
return v;
}
/**
* @param {HTMLMediaElement} video Video element with under test.
* @returns {Promise} Promise that is resolved when video 'visibilitychanged' event fires.
*/
function waitUntilVisible(video) {
let videoChrome = SpecialPowers.wrap(video);
if (videoChrome.isVisible) {
return Promise.resolve();
}
return new Promise(resolve => {
videoChrome.addEventListener("visibilitychanged", () => {
if (videoChrome.isVisible) {
ok(true, `${video.token} is visible.`);
videoChrome.removeEventListener("visibilitychanged", this);
resolve();
}
});
});
}
/**
* @param {HTMLMediaElement} video Video element under test.
* @returns {Promise} Promise that is resolved when video 'playing' event fires.

Просмотреть файл

@ -767,6 +767,9 @@ skip-if = toolkit == 'android' # bug 1306916, bug 1329566, android(bug 1232305)
[test_bug1248229.html]
skip-if = android_version == '17' # bug 1306917, 1323778, android(bug 1232305)
tags=capturestream
[test_bug1512958.html]
skip-if = toolkit == 'android' # android(bug 1232305)
tags=msg capturestream
[test_can_play_type.html]
skip-if = (android_version == '23' && debug) || (android_version == '25' && debug) # android(bug 1232305)
[test_can_play_type_mpeg.html]

Просмотреть файл

@ -60,10 +60,11 @@ startTest({
let v = appendVideoToDoc(test.name, token);
manager.started(token);
waitUntilPlaying(v)
waitUntilVisible(v)
.then(() => waitUntilPlaying(v))
.then(() => testSuspendTimerStartedWhenHidden(v))
.then(() => testSuspendTimerCanceledWhenTainted(v))
.then(() => { manager.finished(token); });
}
});
</script>
</script>

Просмотреть файл

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test that pausing and resuming a captured media element with audio doesn't stall</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<audio id="a"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
function dumpEvent({target, type}) {
info(`${target.name} GOT EVENT ${type} currentTime=${target.currentTime} ` +
`paused=${target.paused} ended=${target.ended} ` +
`readyState=${target.readyState}`);
}
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const a = document.getElementById('a');
const events = ["timeupdate", "seeking", "seeked", "ended", "playing", "pause"];
for (let ev of events) {
a.addEventListener(ev, dumpEvent);
}
(async _ => {
try {
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("Timeouts for shortcutting test-timeout");
const test = getPlayableAudio(gTrackTests.filter(t => t.duration > 2));
if (!test) {
todo(false, "No playable audio");
return;
}
// Start playing and capture
a.src = test.name;
a.name = test.name;
const ac = new AudioContext();
const src = ac.createMediaElementSource(a);
a.play();
do {
await new Promise(r => a.ontimeupdate = r);
} while(a.currentTime == 0)
// Pause to trigger recreating tracks in DecodedStream
a.pause();
await new Promise(r => a.onpause = r);
// Resuming should now work. Bug 1512958 would cause a stall because the
// original track wasn't ended and we'd block on it.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1512958#c5
a.play();
await new Promise(r => a.onplaying = r);
a.currentTime = test.duration - 1;
await Promise.race([
new Promise(res => a.onended = res),
wait(30000).then(_ => Promise.reject(new Error("Timeout"))),
]);
} catch(e) {
ok(false, e);
} finally {
SimpleTest.finish();
}
})();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -204,13 +204,15 @@ partial interface HTMLMediaElement {
};
/*
* This is an API for simulating visibility changes to help debug and write
* These APIs are testing only, they are used to simulate visibility changes to help debug and write
* tests about suspend-video-decoding.
*
* - SetVisible() is for simulating visibility changes.
* - HasSuspendTaint() is for querying that the element's decoder cannot suspend
* video decoding because it has been tainted by an operation, such as
* drawImage().
* - isVisible is a boolean value which indicate whether media element is visible.
* - isVideoDecodingSuspended() is used to know whether video decoding has suspended.
*/
partial interface HTMLMediaElement {
[Pref="media.test.video-suspend"]
@ -218,6 +220,12 @@ partial interface HTMLMediaElement {
[Pref="media.test.video-suspend"]
boolean hasSuspendTaint();
[ChromeOnly]
readonly attribute boolean isVisible;
[ChromeOnly]
readonly attribute boolean isVideoDecodingSuspended;
};
/* Audio Output Devices API */

Просмотреть файл

@ -18,4 +18,7 @@ interface VisualViewport : EventTarget {
readonly attribute double height;
readonly attribute double scale;
attribute EventHandler onresize;
attribute EventHandler onscroll;
};

Просмотреть файл

@ -282,6 +282,8 @@ function runSubtestsSeriallyInFreshWindows(aSubtests) {
// called with at most 2 arguments.
return SimpleTest.ok.apply(SimpleTest, arguments);
};
w.todo_is = function(a, b, msg) { return todo_is(a, b, aFile + " | " + msg); };
w.todo = function(cond, msg) { return todo(cond, aFile + " | " + msg); };
if (test.onload) {
w.addEventListener('load', function(e) { test.onload(w); }, { once: true });
}

Просмотреть файл

@ -7,26 +7,65 @@
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript">
function scrollPage() {
var transformEnd = function() {
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll);
};
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd");
function* test(testDriver) {
let scrEvt = new EventCounter(window, "scroll");
let visScrEvt = new EventCounter(window.visualViewport, "scroll");
// Our internal visual viewport events aren't restricted to the visual view-
// port itself, so we can listen on the window itself, however the event
// listener needs to be in the system group.
let visScrEvtInternal = new EventCounter(window, "mozvisualscroll",
{ mozSystemGroup: true });
// This listener will trigger the test to continue once APZ is done with
// processing the scroll.
SpecialPowers.Services.obs.addObserver(testDriver, "APZ:TransformEnd");
synthesizeNativeTouchDrag(document.body, 10, 100, 0, -50);
dump("Finished native drag, waiting for transform-end observer...\n");
}
function checkScroll() {
// Wait for the APZ:TransformEnd to be fired after touch events are processed.
yield true;
// We get here once the APZ:TransformEnd has fired, so we don't need that
// observer any more.
SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);
// Flush state.
yield waitForApzFlushedRepaints(testDriver);
is(window.scrollY, 50, "check that the window scrolled");
subtestDone();
// Check we've got the expected events.
// This page is using "width=device-width; initial-scale=1.0" and we haven't
// pinch-zoomed any further, so layout and visual viewports have the same
// size and will scroll together. Therefore we should be getting layout
// viewport "scroll" events as well.
scrEvt.unregister();
ok(scrEvt.count > 0, "Got some layout viewport scroll events");
// This one is a bit tricky: Visual viewport "scroll" events are supposed to
// fire only when the relative offset between layout and visual viewport
// changes. Even when they're both scrolling together, we may update their
// positions independently, though, leading to some jitter in the offset and
// triggering the event after all.
// At least for the case here, where both viewports are the same size and we
// have a freshly loaded page, we should however be able to keep the offset at
// a constant zero and therefore not cause any visual viewport scroll events
// to fire.
visScrEvt.unregister();
is(visScrEvt.count, 0, "Got no visual viewport scroll events");
visScrEvtInternal.unregister();
// Our internal visual viewport scroll event on the other hand only cares
// about the absolute offset of the visual viewport and should therefore
// definitively fire.
ok(visScrEvtInternal.count > 0, "Got some mozvisualscroll events");
}
waitUntilApzStable().then(scrollPage);
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</head>

Просмотреть файл

@ -7,9 +7,22 @@
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript">
function* test(testDriver) {
let visResEvt = new EventCounter(window.visualViewport, "resize");
let visScrEvt = new EventCounter(window.visualViewport, "scroll");
// Our internal visual viewport events aren't restricted to the visual view-
// port itself, so we can listen on the window itself, however the event
// listener needs to be in the system group.
let visResEvtInternal = new EventCounter(window, "mozvisualresize",
{ mozSystemGroup: true });
let visScrEvtInternal = new EventCounter(window, "mozvisualscroll",
{ mozSystemGroup: true });
let visResEvtContent = new EventCounter(window, "mozvisualresize");
let visScrEvtContent = new EventCounter(window, "mozvisualscroll");
var initial_resolution = getResolution();
ok(initial_resolution > 0,
'The initial_resolution is ' + initial_resolution + ', which is some sane value');
@ -38,9 +51,31 @@ function* test(testDriver) {
SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);
// Flush state and get the resolution we're at now
yield flushApzRepaints(testDriver);
yield waitForApzFlushedRepaints(testDriver);
let final_resolution = getResolution();
ok(final_resolution > initial_resolution, 'The final resolution (' + final_resolution + ') is greater after zooming in');
// Check we've got the expected events.
// Pinch-zooming the page should fire visual viewport resize events:
visResEvt.unregister();
ok(visResEvt.count > 0, "Got some visual viewport resize events");
visResEvtInternal.unregister();
ok(visResEvtInternal.count > 0, "Got some mozvisualresize events");
// We're pinch-zooming somewhere in the middle of the page, so the visual
// viewport's coordinates change, too.
// This is true both relative to the page (mozvisualscroll), as well as
// relative to the layout viewport (visual viewport "scroll" event).
visScrEvt.unregister();
ok(visScrEvt.count > 0, "Got some visual viewport scroll events");
visScrEvtInternal.unregister();
ok(visScrEvtInternal.count > 0, "Got some mozvisualscroll events");
// Our internal events shouldn't leak to normal content.
visResEvtContent.unregister();
is(visResEvtContent.count, 0, "Got no mozvisualresize events in content");
visScrEvtContent.unregister();
is(visScrEvtContent.count, 0, "Got no mozvisualscroll events in content");
}
waitUntilApzStable()

Просмотреть файл

@ -13,7 +13,8 @@ var basic_pan_prefs = getPrefs("TOUCH_EVENTS:PAN");
var subtests = [
// Simple tests to exercise basic panning behaviour
{'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs},
// The visual viewport isn't yet enabled by default and we want to test its events, too.
{'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs.concat([["dom.visualviewport.enabled", true]])},
{'file': 'helper_div_pan.html', 'prefs': basic_pan_prefs},
{'file': 'helper_iframe_pan.html', 'prefs': basic_pan_prefs},

Просмотреть файл

@ -39,6 +39,8 @@ var prefs = [
// they too need to be disabled for now.
["layout.display-list.retain", false],
["layout.display-list.retain.chrome", false],
// The VisualViewport API currently isn't enabled by default.
["dom.visualviewport.enabled", true],
];
// Increase the tap timeouts so the double-tap is still detected in case of

Просмотреть файл

@ -175,7 +175,8 @@ static ScreenMargin ScrollFrame(nsIContent* aContent,
if (sf->IsRootScrollFrameOfDocument()) {
if (nsCOMPtr<nsIPresShell> shell = GetPresShell(aContent)) {
shell->SetVisualViewportOffset(
CSSPoint::ToAppUnits(aRequest.GetScrollOffset()));
CSSPoint::ToAppUnits(aRequest.GetScrollOffset()),
shell->GetLayoutViewportOffset());
}
}
}

Просмотреть файл

@ -187,6 +187,7 @@
#include "nsBindingManager.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "VisualViewport.h"
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
@ -5173,6 +5174,9 @@ nsresult PresShell::SetResolutionAndScaleTo(float aResolution,
if (aOrigin != nsGkAtoms::apz) {
mResolutionUpdated = true;
}
if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
window->VisualViewport()->PostResizeEvent();
}
return NS_OK;
}
@ -10031,13 +10035,34 @@ void nsIPresShell::SetVisualViewportSize(nscoord aWidth, nscoord aHeight) {
rootScrollFrame->MarkScrollbarsDirtyForReflow();
}
MarkFixedFramesForReflow(nsIPresShell::eResize);
if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
window->VisualViewport()->PostResizeEvent();
}
}
}
void nsIPresShell::SetVisualViewportOffset(
const nsPoint& aScrollOffset, const nsPoint& aPrevLayoutScrollPos) {
if (mVisualViewportOffset != aScrollOffset) {
nsPoint prevOffset = mVisualViewportOffset;
mVisualViewportOffset = aScrollOffset;
if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
window->VisualViewport()->PostScrollEvent(prevOffset,
aPrevLayoutScrollPos);
}
}
}
nsPoint nsIPresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const {
return GetVisualViewportOffset() - GetLayoutViewportOffset();
}
nsPoint nsIPresShell::GetLayoutViewportOffset() const {
nsPoint result;
if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
result = GetVisualViewportOffset() - sf->GetScrollPosition();
result = sf->GetScrollPosition();
}
return result;
}

Просмотреть файл

@ -1647,14 +1647,15 @@ class nsIPresShell : public nsStubDocumentObserver {
return mVisualViewportSize;
}
void SetVisualViewportOffset(const nsPoint& aScrollOffset) {
mVisualViewportOffset = aScrollOffset;
}
void SetVisualViewportOffset(const nsPoint& aScrollOffset,
const nsPoint& aPrevLayoutScrollPos);
nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; }
nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const;
nsPoint GetLayoutViewportOffset() const;
virtual void WindowSizeMoveDone() = 0;
virtual void SysColorChanged() = 0;
virtual void ThemeChanged() = 0;

Просмотреть файл

@ -1144,6 +1144,23 @@ void nsRefreshDriver::RemoveTimerAdjustmentObserver(
mTimerAdjustmentObservers.RemoveElement(aObserver);
}
void nsRefreshDriver::PostVisualViewportResizeEvent(
VVPResizeEvent* aResizeEvent) {
mVisualViewportResizeEvents.AppendElement(aResizeEvent);
EnsureTimerStarted();
}
void nsRefreshDriver::DispatchVisualViewportResizeEvents() {
// We're taking a hint from scroll events and only dispatch the current set
// of queued resize events. If additional events are posted in response to
// the current events being dispatched, we'll dispatch them on the next tick.
VisualViewportResizeEventArray events;
events.SwapElements(mVisualViewportResizeEvents);
for (auto& event : events) {
event->Run();
}
}
void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent, bool aDelayed) {
if (aDelayed) {
mDelayedScrollEvents.AppendElement(aScrollEvent);
@ -1165,6 +1182,24 @@ void nsRefreshDriver::DispatchScrollEvents() {
}
}
void nsRefreshDriver::PostVisualViewportScrollEvent(
VVPScrollEvent* aScrollEvent) {
mVisualViewportScrollEvents.AppendElement(aScrollEvent);
EnsureTimerStarted();
}
void nsRefreshDriver::DispatchVisualViewportScrollEvents() {
// Scroll events are one-shot, so after running them we can drop them.
// However, dispatching a scroll event can potentially cause more scroll
// events to be posted, so we move the initial set into a temporary array
// first. (Newly posted scroll events will be dispatched on the next tick.)
VisualViewportScrollEventArray events;
events.SwapElements(mVisualViewportScrollEvents);
for (auto& event : events) {
event->Run();
}
}
void nsRefreshDriver::AddPostRefreshObserver(
nsAPostRefreshObserver* aObserver) {
mPostRefreshObservers.AppendElement(aObserver);
@ -1734,6 +1769,7 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
}
shell->FireResizeEvent();
}
DispatchVisualViewportResizeEvents();
/*
* The timer holds a reference to |this| while calling |Notify|.
@ -1757,6 +1793,7 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
// This is the FlushType::Style case.
DispatchScrollEvents();
DispatchVisualViewportScrollEvents();
DispatchAnimationEvents();
RunFullscreenSteps();
RunFrameRequestCallbacks(aNowTime);

Просмотреть файл

@ -24,6 +24,7 @@
#include "nsHashKeys.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/dom/VisualViewport.h"
#include "mozilla/layers/TransactionIdAllocator.h"
#include "mozilla/VsyncDispatcher.h"
@ -92,6 +93,10 @@ class nsAPostRefreshObserver {
class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
public nsARefreshObserver {
using TransactionId = mozilla::layers::TransactionId;
using VVPResizeEvent =
mozilla::dom::VisualViewport::VisualViewportResizeEvent;
using VVPScrollEvent =
mozilla::dom::VisualViewport::VisualViewportScrollEvent;
public:
explicit nsRefreshDriver(nsPresContext* aPresContext);
@ -146,9 +151,15 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
void AddTimerAdjustmentObserver(nsATimerAdjustmentObserver* aObserver);
void RemoveTimerAdjustmentObserver(nsATimerAdjustmentObserver* aObserver);
void PostVisualViewportResizeEvent(VVPResizeEvent* aResizeEvent);
void DispatchVisualViewportResizeEvents();
void PostScrollEvent(mozilla::Runnable* aScrollEvent, bool aDelayed = false);
void DispatchScrollEvents();
void PostVisualViewportScrollEvent(VVPScrollEvent* aScrollEvent);
void DispatchVisualViewportScrollEvents();
/**
* Add an observer that will be called after each refresh. The caller
* must remove the observer before it is deleted. This does not trigger
@ -411,7 +422,9 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
private:
typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
typedef nsTArray<RefPtr<VVPResizeEvent>> VisualViewportResizeEventArray;
typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray;
typedef nsTArray<RefPtr<VVPScrollEvent>> VisualViewportScrollEventArray;
typedef nsTHashtable<nsISupportsHashKey> RequestTable;
struct ImageStartData {
ImageStartData() {}
@ -533,7 +546,9 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
RequestTable mRequests;
ImageStartTable mStartTable;
AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
VisualViewportResizeEventArray mVisualViewportResizeEvents;
ScrollEventArray mScrollEvents;
VisualViewportScrollEventArray mVisualViewportScrollEvents;
// Scroll events on documents that might have events suppressed.
ScrollEventArray mDelayedScrollEvents;

Просмотреть файл

@ -74,6 +74,7 @@
#include "mozilla/layers/LayerTransactionChild.h"
#include "mozilla/layers/ScrollLinkedEffectDetector.h"
#include "mozilla/Unused.h"
#include "VisualViewport.h"
#include "LayersLogging.h" // for Stringify
#include <algorithm>
#include <cstdlib> // for std::abs(int/long)
@ -2736,7 +2737,7 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
// offset (e.g. by using nsIDOMWindowUtils.getVisualViewportOffset()
// in chrome JS code) before it's updated by the next APZ repaint,
// we could get incorrect results.
presContext->PresShell()->SetVisualViewportOffset(pt);
presContext->PresShell()->SetVisualViewportOffset(pt, curPos);
}
ScrollVisual();
@ -2856,6 +2857,16 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
nsPresContext::InteractionType::eScrollInteraction, TimeStamp::Now());
PostScrollEvent();
// If this is a viewport scroll, this could affect the relative offset
// between layout and visual viewport, so we might have to fire a visual
// viewport scroll event as well.
if (mIsRoot) {
if (auto* window = nsGlobalWindowInner::Cast(
mOuter->PresContext()->Document()->GetInnerWindow())) {
window->VisualViewport()->PostScrollEvent(
presContext->PresShell()->GetVisualViewportOffset(), curPos);
}
}
// notify the listeners.
for (uint32_t i = 0; i < mListeners.Length(); i++) {

Просмотреть файл

@ -0,0 +1,9 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect height="100%" width="100%" fill="yellow" />
<rect height="50%" width="100%" fill="black" />
</svg>

После

Ширина:  |  Высота:  |  Размер: 330 B

Просмотреть файл

@ -0,0 +1,25 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- We include these <use> elements simply to be sure the SVG resource URLs
get a chance to block 'onload', if they can be loaded. -->
<use xlink:href="http://mochi.test:8888/tests/layout/svg/tests/filters.svg#empty" />
<use xlink:href="http://example.org/tests/layout/svg/tests/filters.svg#empty" />
<!-- giant yellow rect in the background, just so you can visually tell
that this SVG file has loaded/rendered. -->
<rect height="100%" width="100%" fill="yellow" />
<!-- For both rects below: if it's black, its filter resolved successfully.
If it's transparent, it means we failed to load the resource
(e.g. because it was blocked as a cross-origin resource). -->
<rect height="50%" width="100%" fill="red"
filter="url(http://mochi.test:8888/tests/layout/svg/tests/filters.svg#NonWhiteToBlack)"/>
<rect y="50%"
height="50%" width="100%" fill="red"
filter="url(http://example.org/tests/layout/svg/tests/filters.svg#NonWhiteToBlack)"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Просмотреть файл

@ -0,0 +1,9 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect height="100%" width="100%" fill="yellow" />
<rect y="50%" height="50%" width="100%" fill="black" />
</svg>

После

Ширина:  |  Высота:  |  Размер: 338 B

Просмотреть файл

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<!-- so that other documents can svg:use this one and force it to
load before onload -->
<g id="empty" />
<!-- Keep all white pixels white, and change any others to black. -->
<!-- NOTE: alpha is preserved, so it will not adjust alpha edges -->
<filter id="NonWhiteToBlack" x="0%" y="0%" width="100%" height="100%">
<feComponentTransfer>
<feFuncR type="linear" slope="-1" intercept="1" />
<feFuncG type="linear" slope="-1" intercept="1" />
<feFuncB type="linear" slope="-1" intercept="1" />
</feComponentTransfer>
<feColorMatrix type="matrix" values="255 255 255 0 0
255 255 255 0 0
255 255 255 0 0
0 0 0 1 0" />
<feComponentTransfer>
<feFuncR type="linear" slope="-1" intercept="1" />
<feFuncG type="linear" slope="-1" intercept="1" />
<feFuncB type="linear" slope="-1" intercept="1" />
</feComponentTransfer>
</filter>
</defs>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

@ -3,3 +3,9 @@ support-files =
file_disabled_iframe.html
[test_disabled.html]
[test_filter_crossorigin.html]
support-files =
filters.svg
file_filter_crossorigin.svg
file_black_yellow.svg
file_yellow_black.svg

Просмотреть файл

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=695385
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 695385</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="run()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=695385">Mozilla Bug 695385</a>
<br>
<!-- These iframes' renderings are expected to match: -->
<iframe src="http://mochi.test:8888/tests/layout/svg/tests/file_filter_crossorigin.svg"></iframe>
<iframe src="file_black_yellow.svg"></iframe>
<br>
<!-- These iframes' renderings are expected to match: -->
<iframe src="http://example.org/tests/layout/svg/tests/file_filter_crossorigin.svg"></iframe>
<iframe src="file_yellow_black.svg"></iframe>
<pre id="test">
<script type="application/javascript">
// Main Function
function run() {
SimpleTest.waitForExplicitFinish();
let snapshots = new Array(4);
for (let i = 0; i < snapshots.length; i++) {
snapshots[i] = snapshotWindow(frames[i].window, false);
}
// Compare mochi.test iframe against its reference:
assertSnapshots(snapshots[0], snapshots[1], true, null,
"Testcase loaded from mochi.test", "Reference: black/yellow");
// Compare example.org iframe against its reference:
assertSnapshots(snapshots[2], snapshots[3], true, null,
"Testcase loaded from example.org", "Reference: yellow/black");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -58,7 +58,7 @@ public class SpeechSynthesisService {
: sTTS.getLanguage();
for (Locale locale : getAvailableLanguages()) {
final Set<String> features = sTTS.getFeatures(locale);
boolean isLocal = features.contains(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
boolean isLocal = features != null && features.contains(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
String localeStr = locale.toString();
registerVoice("moz-tts:android:" + localeStr, locale.getDisplayName(), localeStr.replace("_", "-"), !isLocal, defaultLocale == locale);
}

Просмотреть файл

@ -1557,6 +1557,12 @@ VARCACHE_PREF(
RelaxedAtomicUint32, 1000
)
VARCACHE_PREF(
"media.test.video-suspend",
MediaTestVideoSuspend,
RelaxedAtomicBool, false
)
//---------------------------------------------------------------------------
// Network prefs
//---------------------------------------------------------------------------

Просмотреть файл

@ -4,6 +4,7 @@ job-defaults:
worker-type:
by-platform:
linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
macosx64.*: releng-hardware/gecko-t-osx-1010
windows10-64.*: aws-provisioner-v1/gecko-t-win10-64
worker:
by-platform:
@ -97,6 +98,7 @@ mozbase:
description: testing/mozbase unit tests
platform:
- linux64/opt
- macosx64/opt
- windows10-64/opt
python-version: [2, 3]
treeherder:
@ -126,6 +128,7 @@ mozlint:
description: python/mozlint unit tests
platform:
- linux64/opt
- macosx64/opt
- windows10-64/opt
python-version: [2]
treeherder:

Просмотреть файл

@ -92,6 +92,7 @@ or added to the task's docker image then added to the PATH.
EXIT_PURGE_CACHE = 72
IS_MACOSX = sys.platform == 'darwin'
IS_POSIX = os.name == 'posix'
IS_WINDOWS = os.name == 'nt'
@ -402,7 +403,9 @@ def vcs_checkout(source_repo, dest, store_path,
print('revision is not specified for checkout')
sys.exit(1)
if IS_POSIX:
if IS_MACOSX:
hg_bin = '/tools/python27-mercurial/bin/hg'
elif IS_POSIX:
hg_bin = 'hg'
elif IS_WINDOWS:
# This is where OCC installs it in the AMIs.
@ -411,6 +414,7 @@ def vcs_checkout(source_repo, dest, store_path,
print('could not find Mercurial executable: %s' % hg_bin)
sys.exit(1)
store_path = os.path.abspath(store_path)
args = [
hg_bin,
'robustcheckout',

Просмотреть файл

@ -64,12 +64,20 @@ def support_vcs_checkout(config, job, taskdesc, sparse=False):
This can only be used with ``run-task`` tasks, as the cache name is
reserved for ``run-task`` tasks.
"""
is_win = job['worker']['os'] == 'windows'
worker = job['worker']
is_mac = worker['os'] == 'macosx'
is_win = worker['os'] == 'windows'
is_linux = worker['os'] == 'linux'
assert is_mac or is_win or is_linux
if is_win:
checkoutdir = './build'
geckodir = '{}/src'.format(checkoutdir)
hgstore = 'y:/hg-shared'
elif is_mac:
checkoutdir = './checkouts'
geckodir = '{}/gecko'.format(checkoutdir)
hgstore = '{}/hg-shared'.format(checkoutdir)
else:
checkoutdir = '{workdir}/checkouts'.format(**job['run'])
geckodir = '{}/gecko'.format(checkoutdir)
@ -78,7 +86,7 @@ def support_vcs_checkout(config, job, taskdesc, sparse=False):
level = config.params['level']
# native-engine and generic-worker do not support caches (yet), so we just
# do a full clone every time :(
if job['worker']['implementation'] in ('docker-worker', 'docker-engine'):
if worker['implementation'] in ('docker-worker', 'docker-engine'):
name = 'level-%s-checkouts' % level
# comm-central checkouts need their own cache, because clobber won't

Просмотреть файл

@ -35,10 +35,15 @@ defaults = {
@run_job_using('generic-worker', 'python-test', schema=python_test_schema, defaults=defaults)
def configure_python_test(config, job, taskdesc):
run = job['run']
worker = job['worker']
if worker['os'] == 'macosx' and run['python-version'] == 3:
# OSX hosts can't seem to find python 3 on their own
run['python-version'] = '/usr/local/bin/python3'
# defer to the mach implementation
run['mach'] = 'python-test --python {python-version} --subsuite {subsuite}'.format(**run)
run['using'] = 'mach'
del run['python-version']
del run['subsuite']
configure_taskdesc_for_run(config, job, taskdesc, job['worker']['implementation'])
configure_taskdesc_for_run(config, job, taskdesc, worker['implementation'])

Просмотреть файл

@ -20,7 +20,7 @@ run_task_schema = Schema({
# tend to hide their caches. This cache is never added for level-1 jobs.
Required('cache-dotcache'): bool,
# if true (the default), perform a checkout in {workdir}/checkouts/gecko
# if true (the default), perform a checkout of gecko on the worker
Required('checkout'): bool,
# The sparse checkout profile to use. Value is the filename relative to the
@ -41,12 +41,12 @@ run_task_schema = Schema({
})
def common_setup(config, job, taskdesc, command, geckodir):
def common_setup(config, job, taskdesc, command):
run = job['run']
if run['checkout']:
support_vcs_checkout(config, job, taskdesc,
sparse=bool(run['sparse-profile']))
command.append('--vcs-checkout={}'.format(geckodir))
command.append('--vcs-checkout={}'.format(taskdesc['worker']['env']['GECKO_PATH']))
if run['sparse-profile']:
command.append('--sparse-profile=build/sparse-profiles/%s' %
@ -73,8 +73,7 @@ def docker_worker_run_task(config, job, taskdesc):
run = job['run']
worker = taskdesc['worker'] = job['worker']
command = ['/builds/worker/bin/run-task']
common_setup(config, job, taskdesc, command,
geckodir='{workdir}/checkouts/gecko'.format(**run))
common_setup(config, job, taskdesc, command)
if run.get('cache-dotcache'):
worker['caches'].append({
@ -101,8 +100,7 @@ def native_engine_run_task(config, job, taskdesc):
run = job['run']
worker = taskdesc['worker'] = job['worker']
command = ['./run-task']
common_setup(config, job, taskdesc, command,
geckodir='{workdir}/checkouts/gecko'.format(**run))
common_setup(config, job, taskdesc, command)
worker['context'] = run_task_url(config)
@ -122,15 +120,16 @@ def generic_worker_run_task(config, job, taskdesc):
run = job['run']
worker = taskdesc['worker'] = job['worker']
is_win = worker['os'] == 'windows'
is_mac = worker['os'] == 'macosx'
if is_win:
command = ['C:/mozilla-build/python3/python3.exe', 'run-task']
geckodir = './build/src'
elif is_mac:
command = ['/tools/python36/bin/python3.6', 'run-task']
else:
command = ['./run-task']
geckodir = '{workdir}/checkouts/gecko'.format(**run)
common_setup(config, job, taskdesc, command, geckodir=geckodir)
common_setup(config, job, taskdesc, command)
worker.setdefault('mounts', [])
if run.get('cache-dotcache'):

Просмотреть файл

@ -2501,3 +2501,42 @@ var PluginUtils =
return false;
}
};
class EventCounter {
constructor(aTarget, aType, aOptions = {}) {
this.target = aTarget;
this.type = aType;
this.options = aOptions;
this.eventCount = 0;
// Bug 1512817:
// SpecialPowers is picky and needs to be passed an explicit reference to
// the function to be called. To avoid having to bind "this", we therefore
// define the method this way, via a property.
this.handleEvent = (aEvent) => {
this.eventCount++;
};
if (aOptions.mozSystemGroup) {
SpecialPowers.addSystemEventListener(aTarget, aType,
this.handleEvent,
aOptions.capture);
} else {
aTarget.addEventListener(aType, this, aOptions);
}
}
unregister() {
if (this.options.mozSystemGroup) {
SpecialPowers.removeSystemEventListener(this.target, this.type,
this.handleEvent,
this.options.capture);
} else {
this.target.removeEventListener(this.type, this, this.options);
}
}
get count() {
return this.eventCount;
}
}

Просмотреть файл

@ -2,6 +2,8 @@
subsuite = mozbase
skip-if = python == 3
[test_binary.py]
skip-if = os == "mac"
[test_install.py]
skip-if = os == "mac" # intermittent
[test_is_installer.py]
[test_uninstall.py]

Просмотреть файл

@ -409,7 +409,7 @@ class TestMetadata(object):
candidate_paths = set()
if any(self.is_wpt_path(path) for path in paths):
if flavor in (None, 'web-platform-tests') and any(self.is_wpt_path(p) for p in paths):
self.add_wpt_manifest_data()
for path in sorted(paths):

Просмотреть файл

@ -3018,11 +3018,9 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
<p>This license applies to parts of the code in:</p>
<ul>
#ifndef RELEASE_OR_BETA
<li><code>browser/extensions/formautofill/content/heuristicsRegexp.js</code></li>
<li><code>browser/extensions/formautofill/FormAutofillHeuristics.jsm</code></li>
<li><code>browser/extensions/formautofill/FormAutofillNameUtils.jsm</code></li>
#endif
<li><code>editor/libeditor/EditorEventListener.cpp</code></li>
<li><code>mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StrictModeContext.java</code></li>
<li><code>security/sandbox/</code></li>

Просмотреть файл

@ -103,6 +103,10 @@ uses-unsafe-cpows = true
[browser_isSynthetic.js]
[browser_keyevents_during_autoscrolling.js]
[browser_label_textlink.js]
[browser_suspend_videos_outside_viewport.js]
support-files =
file_outside_viewport_videos.html
gizmo.mp4
[browser_mediaPlayback.js]
tags = audiochannel
[browser_mediaPlayback_mute.js]

Просмотреть файл

@ -0,0 +1,33 @@
/**
* This test is used to ensure we suspend video decoding if video is not in the
* viewport.
*/
"use strict";
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_outside_viewport_videos.html";
async function test_suspend_video_decoding() {
let videos = content.document.getElementsByTagName("video");
for (let video of videos) {
info(`- start video on the ${video.id} side and outside the viewport -`);
await video.play();
ok(true, `video started playing`);
ok(video.isVideoDecodingSuspended, `video decoding is suspended`);
}
}
add_task(async function setup_test_preference() {
await SpecialPowers.pushPrefEnv({"set": [
["media.suspend-bkgnd-video.enabled", true],
["media.suspend-bkgnd-video.delay-ms", 0],
]});
});
add_task(async function start_test() {
await BrowserTestUtils.withNewTab({
gBrowser,
url: PAGE,
}, async browser => {
await ContentTask.spawn(browser, null, test_suspend_video_decoding);
});
});

Просмотреть файл

@ -0,0 +1,41 @@
<html>
<head>
<title>outside viewport videos</title>
<style>
/**
* These CSS would move elements to the far left/right/top/bottom where user
* can not see elements in the viewport if user doesn't scroll the page.
*/
.outside-left {
position: absolute;
left: -1000%;
}
.outside-right {
position: absolute;
right: -1000%;
}
.outside-top {
position: absolute;
top: -1000%;
}
.outside-bottom {
position: absolute;
bottom: -1000%;
}
</style>
</head>
<body>
<div class="outside-left">
<video id="left" src="gizmo.mp4">
</div>
<div class="outside-right">
<video id="right" src="gizmo.mp4">
</div>
<div class="outside-top">
<video id="top" src="gizmo.mp4">
</div>
<div class="outside-bottom">
<video id="bottom" src="gizmo.mp4">
</div>
</body>
</html>

Просмотреть файл

@ -54,10 +54,10 @@
<xul:hbox class="popup-notification-footer-container">
<children includes="popupnotificationfooter"/>
</xul:hbox>
<xul:hbox class="popup-notification-button-container">
<xul:hbox class="popup-notification-button-container panel-footer">
<children includes="button"/>
<xul:button anonid="secondarybutton"
class="popup-notification-button"
class="popup-notification-button popup-notification-secondary-button"
xbl:inherits="oncommand=secondarybuttoncommand,label=secondarybuttonlabel,accesskey=secondarybuttonaccesskey,hidden=secondarybuttonhidden"/>
<xul:toolbarseparator xbl:inherits="hidden=dropmarkerhidden"/>
<xul:button anonid="menubutton"
@ -73,11 +73,10 @@
</xul:menupopup>
</xul:button>
<xul:button anonid="button"
class="popup-notification-button"
default="true"
class="popup-notification-button popup-notification-primary-button"
label="&defaultButton.label;"
accesskey="&defaultButton.accesskey;"
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey,highlight=buttonhighlight,disabled=mainactiondisabled"/>
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey,default=buttonhighlight,disabled=mainactiondisabled"/>
</xul:hbox>
</content>
<implementation>

Просмотреть файл

@ -838,7 +838,7 @@ PopupNotifications.prototype = {
if (n.mainAction) {
popupnotification.setAttribute("buttonlabel", n.mainAction.label);
popupnotification.setAttribute("buttonaccesskey", n.mainAction.accessKey);
popupnotification.setAttribute("buttonhighlight", !n.mainAction.disableHighlight);
popupnotification.toggleAttribute("buttonhighlight", !n.mainAction.disableHighlight);
popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonEvent(event, 'buttoncommand');");
popupnotification.setAttribute("dropmarkerpopupshown", "PopupNotifications._onButtonEvent(event, 'dropmarkerpopupshown');");
popupnotification.setAttribute("learnmoreclick", "PopupNotifications._onButtonEvent(event, 'learnmoreclick');");
@ -846,7 +846,7 @@ PopupNotifications.prototype = {
} else {
// Enable the default button to let the user close the popup if the close button is hidden
popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonEvent(event, 'buttoncommand');");
popupnotification.setAttribute("buttonhighlight", "true");
popupnotification.toggleAttribute("buttonhighlight", true);
popupnotification.removeAttribute("buttonlabel");
popupnotification.removeAttribute("buttonaccesskey");
popupnotification.removeAttribute("dropmarkerpopupshown");

Просмотреть файл

@ -12,6 +12,8 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
%include ../../shared/global.inc.css
/* ::::: XBL bindings ::::: */
menulist > menupopup {
@ -48,14 +50,6 @@ wizard {
font: message-box;
}
/* deprecated */
window.dialog {
padding-top: 8px;
padding-bottom: 10px;
padding-inline-start: 8px;
padding-inline-end: 10px;
}
/* ::::: alert icons :::::*/
.message-icon {
@ -288,6 +282,3 @@ popupnotificationcontent {
background-image: url("chrome://global/skin/icons/autoscroll-horizontal.svg");
}
/* :::::: Close button icons ::::: */
%include ../../shared/close-icon.inc.css

Просмотреть файл

@ -8,10 +8,10 @@
%include ../../shared/popupnotification.inc.css
.popup-notification-button:-moz-focusring {
outline: 1px -moz-dialogtext dotted;
outline: 1px dotted;
outline-offset: -5px;
}
.popup-notification-button[anonid="secondarybutton"]:not([hidden="true"]) ~ .popup-notification-button[default]:not([highlight="true"]) {
.popup-notification-secondary-button:not([hidden="true"]) ~ .popup-notification-primary-button:not([default]) {
border-inline-start: 1px solid var(--panel-separator-color);
}

Просмотреть файл

@ -9,6 +9,8 @@
%include shared.inc
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
%include ../../shared/global.inc.css
/* ::::: XBL bindings ::::: */
menulist > menupopup {
@ -42,14 +44,6 @@ wizard {
font: message-box;
}
/* deprecated */
window.dialog {
padding-top: 8px;
padding-bottom: 10px;
padding-inline-start: 8px;
padding-inline-end: 10px;
}
/* ::::: alert icons :::::*/
.message-icon,
@ -320,6 +314,3 @@ popupnotificationcontent {
visibility: collapse;
}
/* :::::: Close button icons ::::: */
%include ../../shared/close-icon.inc.css

Просмотреть файл

@ -17,6 +17,6 @@
color: #aa1b08;
}
.popup-notification-button[anonid="secondarybutton"]:not([hidden="true"]) ~ .popup-notification-button[default]:not([highlight="true"]) {
.popup-notification-secondary-button:not([hidden="true"]) ~ .popup-notification-primary-button:not([default]) {
border-inline-start: 1px solid var(--panel-separator-color);
}

Просмотреть файл

@ -1,27 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.close-icon {
-moz-appearance: none;
-moz-context-properties: fill, fill-opacity;
list-style-image: url(chrome://global/skin/icons/close.svg);
color: inherit !important;
fill: currentColor;
fill-opacity: 0;
}
.close-icon:hover {
fill-opacity: 0.1;
}
.close-icon:hover:active {
fill-opacity: 0.2;
}
.close-icon > .button-icon,
.close-icon > .button-box > .button-icon,
.close-icon > .toolbarbutton-icon {
width: 20px;
height: 20px;
}

Просмотреть файл

@ -0,0 +1,67 @@
/* 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/. */
/* Close icon */
.close-icon {
-moz-appearance: none;
-moz-context-properties: fill, fill-opacity;
list-style-image: url(chrome://global/skin/icons/close.svg);
color: inherit !important;
fill: currentColor;
fill-opacity: 0;
}
.close-icon:hover {
fill-opacity: 0.1;
}
.close-icon:hover:active {
fill-opacity: 0.2;
}
.close-icon > .button-icon,
.close-icon > .button-box > .button-icon,
.close-icon > .toolbarbutton-icon {
width: 20px;
height: 20px;
}
/* Panel footers */
.panel-footer {
background-color: var(--arrowpanel-dimmed);
}
.panel-footer > button {
-moz-appearance: none;
/* !important overrides :hover and :active colors from button.css: */
color: inherit !important;
}
.panel-footer > button[disabled] {
color: var(--panel-disabled-color) !important;
}
.panel-footer > button:not([disabled]):hover {
background-color: var(--arrowpanel-dimmed);
}
.panel-footer > button:not([disabled]):hover:active {
background-color: var(--arrowpanel-dimmed-further);
}
.panel-footer > button:not([disabled])[default] {
color: white !important;
background-color: #0996f8;
}
.panel-footer > button:not([disabled])[default]:hover {
background-color: #0675d3;
}
.panel-footer > button:not([disabled])[default]:hover:active {
background-color: #0568ba;
}

Просмотреть файл

@ -41,7 +41,6 @@
}
.popup-notification-button-container {
background-color: var(--arrowpanel-dimmed);
display: flex;
}
@ -59,48 +58,21 @@
.popup-notification-button {
flex: 1;
-moz-appearance: none;
color: inherit;
margin: 0;
min-width: 0;
min-height: 41px;
border-top: 1px solid var(--panel-separator-color);
}
.popup-notification-button:hover:not([disabled]) {
background-color: var(--arrowpanel-dimmed);
color: inherit; /* override button.css on Linux */
}
.popup-notification-button:hover:active:not([disabled]) {
background-color: var(--arrowpanel-dimmed-further);
color: inherit; /* override button.css on Mac */
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
.popup-notification-button[disabled] {
background-color: var(--arrowpanel-dimmed-further);
color: var(--panel-disabled-color);
}
.popup-notification-button[default]:not([alone]) {
.popup-notification-primary-button:not([alone]) {
flex: 0 50%;
}
.popup-notification-button[default][highlight="true"]:not([disabled]) {
background-color: #0996f8;
color: white;
}
.popup-notification-button[default][highlight="true"]:hover:not([disabled]) {
background-color: #0675d3;
}
.popup-notification-button[default][highlight="true"]:hover:active:not([disabled]) {
background-color: #0568ba;
}
.popup-notification-button[anonid="secondarybutton"][hidden="true"] ~ .popup-notification-button[default] {
.popup-notification-secondary-button[hidden="true"] ~ .popup-notification-primary-button {
flex: 1;
}

Просмотреть файл

@ -12,6 +12,8 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
%include ../../shared/global.inc.css
/* ::::: XBL bindings ::::: */
menulist > menupopup {
@ -47,14 +49,6 @@ wizard {
font: message-box;
}
/* deprecated */
window.dialog {
padding-top: 8px;
padding-bottom: 10px;
padding-inline-start: 8px;
padding-inline-end: 10px;
}
/* ::::: alert icons :::::*/
.message-icon,
@ -293,6 +287,3 @@ popupnotificationcontent {
background-image: url("chrome://global/skin/icons/autoscroll-horizontal.svg");
}
/* :::::: Close button icons ::::: */
%include ../../shared/close-icon.inc.css

Просмотреть файл

@ -19,12 +19,12 @@
}
}
/* Swap the default and secondary action, because Windows
* platform conventions put the default action on the left. */
.popup-notification-button[default] {
/* Swap the primary and secondary action, because Windows
* platform conventions put the primary action on the left. */
.popup-notification-primary-button {
order: -1;
}
.popup-notification-button[anonid="secondarybutton"]:not([hidden="true"]) ~ .popup-notification-button[default]:not([highlight="true"]) {
.popup-notification-secondary-button:not([hidden="true"]) ~ .popup-notification-primary-button:not([default]) {
border-inline-end: 1px solid var(--panel-separator-color);
}

Просмотреть файл

@ -3,5 +3,6 @@ subsuite = mozlint
skip-if = python == 3
[test_eslint.py]
skip-if = os == "win" # node not installed on worker
skip-if = os == "win" || os == "mac" # node not installed on worker
[test_flake8.py]
skip-if = os == "mac" # pip unable to find 'flake8==3.5.0'

Просмотреть файл

@ -131,6 +131,9 @@ struct BaseEventFlags {
// listener is added to chrome node, so, don't set this to true for the
// events which are fired a lot of times like eMouseMove.
bool mOnlySystemGroupDispatchInContent : 1;
// If mOnlySystemGroupDispatch is true, the event will be dispatched only to
// event listeners added in the system group.
bool mOnlySystemGroupDispatch : 1;
// The event's action will be handled by APZ. The main thread should not
// perform its associated action.
bool mHandledByAPZ : 1;
@ -454,7 +457,8 @@ class WidgetEvent : public WidgetEventTime {
mFlags.mBubbles = true;
break;
default:
if (mMessage == eResize || mMessage == eEditorInput) {
if (mMessage == eResize || mMessage == eMozVisualResize ||
mMessage == eMozVisualScroll || mMessage == eEditorInput) {
mFlags.mCancelable = false;
} else {
mFlags.mCancelable = true;

Просмотреть файл

@ -55,6 +55,8 @@ NS_EVENT_MESSAGE(eAccessKeyNotFound)
NS_EVENT_MESSAGE(eResize)
NS_EVENT_MESSAGE(eScroll)
NS_EVENT_MESSAGE(eMozVisualResize)
NS_EVENT_MESSAGE(eMozVisualScroll)
// Application installation
NS_EVENT_MESSAGE(eInstall)

Просмотреть файл

@ -1881,6 +1881,10 @@ STATIC_ATOMS = [
# MediaDevices device change event
Atom("ondevicechange", "ondevicechange"),
# Internal Visual Viewport events
Atom("onmozvisualresize", "onmozvisualresize"),
Atom("onmozvisualscroll", "onmozvisualscroll"),
# WebExtensions
Atom("moz_extension", "moz-extension"),
Atom("all_urlsPermission", "<all_urls>"),