mirror of
https://github.com/zen-browser/desktop.git
synced 2026-08-19 05:01:33 +00:00
gh-14843: Prevent accessing old media data (gh-14888)
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css
|
||||
--- a/browser/themes/linux/browser.css
|
||||
+++ b/browser/themes/linux/browser.css
|
||||
@@ -32,11 +32,11 @@
|
||||
*/
|
||||
@media (-moz-gtk-csd-transparency-available) {
|
||||
:root[customtitlebar] {
|
||||
background-color: transparent;
|
||||
|
||||
- &[sizemode="normal"]:not([gtktiledwindow]) {
|
||||
+ &[sizemode="normal"]:not([tiled]) {
|
||||
/* Firefox draws its contents to a child window, while GTK takes care of
|
||||
* drawing the toplevel (which in most cases is just the window
|
||||
* decorations).
|
||||
*
|
||||
* Due to how X11 child windows work, pixels painted by a child window will
|
||||
diff --git a/browser/themes/shared/browser-shared.css b/browser/themes/shared/browser-shared.css
|
||||
--- a/browser/themes/shared/browser-shared.css
|
||||
+++ b/browser/themes/shared/browser-shared.css
|
||||
@@ -433,18 +433,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-platform: linux) and (-moz-gtk-csd-reversed-placement: 0) {
|
||||
:root:not([sizemode="normal"]) &[type="pre-tabs"],
|
||||
- :root[gtktiledwindow] &[type="pre-tabs"] {
|
||||
+ :root[tiled] &[type="pre-tabs"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-gtk-csd-reversed-placement) {
|
||||
:root:not([sizemode="normal"]) &[type="post-tabs"],
|
||||
- :root[gtktiledwindow] &[type="post-tabs"] {
|
||||
+ :root[tiled] &[type="post-tabs"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
|
||||
--- a/widget/gtk/nsWindow.cpp
|
||||
+++ b/widget/gtk/nsWindow.cpp
|
||||
@@ -3683,11 +3683,11 @@
|
||||
#ifdef ACCESSIBILITY
|
||||
DispatchRestoreEventAccessible();
|
||||
#endif // ACCESSIBILITY
|
||||
}
|
||||
|
||||
- mIsTiled = aEvent->new_window_state & GDK_WINDOW_STATE_TILED;
|
||||
+ SetIsTiled(aEvent->new_window_state & GDK_WINDOW_STATE_TILED);
|
||||
LOG("\tTiled: %d\n", int(mIsTiled));
|
||||
mResizableEdges = [&] {
|
||||
Sides result;
|
||||
if (mSizeMode != nsSizeMode_Normal) {
|
||||
return result;
|
||||
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
|
||||
--- a/widget/nsIWidget.h
|
||||
+++ b/widget/nsIWidget.h
|
||||
@@ -1379,10 +1379,12 @@
|
||||
|
||||
protected:
|
||||
// Returns whether compositing should use an external surface size.
|
||||
virtual bool UseExternalCompositingSurface() const { return false; }
|
||||
|
||||
+ void SetIsTiled(bool);
|
||||
+
|
||||
/**
|
||||
* Starts the OMTC compositor destruction sequence.
|
||||
*
|
||||
* When this function returns, the compositor should not be
|
||||
* able to access the opengl context anymore.
|
||||
diff --git a/widget/nsIWidget.cpp b/widget/nsIWidget.cpp
|
||||
--- a/widget/nsIWidget.cpp
|
||||
+++ b/widget/nsIWidget.cpp
|
||||
@@ -327,10 +327,16 @@
|
||||
void nsIWidget::QuitIME() {
|
||||
IMEStateManager::WidgetOnQuit(this);
|
||||
this->mIMEHasQuit = true;
|
||||
}
|
||||
|
||||
+void nsIWidget::SetIsTiled(bool aIsTiled) {
|
||||
+ // TODO: Do we want to report an event here or something when stuff changes?
|
||||
+ // For now this is propagated in a somewhat out-of-band way via AppWindow.
|
||||
+ mIsTiled = aIsTiled;
|
||||
+}
|
||||
+
|
||||
void nsIWidget::DestroyCompositor() {
|
||||
RevokeTransactionIdAllocator();
|
||||
|
||||
// We release this before releasing the compositor, since it may hold the
|
||||
// last reference to our ClientLayerManager. ClientLayerManager's dtor can
|
||||
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
|
||||
--- a/xpcom/ds/StaticAtoms.py
|
||||
+++ b/xpcom/ds/StaticAtoms.py
|
||||
@@ -1512,11 +1512,10 @@
|
||||
Atom("gamma", "gamma"),
|
||||
Atom("glyphRef", "glyphRef"),
|
||||
Atom("grad", "grad"),
|
||||
Atom("gradientTransform", "gradientTransform"),
|
||||
Atom("gradientUnits", "gradientUnits"),
|
||||
- Atom("gtktiledwindow", "gtktiledwindow"),
|
||||
Atom("hardLight", "hard-light"),
|
||||
Atom("hue", "hue"),
|
||||
Atom("hueRotate", "hueRotate"),
|
||||
Atom("identity", "identity"),
|
||||
Atom("image_rendering", "image-rendering"),
|
||||
@@ -1950,10 +1949,11 @@
|
||||
Atom("superscriptshift", "superscriptshift"),
|
||||
Atom("symmetric", "symmetric"),
|
||||
Atom("tanh", "tanh"),
|
||||
Atom("tan", "tan"),
|
||||
Atom("tendsto", "tendsto"),
|
||||
+ Atom("tiled", "tiled"),
|
||||
Atom("times", "times"),
|
||||
Atom("transpose", "transpose"),
|
||||
Atom("union_", "union"),
|
||||
Atom("uplimit", "uplimit"),
|
||||
Atom("variance", "variance"),
|
||||
diff --git a/xpfe/appshell/AppWindow.cpp b/xpfe/appshell/AppWindow.cpp
|
||||
--- a/xpfe/appshell/AppWindow.cpp
|
||||
+++ b/xpfe/appshell/AppWindow.cpp
|
||||
@@ -1992,11 +1992,11 @@
|
||||
aRootElement.SetAttr(nsGkAtoms::sizemode, sizeString, IgnoreErrors());
|
||||
if (aShouldPersist && aPersistString.Find(u"sizemode") >= 0) {
|
||||
(void)SetPersistentValue(nsGkAtoms::sizemode, sizeString);
|
||||
}
|
||||
}
|
||||
- aRootElement.SetBoolAttr(nsGkAtoms::gtktiledwindow, mWindow->IsTiled());
|
||||
+ aRootElement.SetBoolAttr(nsGkAtoms::tiled, mWindow->IsTiled());
|
||||
}
|
||||
|
||||
void AppWindow::SavePersistentAttributes(
|
||||
const PersistentAttributes aAttributes) {
|
||||
// can happen when the persistence timer fires at an inopportune time
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp
|
||||
--- a/widget/windows/nsWindow.cpp
|
||||
+++ b/widget/windows/nsWindow.cpp
|
||||
@@ -6423,10 +6423,13 @@
|
||||
// Skip window size change events below on minimization.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+ // Recompute tiled state.
|
||||
+ SetIsTiled(mWnd && ::IsWindowArranged(mWnd));
|
||||
+
|
||||
// Notify visibility change when window is activated.
|
||||
if (!(wp->flags & SWP_NOACTIVATE) && NeedsToTrackWindowOcclusionState()) {
|
||||
WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
|
||||
this, mFrameState->GetSizeMode() != nsSizeMode_Minimized);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
|
||||
--- a/widget/cocoa/nsCocoaWindow.mm
|
||||
+++ b/widget/cocoa/nsCocoaWindow.mm
|
||||
@@ -70,10 +70,11 @@
|
||||
#include "nsDocShell.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "qcms.h"
|
||||
|
||||
+#import <objc/runtime.h>
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/NativeKeyBindingsType.h"
|
||||
@@ -6948,10 +6949,31 @@
|
||||
return nsSizeMode_Maximized;
|
||||
}
|
||||
return nsSizeMode_Normal;
|
||||
}
|
||||
|
||||
+// The tiling state lives in a private NSWindow ivar "_tilingStateController"
|
||||
+// (an _NSWMWindowTilingStateController). That controller's -tilingState returns
|
||||
+// a non-nil _WMWindowTilingState only while the window is tiled by the system
|
||||
+// window manager. None of this is public API, so it is all guarded and degrades
|
||||
+// to false if the internals ever change.
|
||||
+static bool WindowIsTiled(NSWindow* aWindow) {
|
||||
+ if (!aWindow) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ static Ivar sControllerIvar =
|
||||
+ class_getInstanceVariable([NSWindow class], "_tilingStateController");
|
||||
+ if (!sControllerIvar) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ id controller = object_getIvar(aWindow, sControllerIvar);
|
||||
+ if (![controller respondsToSelector:@selector(tilingState)]) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ return [controller performSelector:@selector(tilingState)] != nil;
|
||||
+}
|
||||
+
|
||||
void nsCocoaWindow::ReportMoveEvent() {
|
||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
||||
|
||||
// Prevent recursion, which can become infinite (see bug 708278). This
|
||||
// can happen when the call to [NSWindow setFrameTopLeftPoint:] in
|
||||
@@ -6961,10 +6983,11 @@
|
||||
return;
|
||||
}
|
||||
mInReportMoveEvent = true;
|
||||
|
||||
UpdateBounds();
|
||||
+ SetIsTiled(WindowIsTiled(mWindow));
|
||||
|
||||
// The zoomed state can change when we're moving, in which case we need to
|
||||
// update our internal mSizeMode. This can happen either if we're maximized
|
||||
// and then moved, or if we're not maximized and moved back to zoomed state.
|
||||
if (mWindow && (mSizeMode == nsSizeMode_Maximized) ^ mWindow.isZoomed) {
|
||||
@@ -7055,10 +7078,11 @@
|
||||
|
||||
void nsCocoaWindow::ReportSizeEvent() {
|
||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
||||
|
||||
UpdateBounds();
|
||||
+ SetIsTiled(WindowIsTiled(mWindow));
|
||||
LayoutDeviceIntRect innerBounds = GetClientBounds();
|
||||
if (mWidgetListener) {
|
||||
mWidgetListener->WindowResized(this, innerBounds.Size());
|
||||
}
|
||||
if (mAttachedWidgetListener) {
|
||||
|
||||
@@ -39,5 +39,14 @@
|
||||
"type": "phabricator",
|
||||
"id": "D291714",
|
||||
"name": "gh-12979 Clip dirty_rect to device_size"
|
||||
},
|
||||
{
|
||||
"type": "phabricator",
|
||||
"ids": [
|
||||
"D312028",
|
||||
"D312036",
|
||||
"D312091"
|
||||
],
|
||||
"name": "Expose tiled attribute to all platforms"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -22,27 +22,64 @@ class ZenSidebarNotification extends MozLitElement {
|
||||
static properties = {
|
||||
headingL10nId: { type: String, fluent: true },
|
||||
links: { type: Array },
|
||||
autoHideMs: { type: Number },
|
||||
};
|
||||
|
||||
constructor({ headingL10nId = "", links = [] } = {}) {
|
||||
#autoHideAnimation = null;
|
||||
|
||||
constructor({ headingL10nId = "", links = [], autoHideMs = 0 } = {}) {
|
||||
super();
|
||||
this.headingL10nId = headingL10nId;
|
||||
this.links = links;
|
||||
this.autoHideMs = autoHideMs;
|
||||
// Hold the countdown while the user is reading.
|
||||
this.addEventListener("mouseenter", () => this.#autoHideAnimation?.pause());
|
||||
this.addEventListener("mouseleave", () => this.#autoHideAnimation?.play());
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.parentElement) {
|
||||
this.#animateIn();
|
||||
this.#animateIn().then(() => this.#startAutoHide());
|
||||
}
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.#autoHideAnimation?.playState !== "finished") {
|
||||
// Stop a running countdown; a finished one must keep its fill so
|
||||
// the bar doesn't snap back to full during the fade-out.
|
||||
this.#autoHideAnimation?.cancel();
|
||||
}
|
||||
this.#autoHideAnimation = null;
|
||||
this.#animateOut().then(() => {
|
||||
super.remove();
|
||||
});
|
||||
}
|
||||
|
||||
async #startAutoHide() {
|
||||
if (!this.autoHideMs || !this.isConnected) {
|
||||
return;
|
||||
}
|
||||
await this.updateComplete;
|
||||
const bar = this.shadowRoot?.querySelector(
|
||||
".zen-sidebar-notification-progress-bar"
|
||||
);
|
||||
if (!bar || this.#autoHideAnimation) {
|
||||
return;
|
||||
}
|
||||
this.#autoHideAnimation = bar.animate(
|
||||
{ transform: ["scaleX(1)", "scaleX(0)"] },
|
||||
{ duration: this.autoHideMs, easing: "linear", fill: "forwards" }
|
||||
);
|
||||
this.#autoHideAnimation.finished.then(
|
||||
() => this.remove(),
|
||||
() => {}
|
||||
);
|
||||
if (this.matches(":hover")) {
|
||||
this.#autoHideAnimation.pause();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
@@ -62,6 +99,15 @@ class ZenSidebarNotification extends MozLitElement {
|
||||
<img src="chrome://browser/skin/zen-icons/close.svg" />
|
||||
</div>
|
||||
</div>
|
||||
${
|
||||
this.autoHideMs
|
||||
? html`
|
||||
<div class="zen-sidebar-notification-progress">
|
||||
<div class="zen-sidebar-notification-progress-bar"></div>
|
||||
</div>
|
||||
`
|
||||
: null
|
||||
}
|
||||
<div class="zen-sidebar-notification-body">
|
||||
${this.links.map(
|
||||
link => html`
|
||||
|
||||
@@ -7,6 +7,7 @@ import createSidebarNotification from "chrome://browser/content/zen-components/Z
|
||||
const ZEN_UPDATE_PREF = "zen.updates.last-version";
|
||||
const ZEN_BUILD_ID_PREF = "zen.updates.last-build-id";
|
||||
const ZEN_UPDATE_SHOW = "zen.updates.show-update-notification";
|
||||
const ZEN_UPDATE_NOTIFICATION_TIMEOUT_MS = 15000;
|
||||
|
||||
export default function checkForZenUpdates() {
|
||||
const version = Services.appinfo.version;
|
||||
@@ -25,6 +26,7 @@ export default function checkForZenUpdates() {
|
||||
);
|
||||
createSidebarNotification({
|
||||
headingL10nId: "zen-sidebar-notification-updated-heading",
|
||||
autoHideMs: ZEN_UPDATE_NOTIFICATION_TIMEOUT_MS,
|
||||
links: [
|
||||
{
|
||||
url: Services.urlFormatter.formatURL(
|
||||
|
||||
@@ -77,6 +77,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.zen-sidebar-notification-progress {
|
||||
height: 2px;
|
||||
overflow: hidden;
|
||||
transform: translateY(-1px);
|
||||
|
||||
& .zen-sidebar-notification-progress-bar {
|
||||
height: 100%;
|
||||
background: light-dark(color-mix(in srgb, var(--zen-primary-color) 50%, black), color-mix(in srgb, var(--zen-colors-primary) 15%, #ebebeb));
|
||||
transform-origin: left center;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-sidebar-notification-body {
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
|
||||
@@ -12,7 +12,7 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
|
||||
// Maximum number of cards shown in the stack; extra cards stay hidden until
|
||||
// a slot frees up.
|
||||
const MAX_STACKED_CARDS = 5;
|
||||
const MAX_STACKED_CARDS = 3;
|
||||
// How many background cards visually peek out at the top of the stack.
|
||||
// Deeper cards hide perfectly behind the last peeking one, so the stack
|
||||
// doesn't take more room with every extra card.
|
||||
@@ -412,7 +412,25 @@ class ZenMediaCard {
|
||||
this.#tabTimeout = null;
|
||||
}
|
||||
|
||||
this.element.remove();
|
||||
const { element } = this;
|
||||
if (element.hidden) {
|
||||
element.remove();
|
||||
} else {
|
||||
// Animate the card out instead of popping it away. It leaves the
|
||||
// flex flow immediately (so the remaining cards re-slot right away)
|
||||
// but stays anchored where it currently is while it sinks and fades.
|
||||
const barBottom =
|
||||
this.manager.mediaControlBar.getBoundingClientRect().bottom;
|
||||
element.style.bottom =
|
||||
barBottom - element.getBoundingClientRect().bottom + "px";
|
||||
element.setAttribute("zen-removing", "true");
|
||||
// Resolves once the exit transitions finish or get cancelled, and
|
||||
// immediately if they never start (e.g. reduced motion).
|
||||
Promise.allSettled(
|
||||
element.getAnimations().map(animation => animation.finished)
|
||||
).then(() => element.remove());
|
||||
}
|
||||
|
||||
this.manager.onCardDestroyed(this);
|
||||
}
|
||||
}
|
||||
@@ -452,21 +470,28 @@ class nsZenMediaController {
|
||||
|
||||
window.addEventListener("DOMAudioPlaybackStarted", event => {
|
||||
const browser = event.target;
|
||||
// Only create the card if the media is still playing a moment
|
||||
// later, so short sounds (e.g. notification pings) never produce
|
||||
// one.
|
||||
// The card is created right away (while the controller is fresh)
|
||||
// but only shown if the media is still playing a moment later, so
|
||||
// short sounds (e.g. notification pings) never reveal it.
|
||||
setTimeout(() => {
|
||||
const mediaController = browser.browsingContext?.mediaController;
|
||||
if (!mediaController?.isPlaying) {
|
||||
const card = this.#cardForBrowser(browser);
|
||||
if (!card || card.isSharing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activateMediaControls(mediaController, browser);
|
||||
const card = this.#cardForBrowser(browser);
|
||||
if (card && !card.isSharing) {
|
||||
if (card.controller.isPlaying) {
|
||||
card.refreshVisibility();
|
||||
} else if (card.element.hidden) {
|
||||
// The sound was over before ever being shown (e.g. a
|
||||
// notification ping): drop the card instead of keeping a ghost
|
||||
// around.
|
||||
card.destroy();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.activateMediaControls(
|
||||
browser.browsingContext.mediaController,
|
||||
browser
|
||||
);
|
||||
});
|
||||
|
||||
window.addEventListener("DOMAudioPlaybackStopped", () => {
|
||||
@@ -623,18 +648,31 @@ class nsZenMediaController {
|
||||
card => !card.element.hidden
|
||||
);
|
||||
|
||||
// Re-slot the indices without transitions first: the index transform
|
||||
// only compensates the flex-order shift, so a reindexed card keeps
|
||||
// its visual position through this step.
|
||||
visibleCards.forEach((card, index) => {
|
||||
card.element.toggleAttribute(
|
||||
"stack-overflow",
|
||||
index >= MAX_STACKED_CARDS
|
||||
);
|
||||
card.element.toggleAttribute("stacked-behind", index > 0);
|
||||
card.element.setAttribute("zen-reindexing", "true");
|
||||
card.element.style.setProperty("--zen-media-card-index", index);
|
||||
card.element.style.zIndex = 100 - index;
|
||||
});
|
||||
|
||||
// Flush styles so the re-slotting lands instantly...
|
||||
this.mediaControlBar.getBoundingClientRect();
|
||||
|
||||
// ...then animate only the peek level: demoted cards slide up from
|
||||
// behind the front card instead of dropping in from above.
|
||||
visibleCards.forEach((card, index) => {
|
||||
card.element.removeAttribute("zen-reindexing");
|
||||
card.element.style.setProperty(
|
||||
"--zen-media-card-peek-level",
|
||||
Math.min(index, MAX_PEEK_LEVELS)
|
||||
);
|
||||
card.element.style.zIndex = 100 - index;
|
||||
});
|
||||
|
||||
const stackedCount = Math.min(visibleCards.length, MAX_STACKED_CARDS);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
--button-spacing: 2px;
|
||||
--zen-media-stack-peek: 8px;
|
||||
--zen-media-stack-gap: 6px;
|
||||
--zen-media-stack-easing: cubic-bezier(0.25, 1, 0.5, 1);
|
||||
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
@@ -88,7 +89,7 @@
|
||||
animation: zen-back-and-forth-text 10s infinite ease-in-out;
|
||||
}
|
||||
|
||||
& .zen-media-card {
|
||||
& .zen-media-card:not([zen-removing]) {
|
||||
transform: none;
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -114,10 +115,10 @@
|
||||
padding: 0 6px;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
max-height 0.2s ease,
|
||||
opacity 0.2s ease,
|
||||
transform 0.2s ease,
|
||||
padding 0.2s ease;
|
||||
max-height 0.3s var(--zen-media-stack-easing),
|
||||
opacity 0.3s var(--zen-media-stack-easing),
|
||||
transform 0.3s var(--zen-media-stack-easing),
|
||||
padding 0.3s var(--zen-media-stack-easing);
|
||||
}
|
||||
|
||||
.zen-media-current-time,
|
||||
@@ -149,12 +150,39 @@
|
||||
)
|
||||
)
|
||||
scale(calc(1 - 0.04 * var(--zen-media-card-peek-level, 0)));
|
||||
opacity: calc(1 - 0.4 * var(--zen-media-card-peek-level, 0));
|
||||
opacity: calc(1 - 0.3 * var(--zen-media-card-peek-level, 0));
|
||||
filter: blur(0);
|
||||
transition:
|
||||
transform 0.1s ease-out,
|
||||
opacity 0.2s ease-out,
|
||||
transform 0.35s var(--zen-media-stack-easing),
|
||||
opacity 0.3s var(--zen-media-stack-easing),
|
||||
filter 0.3s var(--zen-media-stack-easing),
|
||||
padding 0.3s ease-out;
|
||||
|
||||
@starting-style {
|
||||
opacity: 0;
|
||||
transform: translateY(0.5rem);
|
||||
filter: blur(4px);
|
||||
}
|
||||
|
||||
&[zen-reindexing] {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&[zen-removing] {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
transform: none;
|
||||
translate: 0 0.5rem;
|
||||
opacity: 0;
|
||||
filter: blur(2px);
|
||||
pointer-events: none;
|
||||
transition:
|
||||
translate 0.3s var(--zen-media-stack-easing),
|
||||
opacity 0.3s var(--zen-media-stack-easing),
|
||||
filter 0.3s var(--zen-media-stack-easing);
|
||||
}
|
||||
|
||||
&[stack-overflow] {
|
||||
display: none;
|
||||
}
|
||||
@@ -249,7 +277,7 @@
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
transition: height 0.2s ease-out;
|
||||
transition: height 0.3s var(--zen-media-stack-easing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ XPCOMUtils.defineLazyServiceGetter(
|
||||
// comfortably above click jitter or clicks in the region get lost.
|
||||
const DRAG_START_THRESHOLD_PX = 4;
|
||||
|
||||
// Un-snapping a maximized or tiled window is more disruptive, so those
|
||||
// need a firmer gesture, mirroring how native titlebars behave.
|
||||
const SNAPPED_DRAG_START_THRESHOLD_PX = 50;
|
||||
|
||||
// Content that drives its own mouse interaction without being
|
||||
// interactive HTML content in the spec sense.
|
||||
const kAppContentTags = new Set(["audio", "canvas", "video"]);
|
||||
@@ -63,6 +67,9 @@ export class ZenWindowDragChild extends JSWindowActorChild {
|
||||
#dragging = false;
|
||||
#startScreenX = 0;
|
||||
#startScreenY = 0;
|
||||
// 0 while waiting for the ZenWindowDrag:IsSnapped answer; no drag can
|
||||
// start until the proper threshold is known.
|
||||
#dragThresholdPx = 0;
|
||||
|
||||
handleEvent(event) {
|
||||
// Never let pages spoof the gesture with synthetic events.
|
||||
@@ -145,8 +152,18 @@ export class ZenWindowDragChild extends JSWindowActorChild {
|
||||
const { screenX, screenY } = this.#screenPoint(event);
|
||||
this.#startScreenX = screenX;
|
||||
this.#startScreenY = screenY;
|
||||
this.#dragThresholdPx = 0;
|
||||
this.#tracking = true;
|
||||
this.#addGestureListeners();
|
||||
this.sendQuery("ZenWindowDrag:IsSnapped")
|
||||
.then(snapped => {
|
||||
if (this.#tracking) {
|
||||
this.#dragThresholdPx = snapped
|
||||
? SNAPPED_DRAG_START_THRESHOLD_PX
|
||||
: DRAG_START_THRESHOLD_PX;
|
||||
}
|
||||
})
|
||||
.catch(() => this.#reset());
|
||||
}
|
||||
|
||||
#onMouseMove(event) {
|
||||
@@ -164,9 +181,12 @@ export class ZenWindowDragChild extends JSWindowActorChild {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (!this.#dragThresholdPx) {
|
||||
return;
|
||||
}
|
||||
const point = this.#screenPoint(event);
|
||||
const threshold =
|
||||
DRAG_START_THRESHOLD_PX * this.contentWindow.devicePixelRatio;
|
||||
this.#dragThresholdPx * this.contentWindow.devicePixelRatio;
|
||||
if (
|
||||
Math.hypot(
|
||||
point.screenX - this.#startScreenX,
|
||||
|
||||
@@ -15,19 +15,42 @@ XPCOMUtils.defineLazyServiceGetter(
|
||||
|
||||
export class ZenWindowDragParent extends JSWindowActorParent {
|
||||
receiveMessage(message) {
|
||||
if (message.name !== "ZenWindowDrag:StartDrag") {
|
||||
return;
|
||||
}
|
||||
const win = this.browsingContext.topChromeWindow;
|
||||
if (!win || win.closed || win.windowState === win.STATE_FULLSCREEN) {
|
||||
return;
|
||||
if (!win || win.closed) {
|
||||
return undefined;
|
||||
}
|
||||
if (Cu.isInAutomation) {
|
||||
// Tests can't exercise a real OS drag session; let them observe the
|
||||
// decision instead.
|
||||
Services.obs.notifyObservers(win, "zen-window-drag-started");
|
||||
return;
|
||||
switch (message.name) {
|
||||
case "ZenWindowDrag:StartDrag": {
|
||||
if (win.windowState === win.STATE_FULLSCREEN) {
|
||||
break;
|
||||
}
|
||||
if (Cu.isInAutomation) {
|
||||
// Tests can't exercise a real OS drag session; let them observe
|
||||
// the decision instead.
|
||||
Services.obs.notifyObservers(win, "zen-window-drag-started");
|
||||
break;
|
||||
}
|
||||
lazy.zenDragAndDropService.beginNativeWindowMove(win);
|
||||
break;
|
||||
}
|
||||
case "ZenWindowDrag:IsSnapped": {
|
||||
return this.#isSnapped(win);
|
||||
}
|
||||
}
|
||||
lazy.zenDragAndDropService.beginNativeWindowMove(win);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the window is maximized or tiled to an edge. The tiled
|
||||
* attribute is kept in sync with the widget by AppWindow, the same way
|
||||
* sizemode is.
|
||||
*
|
||||
* @param {ChromeWindow} win
|
||||
*/
|
||||
#isSnapped(win) {
|
||||
return (
|
||||
win.windowState === win.STATE_MAXIMIZED ||
|
||||
win.document.documentElement.hasAttribute("tiled")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.21.12b",
|
||||
"displayVersion": "1.21.13b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user