mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			259 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
 | 
						|
 | 
						|
// eslint-disable-next-line import/no-unassigned-import
 | 
						|
import "chrome://browser/content/firefoxview/card-container.mjs";
 | 
						|
// eslint-disable-next-line import/no-unassigned-import
 | 
						|
import "chrome://browser/content/firefoxview/fxview-empty-state.mjs";
 | 
						|
// eslint-disable-next-line import/no-unassigned-import
 | 
						|
import "chrome://browser/content/firefoxview/fxview-search-textbox.mjs";
 | 
						|
// eslint-disable-next-line import/no-unassigned-import
 | 
						|
import "chrome://browser/content/firefoxview/fxview-tab-list.mjs";
 | 
						|
 | 
						|
import { placeLinkOnClipboard } from "./helpers.mjs";
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
const WIN_RESIZE_DEBOUNCE_RATE_MS = 500;
 | 
						|
const WIN_RESIZE_DEBOUNCE_TIMEOUT_MS = 1000;
 | 
						|
 | 
						|
/**
 | 
						|
 * A base class for content container views displayed on firefox-view.
 | 
						|
 *
 | 
						|
 * @property {boolean} recentBrowsing
 | 
						|
 *   Is part of the recentbrowsing page view
 | 
						|
 * @property {boolean} paused
 | 
						|
 *   No content will be updated and rendered while paused
 | 
						|
 */
 | 
						|
export class ViewPageContent extends MozLitElement {
 | 
						|
  static get properties() {
 | 
						|
    return {
 | 
						|
      recentBrowsing: { type: Boolean },
 | 
						|
      paused: { type: Boolean },
 | 
						|
    };
 | 
						|
  }
 | 
						|
  constructor() {
 | 
						|
    super();
 | 
						|
    // don't update or render until explicitly un-paused
 | 
						|
    this.paused = true;
 | 
						|
  }
 | 
						|
 | 
						|
  get ownerViewPage() {
 | 
						|
    return this.closest("[type='page']") || this;
 | 
						|
  }
 | 
						|
 | 
						|
  get isVisible() {
 | 
						|
    if (!this.isConnected || this.ownerDocument.visibilityState != "visible") {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    return this.ownerViewPage.selectedTab;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Override this function to run a callback whenever this content is visible.
 | 
						|
   */
 | 
						|
  viewVisibleCallback() {}
 | 
						|
 | 
						|
  /**
 | 
						|
   * Override this function to run a callback whenever this content is hidden.
 | 
						|
   */
 | 
						|
  viewHiddenCallback() {}
 | 
						|
 | 
						|
  getWindow() {
 | 
						|
    return window.browsingContext.embedderWindowGlobal.browsingContext.window;
 | 
						|
  }
 | 
						|
 | 
						|
  get isSelectedBrowserTab() {
 | 
						|
    const { gBrowser } = this.getWindow();
 | 
						|
    return gBrowser.selectedBrowser.browsingContext == window.browsingContext;
 | 
						|
  }
 | 
						|
 | 
						|
  copyLink(e) {
 | 
						|
    placeLinkOnClipboard(this.triggerNode.title, this.triggerNode.url);
 | 
						|
    this.recordContextMenuTelemetry("copy-link", e);
 | 
						|
  }
 | 
						|
 | 
						|
  openInNewWindow(e) {
 | 
						|
    this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
 | 
						|
      private: false,
 | 
						|
    });
 | 
						|
    this.recordContextMenuTelemetry("open-in-new-window", e);
 | 
						|
  }
 | 
						|
 | 
						|
  openInNewPrivateWindow(e) {
 | 
						|
    this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
 | 
						|
      private: true,
 | 
						|
    });
 | 
						|
    this.recordContextMenuTelemetry("open-in-private-window", e);
 | 
						|
  }
 | 
						|
 | 
						|
  recordContextMenuTelemetry(menuAction, event) {
 | 
						|
    Services.telemetry.recordEvent(
 | 
						|
      "firefoxview_next",
 | 
						|
      "context_menu",
 | 
						|
      "tabs",
 | 
						|
      null,
 | 
						|
      {
 | 
						|
        menu_action: menuAction,
 | 
						|
        data_type: event.target.panel.dataset.tabType,
 | 
						|
      }
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  shouldUpdate(changedProperties) {
 | 
						|
    return !this.paused && super.shouldUpdate(changedProperties);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * A "page" in firefox view, which may be hidden or shown by the named-deck container or
 | 
						|
 * via the owner document's visibility
 | 
						|
 *
 | 
						|
 * @property {boolean} selectedTab
 | 
						|
 *   Is this page the selected view in the named-deck container
 | 
						|
 */
 | 
						|
export class ViewPage extends ViewPageContent {
 | 
						|
  static get properties() {
 | 
						|
    return {
 | 
						|
      selectedTab: { type: Boolean },
 | 
						|
      searchTextboxSize: { type: Number },
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  constructor() {
 | 
						|
    super();
 | 
						|
    this.selectedTab = false;
 | 
						|
    this.recentBrowsing = Boolean(this.recentBrowsingElement);
 | 
						|
    this.onTabSelect = this.onTabSelect.bind(this);
 | 
						|
    this.onResize = this.onResize.bind(this);
 | 
						|
  }
 | 
						|
 | 
						|
  get recentBrowsingElement() {
 | 
						|
    return this.closest("VIEW-RECENTBROWSING");
 | 
						|
  }
 | 
						|
 | 
						|
  onResize() {
 | 
						|
    this.windowResizeTask = new lazy.DeferredTask(
 | 
						|
      () => this.updateAllVirtualLists(),
 | 
						|
      WIN_RESIZE_DEBOUNCE_RATE_MS,
 | 
						|
      WIN_RESIZE_DEBOUNCE_TIMEOUT_MS
 | 
						|
    );
 | 
						|
    this.windowResizeTask?.arm();
 | 
						|
  }
 | 
						|
 | 
						|
  onTabSelect({ target }) {
 | 
						|
    const win = target.ownerGlobal;
 | 
						|
 | 
						|
    let selfBrowser = window.docShell?.chromeEventHandler;
 | 
						|
    const { gBrowser } = this.getWindow();
 | 
						|
    let isForegroundTab = gBrowser.selectedBrowser == selfBrowser;
 | 
						|
 | 
						|
    if (win.FirefoxViewHandler.tab?.selected && isForegroundTab) {
 | 
						|
      this.paused = false;
 | 
						|
      this.viewVisibleCallback();
 | 
						|
    } else {
 | 
						|
      this.paused = true;
 | 
						|
      this.viewHiddenCallback();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  connectedCallback() {
 | 
						|
    super.connectedCallback();
 | 
						|
  }
 | 
						|
 | 
						|
  disconnectedCallback() {
 | 
						|
    super.disconnectedCallback();
 | 
						|
    this.getWindow().removeEventListener("resize", this.onResize);
 | 
						|
    this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
 | 
						|
  }
 | 
						|
 | 
						|
  updateAllVirtualLists() {
 | 
						|
    if (!this.paused) {
 | 
						|
      let tabLists = [];
 | 
						|
      if (this.recentBrowsing) {
 | 
						|
        let viewComponents = this.querySelectorAll("[slot]");
 | 
						|
        viewComponents.forEach(viewComponent => {
 | 
						|
          let currentTabLists = [];
 | 
						|
          if (viewComponent.nodeName.includes("OPENTABS")) {
 | 
						|
            viewComponent.viewCards.forEach(viewCard => {
 | 
						|
              currentTabLists.push(viewCard.tabList);
 | 
						|
            });
 | 
						|
          } else {
 | 
						|
            currentTabLists =
 | 
						|
              viewComponent.shadowRoot.querySelectorAll("fxview-tab-list");
 | 
						|
          }
 | 
						|
          tabLists.push(...currentTabLists);
 | 
						|
        });
 | 
						|
      } else {
 | 
						|
        tabLists = this.shadowRoot.querySelectorAll("fxview-tab-list");
 | 
						|
      }
 | 
						|
      tabLists.forEach(tabList => {
 | 
						|
        if (!tabList.updatesPaused && tabList.rootVirtualListEl?.isVisible) {
 | 
						|
          tabList.rootVirtualListEl.recalculateAfterWindowResize();
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  toggleVisibilityInCardContainer(isOpenTabs) {
 | 
						|
    let cards = [];
 | 
						|
    let tabLists = [];
 | 
						|
    if (!isOpenTabs) {
 | 
						|
      cards = this.shadowRoot.querySelectorAll("card-container");
 | 
						|
      tabLists = this.shadowRoot.querySelectorAll("fxview-tab-list");
 | 
						|
    } else {
 | 
						|
      this.viewCards.forEach(viewCard => {
 | 
						|
        if (viewCard.cardEl) {
 | 
						|
          cards.push(viewCard.cardEl);
 | 
						|
          tabLists.push(viewCard.tabList);
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
    if (tabLists.length && cards.length) {
 | 
						|
      cards.forEach(cardEl => {
 | 
						|
        if (cardEl.visible !== !this.paused) {
 | 
						|
          cardEl.visible = !this.paused;
 | 
						|
        } else if (
 | 
						|
          cardEl.isExpanded &&
 | 
						|
          Array.from(tabLists).some(
 | 
						|
            tabList => tabList.updatesPaused !== this.paused
 | 
						|
          )
 | 
						|
        ) {
 | 
						|
          // If card is already visible and expanded but tab-list has updatesPaused,
 | 
						|
          // update the tab-list updatesPaused prop from here instead of card-container
 | 
						|
          tabLists.forEach(tabList => {
 | 
						|
            tabList.updatesPaused = this.paused;
 | 
						|
          });
 | 
						|
        }
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  enter() {
 | 
						|
    this.selectedTab = true;
 | 
						|
    if (this.isVisible) {
 | 
						|
      this.paused = false;
 | 
						|
      this.viewVisibleCallback();
 | 
						|
      this.getWindow().addEventListener("resize", this.onResize);
 | 
						|
      this.getWindow().addEventListener("TabSelect", this.onTabSelect);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  exit() {
 | 
						|
    this.selectedTab = false;
 | 
						|
    this.paused = true;
 | 
						|
    this.viewHiddenCallback();
 | 
						|
    if (!this.windowResizeTask?.isFinalized) {
 | 
						|
      this.windowResizeTask?.finalize();
 | 
						|
    }
 | 
						|
    this.getWindow().removeEventListener("resize", this.onResize);
 | 
						|
    this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
 | 
						|
  }
 | 
						|
}
 |