【问题标题】:Detect origin tab, if any, that opened new tab/window检测打开新选项卡/窗口的源选项卡(如果有)
【发布时间】:2015-02-16 20:57:53
【问题描述】:

对于我创建的即将发布的插件版本,我想检测从哪个选项卡(如果有)在新选项卡或窗口中打开了链接。

我的插件是一个缩放插件,如果用户在新选项卡或窗口中打开链接,我希望能够复制原始选项卡的缩放级别。

我已经想出了如何在 content-JavaScript 中使用window.open() 打开链接时如何检测打开器(这相当容易),但是我不知道如何确定是否有新的选项卡/窗口从上下文菜单(在新选项卡/新窗口中打开链接)或 Ctrl//Shift+单击打开。

为了检测它是否是从 content-Javascript 打开的,我使用:

const Cc = Components.classes;
const Ci = Components.interfaces;

let windowTracker = {
  observe: function( subject, topic, data ) {
    if( 'chrome-document-global-created' == topic && subject instanceof Ci.nsIDOMWindow ) {
      // here I can use subject.opener
    }
  }
}

let observerService = Cc[ '@mozilla.org/observer-service;1' ].getService( Ci.nsIObserverService );
observerService.addObserver( this, 'chrome-document-global-created', false );

为了检测任何可能的非内容 JavaScript 开启器,我在 observe() 中尝试过这个:

let windowTracker = {
  observe: function( subject, topic, data ) {
    if( 'chrome-document-global-created' == topic && subject instanceof Ci.nsIDOMWindow ) {
      let topWindow = subject.QueryInterface( Ci.nsIInterfaceRequestor )
                             .getInterface( Ci.nsIWebNavigation )
                             .QueryInterface( Ci.nsIDocShellTreeItem )
                             .rootTreeItem
                             .QueryInterface( Ci.nsIInterfaceRequestor )
                             .getInterface( Ci.nsIDOMWindow );
      // here I tried topWindow.opener, with mixed results
    }
  }
}

...但结果好坏参半。

当我使用上下文菜单或 Ctrl//Shift+单击在新选项卡或窗口中打开链接时,@987654327 @ 等于 null,但如果我用 Ctrl+Shift+A 打开插件管理器,topWindow.opener将是ChromeWindow 的一个实例。

所以,我似乎走在了正确的轨道上,但并不完全正确,因为我只对用户在新标签页或窗口中打开内容链接感兴趣。

我所追求的可行吗?

在检查 Firefox 的源代码时,我注意到可能有一种方法(除非我误解了它的目的)可以满足我的需求:getOpener() in nsIDocShell。但是,该方法并未在脚本 API 中公开。

有什么类似的东西我可以利用吗?

【问题讨论】:

    标签: javascript firefox firefox-addon xul


    【解决方案1】:

    我已经设法通过使用Proxy 对象拦截ChromeWindow.openLinkIn() 来使其工作:

    // window is a top level ChromeWindow
    window.openLinkIn = new Proxy( window.openLinkIn, {
      apply: function( target, thisArg, argumentsList ) {
    
        let url    = argumentsList[ 0 ];
        let where  = argumentsList[ 1 ];
        let params = argumentsList.length > 2 ? argumentsList[ 2 ] : {};
    
        // links opened through the context menu or Ctrl/⌘/Shift+click
        // don't have params.fromChrome = true
        if( !url || !where || params.fromChrome ) {
          return target.apply( thisArg, argumentsList );
        }
    
        switch( where ) {
          case 'tab':
            // break intentionally omitted
          case 'tabshifted':
            let tabBrowser = window.gBrowser;
            tabBrowser.tabContainer.addEventListener( 'TabOpen', function onTabOpen( event ) {
              tabBrowser.tabContainer.removeEventListener( 'TabOpen', onTabOpen, true );
    
              let tab = event.target;
              // do something with the new tab
            }, true );
    
            break;
          case 'window':
            let windowTracker = {
              init: function() {
                observerService.addObserver( this, 'toplevel-window-ready', false );
              },
              destroy: function() {
                observerService.removeObserver( this, 'toplevel-window-ready' );
              },
              observe: function( subject, topic, data ) {
                let self = this;
                let chromeWindow = subject;
                if( 'toplevel-window-ready' == topic && chromeWindow instanceof Ci.nsIDOMWindow ) {
                  chromeWindow.addEventListener( 'DOMContentLoaded', function onChromeWindowDOMContentLoaded( event ) {
                    this.removeEventListener( 'DOMContentLoaded', onChromeWindowDOMContentLoaded, true );
    
                    if( 'navigator:browser' == this.document.documentElement.getAttribute( 'windowtype' ) ) {
    
                      // do something with the new chromeWindow
    
                      // destroy the windowTracker
                      self.destroy();
                    }
                  }, true );
    
                }
              }
            }
            windowTracker.init();
    
            break;
        }
    
        return target.apply( thisArg, argumentsList );
      }
    } );
    

    PS.:请务必清理并重新安装原始功能,当然,当您的扩展程序被禁用时。你可以这样做:

    let originalOpenLinkIn = window.openLinkIn;
    window.openLinkIn = new Proxy( window.openLinkIn, {
      apply: /* implementation from above */
    } );
    
    // then when cleaning up:
    window.openLinkIn = originalOpenLinkIn;
    

    【讨论】:

      【解决方案2】:

      但是我不知道如何确定是从上下文菜单(在新选项卡/新窗口中打开链接)还是 Ctrl/⌘/Shift+单击打开了新选项卡/窗口。

      在这种情况下,尤其是在新窗口中打开时,标签实际上并没有所有者。它们被认为等同于用户只需在地址栏中输入 URL。

      您可以做的是在每个window 上安装一个system event listener 用于冒泡clickcontextmenusubmit 事件,并检查目标是表单还是链接,然后检查它们的目标网址。如果新打开的选项卡的位置与上次单击/提交的链接匹配,则它可能来自上一个选项卡。

      【讨论】:

      • 嘿,谢谢。这似乎确实是一个计划。然而,与此同时,我想出了一些可能更可靠的东西,如果有点复杂(它涉及用Proxy 对象拦截ChromeWindow.openLinkIn())。稍后我会将其作为答案或问题编辑发布。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-02
      • 2018-10-31
      • 2012-02-18
      • 2015-04-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多