mirror of
https://github.com/zen-browser/desktop.git
synced 2026-02-24 04:06:40 +00:00
feat: Added copy url button and small tweaks, b=no-bug, c=common, compact-mode, folders, glance
This commit is contained in:
@@ -90,6 +90,9 @@ zen-site-data-get-addons =
|
||||
zen-site-data-site-settings =
|
||||
.label = All Site Settings
|
||||
|
||||
zen-urlbar-copy-url-button =
|
||||
.tooltiptext = Copy URL
|
||||
|
||||
zen-site-data-setting-site-protection = Site Protection
|
||||
|
||||
zen-site-data-panel-feature-callout-title = A new home for add-ons, permissions, and more
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarInput.sys.mjs b/browser/components/urlbar/UrlbarInput.sys.mjs
|
||||
index afc7a6c6ddbf4cf5a5b27c0bd60577b833c63093..77955d888e70409c83b217e676e1575417018831 100644
|
||||
index afc7a6c6ddbf4cf5a5b27c0bd60577b833c63093..a2494f2c0f3e4fc50cbe2fe3bf6e2bd2b69f0cf7 100644
|
||||
--- a/browser/components/urlbar/UrlbarInput.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarInput.sys.mjs
|
||||
@@ -76,6 +76,13 @@ ChromeUtils.defineLazyGetter(lazy, "logger", () =>
|
||||
@@ -157,14 +157,14 @@ index afc7a6c6ddbf4cf5a5b27c0bd60577b833c63093..77955d888e70409c83b217e676e15754
|
||||
: val;
|
||||
// Only trim value if the directionality doesn't change to RTL and we're not
|
||||
// showing a strikeout https protocol.
|
||||
@@ -3407,6 +3475,7 @@ export class UrlbarInput {
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3501,6 +3569,7 @@ export class UrlbarInput {
|
||||
resultDetails = null,
|
||||
browser = this.window.gBrowser.selectedBrowser
|
||||
) {
|
||||
+ openUILinkWhere = this.window.gZenUIManager.getOpenUILinkWhere(url, browser, openUILinkWhere);
|
||||
// No point in setting these because we'll handleRevert() a few rows below.
|
||||
if (openUILinkWhere == "current") {
|
||||
// Make sure URL is formatted properly (don't show punycode).
|
||||
if (this.isAddressbar) {
|
||||
this.#prepareAddressbarLoad(
|
||||
url,
|
||||
@@ -3608,6 +3677,10 @@ export class UrlbarInput {
|
||||
}
|
||||
reuseEmpty = true;
|
||||
|
||||
@@ -944,3 +944,7 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-copy-url-button image {
|
||||
list-style-image: url('link.svg');
|
||||
}
|
||||
|
||||
@@ -527,6 +527,9 @@ var gZenUIManager = {
|
||||
this._toastContainer.removeAttribute('hidden');
|
||||
this._toastContainer.appendChild(toast);
|
||||
const timeoutFunction = () => {
|
||||
if (Services.prefs.getBoolPref('ui.popup.disable_autohide')) {
|
||||
return;
|
||||
}
|
||||
this.motion
|
||||
.animate(toast, { opacity: [1, 0], scale: [1, 0.5] }, { duration: 0.2, bounce: 0 })
|
||||
.then(() => {
|
||||
|
||||
@@ -3,20 +3,6 @@
|
||||
* 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/.
|
||||
*/
|
||||
@keyframes zen-jello-animation {
|
||||
0% {
|
||||
transform: scale3d(0.8, 0.8, 0.8);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: scale3d(1.02, 1.02, 1.02);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-jello-animation-macos {
|
||||
0% {
|
||||
@@ -30,21 +16,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-jello-animation-large {
|
||||
0% {
|
||||
transform: scale3d(0.8, 0.8, 0.8);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: scale3d(1.02, 1.02, 1.02);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-theme-picker-dot-animation {
|
||||
from {
|
||||
transform: scale(0.8) translate(-50%, -50%);
|
||||
|
||||
@@ -130,7 +130,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.identity-box-button {
|
||||
.identity-box-button,
|
||||
#zen-copy-url-button {
|
||||
opacity: 0;
|
||||
transition:
|
||||
opacity 0.2s,
|
||||
@@ -246,7 +247,9 @@
|
||||
:root[zen-single-toolbar='true'] {
|
||||
--urlbar-icon-border-radius: 8px !important;
|
||||
|
||||
.urlbar-page-action:not([open]):not([showing]):not(#identity-permission-box),
|
||||
.urlbar-page-action:not([open]):not([showing]):not(
|
||||
:is(#zen-copy-url-button, #identity-permission-box)
|
||||
),
|
||||
#tracking-protection-icon-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -23,12 +23,6 @@ panel[type='arrow'] {
|
||||
animation: zen-jello-animation-macos 0.2s ease-in-out forwards !important;
|
||||
}
|
||||
}
|
||||
@media (-moz-platform: linux) or ((-moz-platform: windows) and (not (-moz-windows-mica-popups))) and (-moz-panel-animations) {
|
||||
/* Mica popups have a weird background while the animation is running */
|
||||
&::part(content) {
|
||||
animation: zen-jello-animation 0.35s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menupopup,
|
||||
|
||||
@@ -386,13 +386,17 @@ menuseparator {
|
||||
min-width: unset !important;
|
||||
margin: 0px !important;
|
||||
border-radius: calc(var(--zen-native-inner-radius) + 2px) !important;
|
||||
background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)) !important;
|
||||
border: 1px solid light-dark(rgba(0, 0, 0, 0.15), rgba(255, 255, 255, 0.15)) !important;
|
||||
background: light-dark(rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05)) !important;
|
||||
border: 1px solid light-dark(rgba(0, 0, 0, 0.01), rgba(255, 255, 255, 0.01)) !important;
|
||||
color: light-dark(rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0.8)) !important;
|
||||
|
||||
:root[zen-right-side='true'] & {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
& .button-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,11 @@ body > #confetti {
|
||||
background-color 0.1s,
|
||||
transform 0.2s;
|
||||
|
||||
&:active:hover {
|
||||
&[open='true'] {
|
||||
transition: background-color 0.1s;
|
||||
}
|
||||
|
||||
&:not([open='true']):active:hover {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
--zen-native-content-radius: env(-moz-gtk-csd-titlebar-radius);
|
||||
}
|
||||
@media (-moz-mac-tahoe-theme) {
|
||||
--zen-native-content-radius: 14px;
|
||||
--zen-native-content-radius: 15px;
|
||||
}
|
||||
--zen-native-inner-radius: var(
|
||||
--zen-webview-border-radius,
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#navigator-toolbox {
|
||||
--zen-toolbox-max-width: 74px !important;
|
||||
--zen-compact-float: var(--zen-element-separation);
|
||||
|
||||
:root[zen-no-padding='true'] & {
|
||||
--zen-compact-float: 10px;
|
||||
--zen-compact-mode-no-padding-radius-fix: 2px;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: var(--zen-colors-secondary);
|
||||
background-color: var(--toolbar-color);
|
||||
}
|
||||
|
||||
.zen-download-arc-animation-icon {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--zen-colors-primary);
|
||||
background-color: var(--toolbar-color);
|
||||
border-radius: var(--zen-native-content-radius);
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
pointer-events: none;
|
||||
|
||||
@@ -335,13 +335,13 @@ zen-folder {
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-left-radius: max(calc(var(--panel-border-radius) - 6px), 4px);
|
||||
border-bottom-right-radius: max(calc(var(--panel-border-radius) - 6px), 4px);
|
||||
border-bottom-left-radius: max(calc(var(--panel-border-radius) - 2px), 4px);
|
||||
border-bottom-right-radius: max(calc(var(--panel-border-radius) - 2px), 4px);
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: max(calc(var(--panel-border-radius) - 6px), 4px);
|
||||
border-top-right-radius: max(calc(var(--panel-border-radius) - 6px), 4px);
|
||||
border-top-left-radius: max(calc(var(--panel-border-radius) - 2px), 4px);
|
||||
border-top-right-radius: max(calc(var(--panel-border-radius) - 2px), 4px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
* @param {Tab} existingTab - Optional existing tab to reuse
|
||||
* @returns {Browser} The created browser element
|
||||
*/
|
||||
createBrowserElement(url, currentTab, existingTab = null) {
|
||||
#createBrowserElement(url, currentTab, existingTab = null) {
|
||||
const newTabOptions = this.#createTabOptions(currentTab);
|
||||
const newUUID = gZenUIManager.generateUuidv4();
|
||||
|
||||
@@ -250,6 +250,28 @@
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element preview data as a data URL
|
||||
* @param {Object} data - Glance data
|
||||
* @returns {Promise<string|null>} Promise resolving to data URL or null
|
||||
* if not available
|
||||
*/
|
||||
async #getElementPreviewData(data) {
|
||||
// Make the rect relative to the tabpanels. We dont do it directly on the
|
||||
// content process since it does not take into account scroll. This way, we can
|
||||
// be sure that the coordinates are correct.
|
||||
const tabPanelsRect = gBrowser.tabpanels.getBoundingClientRect();
|
||||
const rect = new DOMRect(
|
||||
data.clientX + tabPanelsRect.left,
|
||||
data.clientY + tabPanelsRect.top,
|
||||
data.width,
|
||||
data.height
|
||||
);
|
||||
return await this.#imageBitmapToBase64(
|
||||
await window.browsingContext.currentWindowGlobal.drawSnapshot(rect, 1, 'transparent', true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a glance overlay with the specified data
|
||||
* @param {Object} data - Glance data including URL, position, and dimensions
|
||||
@@ -269,7 +291,7 @@
|
||||
|
||||
this.#setAnimationState(true);
|
||||
const currentTab = ownerTab ?? gBrowser.selectedTab;
|
||||
const browserElement = this.createBrowserElement(data.url, currentTab, existingTab);
|
||||
const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab);
|
||||
|
||||
this.fillOverlay(browserElement);
|
||||
this.overlay.classList.add('zen-glance-overlay');
|
||||
@@ -293,9 +315,13 @@
|
||||
* @returns {Promise<Tab>} Promise that resolves to the glance tab
|
||||
*/
|
||||
#animateGlanceOpening(data, browserElement) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(async (resolve) => {
|
||||
this.#prepareGlanceAnimation(data, browserElement);
|
||||
if (data.width && data.height) {
|
||||
data.elementData = await this.#getElementPreviewData(data);
|
||||
}
|
||||
this.#glances.get(this.#currentGlanceID).elementData = data.elementData;
|
||||
window.requestAnimationFrame(() => {
|
||||
this.#prepareGlanceAnimation(data, browserElement);
|
||||
this.#executeGlanceAnimation(data, browserElement, resolve);
|
||||
});
|
||||
});
|
||||
@@ -450,18 +476,24 @@
|
||||
const transformOrigin = this.#getTransformOrigin(data);
|
||||
|
||||
this.browserWrapper.style.transformOrigin = transformOrigin;
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
this.contentWrapper,
|
||||
{ opacity: [0, 1] },
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: 'easeInOut',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
this.contentWrapper.style.opacity = '';
|
||||
});
|
||||
|
||||
// Only animate if there is element data, so we can apply a
|
||||
// nice fade-in effect to the content. But if it doesn't exist,
|
||||
// we just fall back to always showing the browser directly.
|
||||
if (data.elementData) {
|
||||
gZenUIManager.motion
|
||||
.animate(
|
||||
this.contentWrapper,
|
||||
{ opacity: [0, 1] },
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: 'easeInOut',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
this.contentWrapper.style.opacity = '';
|
||||
});
|
||||
}
|
||||
|
||||
gZenUIManager.motion
|
||||
.animate(this.browserWrapper, arcSequence, {
|
||||
@@ -826,6 +858,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
#imageBitmapToBase64(imageBitmap) {
|
||||
// 1. Create a canvas with the same size as the ImageBitmap
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = imageBitmap.width;
|
||||
canvas.height = imageBitmap.height;
|
||||
|
||||
// 2. Draw the ImageBitmap onto the canvas
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(imageBitmap, 0, 0);
|
||||
|
||||
// 3. Convert the canvas content to a Base64 string (PNG by default)
|
||||
const base64String = canvas.toDataURL('image/png');
|
||||
return base64String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate parent background restoration
|
||||
* @param {Element} browserSidebarContainer - The sidebar container
|
||||
@@ -1481,10 +1528,15 @@
|
||||
*/
|
||||
#createGlanceDataFromBookmark(event) {
|
||||
const rect = window.windowUtils.getBoundsWithoutFlushing(event.target);
|
||||
const tabPanelRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabpanels);
|
||||
// the bookmark is most likely outisde the tabpanel, so we need to give a negative number
|
||||
// so it can be corrected later
|
||||
let top = rect.top - tabPanelRect.top;
|
||||
let left = rect.left - tabPanelRect.left;
|
||||
return {
|
||||
url: event.target._placesNode.uri,
|
||||
clientX: rect.left,
|
||||
clientY: rect.top,
|
||||
clientX: left,
|
||||
clientY: top,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
};
|
||||
|
||||
@@ -28,38 +28,7 @@ export class ZenGlanceParent extends JSWindowActorParent {
|
||||
}
|
||||
}
|
||||
|
||||
#imageBitmapToBase64(imageBitmap) {
|
||||
// 1. Create a canvas with the same size as the ImageBitmap
|
||||
const canvas = this.browsingContext.topChromeWindow.document.createElement('canvas');
|
||||
canvas.width = imageBitmap.width;
|
||||
canvas.height = imageBitmap.height;
|
||||
|
||||
// 2. Draw the ImageBitmap onto the canvas
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(imageBitmap, 0, 0);
|
||||
|
||||
// 3. Convert the canvas content to a Base64 string (PNG by default)
|
||||
const base64String = canvas.toDataURL('image/png');
|
||||
return base64String;
|
||||
}
|
||||
|
||||
async openGlance(window, data) {
|
||||
const win = this.browsingContext.topChromeWindow;
|
||||
const tabPanels = win.gBrowser.tabpanels;
|
||||
// Make the rect relative to the tabpanels. We dont do it directly on the
|
||||
// content process since it does not take into account scroll. This way, we can
|
||||
// be sure that the coordinates are correct.
|
||||
const tabPanelsRect = tabPanels.getBoundingClientRect();
|
||||
const rect = new DOMRect(
|
||||
data.clientX + tabPanelsRect.left,
|
||||
data.clientY + tabPanelsRect.top,
|
||||
data.width,
|
||||
data.height
|
||||
);
|
||||
const elementData = await this.#imageBitmapToBase64(
|
||||
await win.browsingContext.currentWindowGlobal.drawSnapshot(rect, 1, 'transparent', true)
|
||||
);
|
||||
data.elementData = elementData;
|
||||
window.gZenGlanceManager.openGlance(data);
|
||||
openGlance(window, data) {
|
||||
return window.gZenGlanceManager.openGlance(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export class nsZenSiteDataPanel {
|
||||
// Remove the old permissions dialog
|
||||
this.document.getElementById('unified-extensions-panel-template').remove();
|
||||
|
||||
this.#initCopyUrlButton();
|
||||
this.#initEventListeners();
|
||||
this.#maybeShowFeatureCallout();
|
||||
}
|
||||
@@ -65,6 +66,35 @@ export class nsZenSiteDataPanel {
|
||||
this.#initContextMenuEventListener();
|
||||
}
|
||||
|
||||
#initCopyUrlButton() {
|
||||
// This function is a bit out of place, but it's related enough to the panel
|
||||
// that it's easier to do it here than in a separate module.
|
||||
const container = this.document.getElementById('page-action-buttons');
|
||||
const fragment = this.window.MozXULElement.parseXULToFragment(`
|
||||
<hbox id="zen-copy-url-button"
|
||||
class="urlbar-page-action"
|
||||
role="button"
|
||||
data-l10n-id="zen-urlbar-copy-url-button"
|
||||
hidden="true">
|
||||
<image class="urlbar-icon"/>
|
||||
</hbox>
|
||||
`);
|
||||
container.appendChild(fragment);
|
||||
|
||||
const aElement = this.document.getElementById('zen-copy-url-button');
|
||||
aElement.addEventListener('click', (event) => {
|
||||
this.document.getElementById('cmd_zenCopyCurrentURL').doCommand();
|
||||
});
|
||||
|
||||
this.window.gBrowser.addProgressListener({
|
||||
onLocationChange: (aWebProgress, aRequest, aLocation) => {
|
||||
if (aWebProgress.isTopLevel) {
|
||||
aElement.hidden = !this.#canCopyUrl(aLocation);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
#initContextMenuEventListener() {
|
||||
const kCommands = {
|
||||
context_zenClearSiteData: (event) => {
|
||||
@@ -117,10 +147,7 @@ export class nsZenSiteDataPanel {
|
||||
}
|
||||
{
|
||||
const button = this.document.getElementById('zen-site-data-header-share');
|
||||
if (
|
||||
this.window.gBrowser.currentURI.schemeIs('http') ||
|
||||
this.window.gBrowser.currentURI.schemeIs('https')
|
||||
) {
|
||||
if (this.#canCopyUrl(this.window.gBrowser.currentURI)) {
|
||||
button.removeAttribute('disabled');
|
||||
} else {
|
||||
button.setAttribute('disabled', 'true');
|
||||
@@ -128,6 +155,19 @@ export class nsZenSiteDataPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the copy URL button should be hidden for the given URI.
|
||||
* @param {nsIURI} uri - The URI to check.
|
||||
* @returns {boolean} True if the button should be hidden, false otherwise.
|
||||
*/
|
||||
#canCopyUrl(uri) {
|
||||
if (!uri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return uri.scheme.startsWith('http');
|
||||
}
|
||||
|
||||
#setSiteSecurityInfo() {
|
||||
const { gIdentityHandler } = this.window;
|
||||
const button = this.document.getElementById('zen-site-data-security-info');
|
||||
|
||||
Reference in New Issue
Block a user