仅保持一个firefox窗口 for firefox4 - 20101201更新

(5,635 views)
November 13, 2010

今天意外遭遇打开多个firefox窗口,正好趁机总结一下保持单个窗口的办法。

未完待续。。。

1. about:config
里面有几个设置参数跟新开窗口相关。

  • browser. link. open_newwindow
    对于网页上的超链,有一些通常是应该在新窗口打开的,比如设置了target=”_blank”,target=”new”,rel=”external”等等属性的链接。
    通过这个参数可以定制上述链接在firefox中的打开方式。有3个可选值。

    1 : In the current tab or window 在当前tab或窗口打开
    2 : In a new window 在新窗口打开
    3 : In a new tab 在新tab里打开

    所以这里为了不在新窗口打开,我们应该配置成3或者1,根据自己的习惯选择。
    其实菜单项

    Tools → Options → Tabs → Open new windows in a new tab instead

    就是更改这个值,默认不勾选是2,勾选了是3。

  • browser. link. open_newwindow. restriction
    通过javascript打开的窗口(就是我们一般说的弹出窗口,比如广告那些)通过这个参数来控制。3个可选值。

    0 : 按照browser.link.open_newwindow参数的设置来处理.
    1 : 弹出新窗口
    2 : 如果JavaScript调用window.open()时设置了”features”参数则在新窗口打开,否则按照browser.link.open_newwindow参数的设置来处理

    这个设置成0。

  • browser. link. open_external
    这个参数在3.5时已经被删除,但是不排除以后还会恢复(现在bugzilla里面呼声很高)。
    外部程序可能会通过firefox来加载页面,比如在email客户端里点了邮件中的一个链接。
    这个参数就是用来控制如何打开外部程序传递进来的链接。3个可选值

    1 : Opens external links in current window 在当前窗口打开
    2 : Opens external links in a new window 新窗口打开
    3 : Opens external links in a new tab in the most recent window 在最上层窗口用新tab打开

    当前firefox4版本该参数无效,外部链接也是用browser.link.open_newwindow参数来控制,所以如果你想设置成网页里的链接在当前tab打开,而外部程序的链接在新tab打开,通过about:config已经实现不了了(需要其他扩展或脚本)。但是对于我们这个主题,browser.link.open_newwindow设置好就行。

2. UI上屏蔽在新窗口打开
用下面的CSS隐藏超链上右键菜单中“在新窗口打开”选项

#context-openlink {
 display: none !important;
}

3. 脚本
脚本大体上分为2种。

  1. 第1种是从根本上禁止弹出新窗口 – 把所有打开新窗口的代码都替换成打开新tab或者在当前tab打开,比如shift+click超链就可以修改为在当前tab打开(原本是在新窗口)。这种办法需要hack浏览器的代码,修改也比较多(凡是涉及新开窗口的地方都要改),但是可以从最初始的地方杜绝新开窗口。
  2. 第2种办法是允许新开窗口,当侦测到有新的窗口打开时,再把它合并到先前开的窗口里,最后始终只保持一个窗口开着。这种办法实现起来比较简单,而且各种新开窗口的方式都能覆盖,但是在合并窗口之前会有一个时间段内是有多个窗口存在的。

思路清楚了,先来看看第2种简单的。代码基于quchao的singletonFox重写并简化了

(function() {
        var singletonFox = null;

        function isBlank(browser) {
            return browser.mTabs.length===1 && browser.currentURI.spec==='about:blank' && browser.selectedBrowser.sessionHistory.count<2 && !browser.selectedTab.hasAttribute('busy') && !browser.selectedBrowser.contentDocument.body.hasChildNodes();
	    }

        function isBusyLoading(browser) {
            return browser.currentURI.spec == 'about:blank' && browser.selectedTab.getAttribute('busy') !== '' && !browser.selectedBrowser.contentDocument.body.hasChildNodes();
	    }

        function check() {
            var windowsEnumerator = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator).getEnumerator('navigator:browser');
		    if(windowsEnumerator.hasMoreElements()) {
                // iterate all windows opened
		        while(windowsEnumerator.hasMoreElements()) {
                    var win = windowsEnumerator.getNext();
                    if(!singletonFox) {
                        singletonFox = win;
                    }
			        if(win !== singletonFox) {
			            var browser = win.getBrowser();
			            if(!isBlank(browser)) {
				            (function() {
					            if(isBusyLoading(browser)) {
					                setTimeout(arguments.callee, 0);
					            } else {
                                    // merge session data
					                var sessionService =Cc['@mozilla.org/browser/sessionstore;1'].getService(Ci.nsISessionStore);
					                sessionService.setWindowState(singletonFox, sessionService.getWindowState(win), false);
					                singletonFox.focus();
                                    win.BrowserTryToCloseWindow();
					            }
				            })();
                        } else {
                            win.BrowserTryToCloseWindow();
                        }
			        }
		        }
            }
	   }

       if(location == "chrome://browser/content/browser.xul") {
            check();
       }
})();

这个脚本只在新开窗口时执行一个check方法(43-45行),方法主体是遍历当前的所有已开窗口,并合并到一个窗口里。

02行的singletonFox变量保存唯一打开的窗口。
18-20行,如果singletonFox变量未初始化,则当前正在检查的窗口作为这个唯一的窗口。
21-38行,只要不是singletonFox对应的窗口,则关闭该窗口,把该窗口中的tabs合并到singletonFox窗口里去。
36行,如果这个窗口是一个空白窗口,直接关闭。
27-33行,通过sessionService来合并窗口。
25-27行,如果是一个新开的正在加载的窗口,使用定时器延时合并,这是为了避免由于合并导致加载停止。

isBlank和isBusyLoading是2个工具函数,分别用来判断是不是空白窗口和新开的加载窗口。

附件:singletonFox.uc

—————————————————————————————————–
第二种比较麻烦,首先得找出来哪些地方会打开新窗口,然后把代码修改成不要打开新窗口。

  1. shift+click网页链接
    firefox4里面有个函数 – whereToOpenLink,从名字就能猜出来是用来决定链接在哪打开的。

    function whereToOpenLink(e, ignoreButton, ignoreAlt) {
        if (!e) {
            return "current";
        }
        var shift = e.shiftKey;
        var ctrl = e.ctrlKey;
        var meta = e.metaKey;
        var alt = e.altKey && !ignoreAlt;
        var middle = !ignoreButton && e.button == 1;
        var middleUsesTabs = getBoolPref("browser.tabs.opentabfor.middleclick", true);
        if (ctrl || middle && middleUsesTabs) {
            return shift ? "tabshifted" : "tab";
        }
        if (alt) {
            return "save";
        }
        if (shift || middle && !middleUsesTabs) {
            return "window";
        }
        return "current";
    }
    

    看17-19行就知道,shift+click或者鼠标中建(需要开启browser.tabs.opentabfor.middleclick)会在新窗口打开链接。
    所以,这里我们只需要把return “window”;替换掉就行。

    (function() {
        try {
            eval("whereToOpenLink = " + whereToOpenLink.toString().replace(/return "window";/g, 'return "tab";'));
        }catch(e) {}
    })();
    

    这样就实现了shift+click超链是在新tab而不是新窗口打开链接。
    其实我更喜欢alt+click是当前tab打开,shift+click是save,改起来也很简单,可以自行尝试。


related post

(5,635 views)
①若要贴代码,请将 "<" 改成 "&lt;",">" 改成 "&gt;".
②若要从他人留言中复制代码,注意检查引号可能是中文的,请手动修改成英文符号,避免不能工作