火狐扩展开发_他们在Firefox扩展学校从未告诉过的10件事

火狐扩展开发

这是我最近的电子书《 Build Your Own Firefox Extension》的后续文章-我从构建Firefox扩展的经验中收集了许多有用的技巧,窍门和hack,包括CodeBurner和SitePoint参考扩展。 假定您具有有关如何构建Firefox扩展的基本知识,因此,如果您尚未这样做,则可能想先获取电子书的免费副本。

Mozilla开发人员中心 (MDC)并未明确记录此处的大部分内容,这是因为该内容仍在他们的“待办事项”列表中,或者是我自己做了。 其中有些文件有据可查,但是它是如此有用,以至于我仍然值得将您的注意力引向它。

提示按照复杂性的顺序列出,从最短和最简单的开始,再到更长更复杂的想法。

请注意 :在本文的许多地方,我将创建很少的方法来打包独立技术,并且在所有情况下,我都会将它们创建为顶级函数(使用function关键字)。 但是实际上,您应该将它们创建为主扩展对象的方法。

1.添加带有list-style-image图标

许多XUL元素不支持CSS background-image属性,但是许多元素确实支持list-style-image 。 这包括<menuitem><button><textbox> 。 您可以使用以下命令将应用程序的图标添加到其主菜单项,或将一个小放大镜图标附加到用于搜索的文本框中:

textbox[type="search"]
{
 list-style-image:url(chrome://myextension/content/images/magglass.png);
}
2.在Mac OS X中使<tab>元素键盘可访问
 <tab> elements are natively inaccessible to the keyboard in Firefox for Mac OS X. To make them accessible you need to manually insert them into the tab order, by adding a tabindex attribute with the value 0 . This value is effectively "auto", and places the element at its source-order position in the overall tab order:
<tab label="About" tabindex="0"/>

Once that's done you can use the arrow keys to switch between tabs, just the same as in Windows and Linux.

A keyboard-focused tab in Mac OS X

3. Reference the Original Mouse-target of a Context Menu Event

When you click on an item in an XUL context menu, the event target reference is to the <menuitem> you clicked on. But what if you wanted a reference to the original target element; that is, the element you right-clicked on to spawn the menu in the first place?
This is incredibly simple, as Firefox provides a property that contains this very reference. It's called popupNode and is a property of the document . The easiest way to use it is to pass it through the menu item's command event:

<popup id="contentAreaContextMenu">
 <menuitem label="This bloke won't haggle"  
           oncommand="offerMeFourteen(document.popupNode)"
 />
</popup>
4. Preventing an Element from Inheriting flex

If you add the flex attribute to most XUL elements, they'll expand to fill the available space. But flex is inherited, so its children will also expand, which in some cases is highly undesirable. For example, if the child element is an <image> you would want it to have precise dimensions; but there is no way to explicitly negate inherited flex .

But it only inherits one level deep, so you can negate it by adding an intermediate wrapper element, without a declared flex attribute:

<hbox flex="1">

 <hbox>
   <image  
      src="chrome://myextension/content/images/logo.png"  
      width="135"  
      height="130"
    />
 </hbox>

</hbox>
5. Spawn a Dialog from the Chrome load Event

If you use window.openDialog to spawn a dialog with the modal and centerscreen features from the chrome load event, the dialog will be mostly invisible in Mac OS X, hidden in the top-left corner of the screen. This is because the dialog is positioned before the window's size is established, so the centerscreen property fails to work as expected. The same problem occurs with the alert function, which can be an issue if you're using it as a quick and dirty debugging tool.

One solution is to wrap the openDialog or alert function in a quick setTimeout . This ensures that the main window is sized before the dialog fires, so it will be positioned correctly:

setTimeout(function(){ alert(foo.bar); },1);
6. Add Custom Dialog Icons for Windows and Linux

To add a custom icon to a dialog, first create a folder named icons inside your extension's chrome directory. Then, inside the icons folder, create another folder called default . Within the default folder, save an icon with the same name as the <dialog> element's ID.

So, for example, if the dialog had the ID myextension-preferences you would create an icon called myextension-preferences.ico (for Windows, or .png for Linux). The MDC documentation says to use XPM images for Linux, but they lack support for alpha-channel transparency. PNG files do provide support, and they work just as well.

In Windows the icon will also show up in the taskbar:

A custom dialog icon in Windows XP

This differs to Mac OS X, because its dialogs are displayed without icons.

7. Get a Reference to the Most Recently Opened Window

You can use Firefox's window mediator interface to get a reference to the most recently opened browser window. This might be useful if you wanted to open a web link from an external dialog, and is more reliable than window.opener .

Here's a short and sweet little method that returns the window reference, or null if no browser windows are open:

function getRecentWindow()
{
 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
 var win = wm.getMostRecentWindow("navigator:browser");

 return win;
}

Learn PHP for free!

Make the leap into server-side programming with a comprehensive cover of PHP & MySQL.

Normally RRP $11.95 Yours absolutely free

8. Get the URL of every Open Tab

Following on from the previous tip, we can iterate through all currently open browser windows, extract their URLs, and package them into a hierarchical array (grouped first by window, then by tab).

The following method does precisely that. Each member of the final matrix is itself an array, containing the tab's URL and a Boolean flag ( selected ) to indicate if it's the currently selected tab in that window:

function getTabsHeirarchy()
{  
 var heirarchy = [],  
     wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator),  
     benumerator = wm.getEnumerator('navigator:browser');  
 
 while(benumerator.hasMoreElements())  
 {  
   var browserwin = benumerator.getNext(),  
       tabbrowser = browserwin.getBrowser(),  
       tabs = [];  
 
   for(var i=0; i<tabbrowser.browsers.length; i++)  
   {  
     var browser = tabbrowser.getBrowserAtIndex(i);  
     tabs[i] = {  
       'uri' : browser.currentURI.spec,  
       'selected' : (tabbrowser.selectedTab == tabbrowser.mTabs[i])  
     };  
   }  
 
   heirarchy.push(tabs);  
 }  
 
 return heirarchy;  
}
9. Make Your Interface Respond to Window Focus Changes

Most Mac windows change their appearance when the window loses focus: lighter backgrounds, window decorations, or grayed-out buttons, for example. To implement this effect on your own interface controls you need to know when the window gains and loses focus.

Your first instinct might be to use the window focus and blur events, but it turns out they're unreliable for this purpose, since they sometimes behave unintuitively. For example, if the application focus moves to a document in an embedded <browser> , the main window blur event will fire, even though the window is still the focused one. This is because the <window> element itself no longer has the application focus. Although this behavior is logical, it may also be unexpected. The good news is that Firefox's main interface window has an active attribute that changes from true to "" (an empty string) when the window genuinely loses focus. You can watch this attribute with a DOM mutation event listener, and use it as the trigger for whatever you need to do:

window.addEventListener('DOMAttrModified', function(e)
{  
 if(e.attrName == 'active')  
 {  
   if(e.newValue == 'true')  
   {  
     //window has gained the focus  
   }  
   else  
   {  
     //window has lost the focus  
   }  
 }  
}, false);

Be careful how you use this. For example, if you used it to trigger a modal dialog such as alert , the action of spawning the dialog would cause the window to lose focus; dismissing it would regain the focus, which would re-trigger the dialog!

Alternatively, if the effect you want to achieve can be achieved with pure CSS, you can use an attribute selector along with the negation pseudo-class . For instance, to switch an icon between its normal and disabled states:

window[active="true"] #main-icon
{  
 list-style-image:url(chrome://myextension/content/images/main-icon.png);  
}  
 
window:not([active="true"]) #main-icon  
{  
 list-style-image:url(chrome://myextension/content/images/main-icon-disabled.png);  
}
10. Implement Platform-specific Style Sheets

Firefox is available on multiple platforms, and each of these has its own conventions with regard to the appearance and even the placement of interface components. A good example of this is the OK and Cancel buttons in an alert dialog: on Windows and Linux the OK button is to the left of the Cancel button, while on Mac OS it's the other way round. The appearance of the close and minimize window buttons is another instance, as they differ for each platform.

So given these variations, it's often useful to be able to apply platform-specific style sheets to your own extension's interface. This enables you to implement variations like alternate icons for buttons, different fonts for custom interface controls, and so on.

Fortunately, Firefox provides a simple mechanism for doing this, using a special folder hierarchy and a set of manifest files.

First of all you need to create the folder hierarchy. The top-level platform folder in the following diagram should go inside your extension's root folder - at the same level as the chrome folder. All the folder names and filenames must be exactly as shown here (they are also case-sensitive), except for the name of the style sheet itself; this can be anything you like, but of course it must be the same for each platform's copy.

The folder hierarchy for platform-specific style sheets

Just in case it was less than obvious, "Darwin" is Mac OS X, "WINNT" is Windows, and "Linux" is ... er ... Linux. Each of those chrome.manifest files should contain this identical tab-delimited line (replacing "myextension" with the name of your extension):

skin  myextension  classic/1.0  chrome/skin/classic/

To add the style sheets to your interface, simply add an xml-stylesheet processing instruction with the following URL pattern:

<?xml-stylesheet href="chrome://myextension/skin/browser.css"?>

See how all you need to do is refer to the skin directory, and Firefox will work out which specific style sheet to include, according to the platform it's running on. You can extend the principle with as many different style sheets as you want: just create a version in each of the platform folders, and then add it to an XUL document using the same URL pattern.

11. Add a URL to the Browser's History

Here's an extra bonus tip. The XUL Reference at MDC tells you how to create a textbox with history auto-complete . Unfortunately, it fails to tell you how to add new URLs to the history, so I had to work this out the hard way, by trawling through Firefox's source code. The method I'm going to show you here adds URLs, retrieves and saves favicons, and includes the basic history auto-complete into the bargain!

A textbox with a history auto-complete menu, showing entries we added programmatically

Note : adding to the browser's history will work in Firefox 3 or later, but retrieving the favicon will only work in version 3.5 or later.

So, to begin with we need a <textbox> with the necessary attributes. In the following code example, the function name addURLToHistory can be anything you want, and the flex attribute is optional, but everything else must be exactly as shown:

<textbox flex="1"
        newlines="stripsurroundingwhitespace"  
        type="autocomplete"  
        autocompletesearch="history"  
        completeselectedindex="true"  
        onkeydown="if(event.keyCode == KeyEvent.DOM_VK_RETURN) { addURLToHistory(this); }"  
      />

The type and autocompletesearch attributes are what trigger the primary auto-complete behavior. The completeselectedindex attribute is so that when you select an item from the auto-complete menu, its value is automatically written into the textbox; this allows you to press the Enter key straight away to fire the command function. The newlines attribute is simply so that we can avoid manually parsing the value of unwanted whitespace (such as leading or trailing spaces).

Note how the command function is triggered by onkeydown , rather than oncommand . That's because the <textbox> element lacks an oncommand event. The events that are normally used for it are oninput (fired when displayable text is entered) and onchange (fired when the value changes). Because the value will be frequently changing in response to auto-complete suggestions, most of which will be unwanted values, we're deferring the command action until the Enter key is pressed.

What we have here is already enough for a functional auto-completing history box. You can type or paste text into the textbox and a drop-down menu will appear with your history, filtered according to what's been entered. You can then select from that menu, and your selection will be written into the textbox.

You could also add an arrow button to make the drop-down menu appear by adding enablehistory="true" .

So now let's look at the command function that's fired when you press Enter. In practice you would go on to perform another task after this, (such as loading the specified URL into a <browser> ) but I'm just going to focus on how to add it to the history. I'll show you the code first, and then go through it bit by bit:

function addURLToHistory(textbox)
{  
 var url = textbox.value;  
 
 if(!/^(((ht|f)tp[s]?):)/i.test(url))  
 {  
   url = 'http://' + url;  
 }  
 textbox.value = url;  
 
 if(url.indexOf(' ') != -1  
    || url.split('?')[0].indexOf('..') != -1)  
 {  
   alert('Malformed URL');  
   return;  
 }  
 
 var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);    
 var nsIURI = ioService.newURI(url, null, null);    
 
 var historyService2 = Components.classes["@mozilla.org/browser/nav-history-service;1"].getService(Components.interfaces.nsIGlobalHistory2);  
 historyService2.addURI(nsIURI, false, true, null);  
 
 try  
 {  
   var faviconService = Components.classes["@mozilla.org/browser/favicon-service;1"].getService(Components.interfaces.nsIFaviconService);  
   var faviconURI = ioService.newURI('http://' + nsIURI.host + '/favicon.ico', null, null);  
   faviconService.setAndLoadFaviconForPage(nsIURI, faviconURI, false);  
 }  
 catch(err) {}  
}

First and foremost we do a little validation, adding a protocol if the URL is without one (so that the user can just type "www."), then writing the [modified] URL back to the textbox. Then, if it contains any spaces or multiple dots other than in CGI parameters, we throw an alert over the malformed syntax and exit the function. This is all the validation we really need to stop Firefox from choking. You may prefer to handle the error more gracefully, for example by throwing the error to the console or implementing a custom method to alert the user that an error has occurred.

Next, we do the business that actually adds the URL to the history. The history service won't accept a plain URI string, so we need to create what's called an IURI. This is a URI object that contains a variety of metadata, including its host, which will come in handy later. We create the IURI object using the IO service , and then pass that to the global history service , to add it to the browser's history.

The rest of the code is for grabbing the favicon, and this is wrapped in a try ... catch block for two reasons. Firstly, so that an error is not thrown if, for any reason, the favicon fails to be at the expected URL, and secondly, because it only works in Firefox 3.5 or later. So, we first initialize the favicon service , and then create an IURI object for the favicon's address (using the host name from the original IURI). We then pass the favicon IURI object to the favicon service, to load and save the favicon.

And there we have it! Next time we type that same address into the textbox, it will show up in the auto-complete menu, along with its favicon.

Note that the favicon process is asynchronous. If you want to display it in the textbox straight away, you need to run a setInterval loop to continually check whether it exists yet. You can do that with code like this:

var count = 0, faviconclock = window.setInterval(function()
{  
 var fsURI = faviconService.getFaviconImageForPage(nsIURI);  
 if(++count == 20 || /moz-anno:favicon:/.test(fsURI.spec))  
 {  
   window.clearInterval(faviconclock);  
   textbox.setAttribute('style', 'list-style-image:url(' + fsURI.spec + ')');  
 }  
}, 500);

This code is a little tricky: every 500 milliseconds (the second parameter to setInterval ), we ask the favicon service for the page's favicon. It will return a URI formatted either with the moz-anno:favicon: protocol (if the favicon has been downloaded) or with the chrome: protocol (if it's returning the default image). If we've tried 20 times (a total of 10 seconds), or if we've successfully downloaded a favicon for the page - as indicated by moz-anno:favicon: in the URI - then we set it as the list-style-image url for the textbox.

We'll that's all for the quick tips list. If you haven't done so already, download my ebook Build Your Own Firefox Extension which comes free with the CodeBurner extension.

Keep a watchful eye out for another article all about building Firefox extensions, as well as some new additions to the CodeBurner family, coming soon!

翻译自: https://www.sitepoint.com/ten-tips-firefox-extensions/

火狐扩展开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值