Changes

Jump to: navigation, search
m
addtabbeside/
[[Dive into Mozilla]] > [[Dive into Mozilla Day 5]] > Modifying Firefox using an Extension Lab
 
 
'''...in progress...'''
 
=Introduction=
In the [[Dive into Mozilla Modifying Firefox Lab|previous lab]] we made a small change to the behaviour of Firefox by modifying the browser's source code. In this lab we explore how to achieve the same effect using an '''extension''' rather than modifying the tree. ''(Thanks to [http://www.starkravingfinkle.org/blog/ Mark Finkle] for the idea of redoing this as an extension.)''
The goal of this exercise is to expose you to Firefox extensions and to show you how to modify or extend the browser without changing it's its source code directly. Some thought will also be given to the two methods of doing this (i.e., in the tree vs. as an extension), comparing their advantages and disadvantages.
=The 'What': write a tab creation extension=
What does this leave? You could [http://en.wikipedia.org/wiki/Fork_(software_development) fork] Firefox, as some people have done, and create your own version of the browser. Obviously this isn't what we'd like to do. Rather, what we need is a mechanism to insert a small change into the browser, and do so in such a way that users can choose to install our code or not. Mozilla provides such a mechanism in the form of Extensions.
Extensions allow third-party developers (and Mozilla, for that matter) to write add-on packages that users can install to extend or modify the standard browser. By rewriting our earlier code as an extension, we can give our users an extension to installa small add-on, which will have the same effect as our custom build. For all but the most universal of changes, extensions are the best way for developers to write code that targets the browser.
==Planning the extension==
return t;
After the new tab is created and appended to the list, an '''event''' is '''[http://developer.mozilla.org/en/docs/DOM:element.dispatchEvent dispatched'''] to the new tab element. The developers of tabbrowser.xml wanted their code to be extensible; that is, they wanted to provide a way for future developers to extend the code by inserting a hook. In this case, after a tab is created an opportunity is provided to do ''something''. Their foresight makes our job much easier.
===How to move a tab?===
Now we know that there is a logical time/place to run our code via the '''TabOpen''' event. The next thing to consider is what our code will do. Previously we replaced '''append''' with '''insertBefore''' and stopped the problem before it happened. In However, in this case, by the time the '''TabOpen''' event is dispatched, the tab will already be created and positioned at the end of the list.
We need another solution, so we need . It's time to go hunting in tabbrowser's [http://developer.mozilla.org/en/docs/XUL:tabbrowser documentation] and [http://lxr.mozilla.org/seamonkey/source/toolkit/content/widgets/tabbrowser.xml code]. What we need is a way to reposition a tab after it has been created. A quick look through the docs for [http://developer.mozilla.org/en/docs/XUL:tabbrowser tabbrowser] reveals nothing. However, the [http://lxr.mozilla.org/seamonkey/source/toolkit/content/widgets/tabbrowser.xml code] is more helpful:
<method name="moveTabTo">
===How to get the newly created tab using code?===
Having already studied and worked with the code in '''addTab''', and knowing that new tabs are always appended to the end of the list, we could do thisthe following in order to get the new tab:
// In an extension, gBrowser is a global reference to the tabbrowser element
var container = gBrowser.tabContainer;
var lastIndex = container.childNodes.length - 1;
var newTab = container.childNodes[lastIndex];
However, looking back at the event dispatching code for '''TabOpen''' we see that the event is dispatched ??? to t, where t is the newly created tab:
t.dispatchEvent(evt);
This means that in the event system, we'll be able to listen for and capture this event, and in so doing get at the tab object via the event's '''target'''. Three lines of code become one:
 
var newTab = e.target;
We'll discuss this code in more detail below.
===How to get the index of the currently selected tab?===
We know that in order to move a tab using tabbrowser's '''moveTabTo''' method we need the tab object--which we now have--and the index where it should be placed. Looking through the tabbrowser code again, we see [http://lxr.mozilla.org/seamonkey/source/toolkit/content/widgets/tabbrowser.xml#1453 references] to code like this:
var currentIndex = this.mTabContainer.selectedIndex;
Obviously we can't use '''this''' outside the context of tabbrowser, so in an extension we use '''gBrowser''' instead to get a reference to tabbrowser:
gBrowser.tabContainer.selectedIndex
If we want to get the tab to the right of the current tab, we simply do this:
Start by creating a new extension, either by hand, or using Ted Mielczarek's wonderful wizard:  http://ted.mielczarek.org/code/mozilla/extensionwiz/  Directory structure:  '''addtabbeside/''' content.manifest install.rdf '''content/''' firefoxOverlay.xul overlay.js   Here is the '''content.manifest''' file:  content addtabbeside content/ overlay chrome://browser/content/browser.xul chrome://addtabbeside/content/firefoxOverlay.xul    Here is the '''install.rdf''' file for the extension: <pre> <?xml versionvar positionPlusOne ="1gBrowser.0" encoding="UTF-8"?> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:id>addtabbeside@senecac.on.ca</em:id> <em:name>Add Tab Beside</em:name> <em:version>0tabContainer.selectedIndex + 1</em:version> <em:creator>David Humphrey</em:creator> <em:description>New tabs are created beside the current tab instead of at the end of the tab list.</em:description> <em:targetApplication> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox --> <em:minVersion>2.0</em:minVersion> <em:maxVersion>3.0a3pre</em:maxVersion> <!-- trunk build Feb 27, 2007 --> </Description> </em:targetApplication> </Description> </RDF></pre>  Here is firefoxOverlay.xul:;
<pre>Are we done? <?xml version="1Not quite.0" encoding="UTF-8"?> <overlay id="addtabbeside-overlay" xmlns="http://www This code will work, but the problem we now face is that our code will run ''after'' the tab has been placed at the end of the list, when '''TabOpen''' is dispatched.mozilla.org/keymaster/gatekeeper/there As soon as the new tab is created, it becomes the active tab. That means that '''selectedIndex''' will always be the index of the last tab, and moving the last tab one to the right isnonsense.only.xul"> <script src="overlay.js"/> </overlay></pre>
What we ''really'' need is the position of the tab that we were on when he made the new tab. We even know how to get this information, using '''tabContainer.selectedIndex'''. What we don't know is how to get this information, since it is lost by the time our code runs.
Load Listener code: ===Saving tab position state===
http://developerIt's clear that we need data from the past--the position of the tab we were on before this new tab was created.mozilla We can't go back in time, so we'll have to store this data in a variable for inspection later.org/en/docs/Extension_Frequently_Asked_Questions#Why_doesn.27t_my_script_run_properly.3F
In order to accomplish this, we need another event that tells us when a tab has become the active tab so we can store the position state. A quick search through tabbrowser for other events (e.g., search on '''"dispatchEvent"''') shows these possibilities:
* TabSelect
* TabOpen
* TabClose
* NewTab
* TabMove
Reading through The first event looks interesting. All we need to do now is have a variable that gets updated with the rest value of the '''moveTabtabContainer.selectedIndex''' every time '''TabSelect''' methodoccurs. Then, we see that when a tab is successfully created, can use this variable's value to calculate the desired position in '''TabOpenmoveTabTo''' event is dispatched .
(see Finally, we've got all the pieces in place and can write some code...)
// Dispatch a new tab notification. We do this once we=The 're // entirely done, so that things are in a consistent state // even if How': the event listener opens or closes tabs. var evt extension's code= document.createEvent("Events"); evt.initEvent("TabOpen", true, false); t.dispatchEvent(evt); return t;
This Now that we have the logic for our code, all that remains is useful information, because to write the necessary pieces in order to have it tells us get executed at the right time. When we modified the code in the tree this wasn't a concern: we assumed that if code we want to know when a tab has been createdchanged in '''addTab''' would get executed whenever '''addTab''' got called. With an extension, we have to add a listener for explicitly tell the browser how and when to execute our code. We do this using '''eventlisteners'''.
From our previous research, we know that the methods in tabbrowser cause a variety of events to get dispatched into the event system. These events move through the event system regardless of whether any code notices them--they are passive. In most cases, nothing is done in response to events. However, any developer can decide to do something in response to an event, which is what extension developers must do in order to get their code into the browser.
moveTabToWe need to wire our code to a number of events by [http://developer.mozilla.org/en/docs/XUL_Event_Propagation#Adding_an_Event_Listener adding event listeners]. Two of the events we know already, '''TabOpen''' and '''TabSelect'''. However, we also need to register our listeners at start-up using the '''window's load event'''. We'll add code to remove our event listeners in the '''window's unload event''' too.
http://lxr==addtabbeside.mozilla.org/seamonkey/source/toolkit/content/widgets/tabbrowser.xml#1958js==
 Create a file named: '''addtabbeside@senecac.on.ca''To keep things clean weThis file should contain the full path to your extension, for example:  C:\temp\addtabbeside Now put ll write all this file code in your development profile's extensions directory (NOTE: replace a custom object called ''Username'AddTabBeside' with your username and ''dev-profile'' with your development profile name): . C:\Documents and Settings\Here is ''Username''\Application Data\Mozilla\Firefox\Profiles\''dev-profile''\extensions       '''overlayaddtabbeside.js''':
var AddTabBeside = {
onUnload: function() {
// Remove our listeners
var container = gBrowser.tabContainer;
container.removeEventListener("TabOpen", this.onTabOpen, false);
onTabSelect: function (e) {
// when When a different tab is selected, remember which one. This is
// necessary because when a new tab is created, it will get pushed
// to the end of the list, but we need to know where to put it.
};
// Insure that our code gets loaded at start-up
window.addEventListener("load", function(e) { AddTabBeside.onLoad(e); }, false);
This code will become part of an overlay that will be merged with the browser at runtime. But where do we put this file? Firefox needs to know where it is in order to load and run it at startup. We also need to create an installation package for our overlay.
==Creating the rest of the extension==
Firefox extensions are packaged as compressed zip files with a '''.XPI''' extension. This is what you download when you visit http://addons.mozilla.org and choose to install an extension. Because extensions are just .zip files, you can unpack any extension and see how it's built. For example, try saving [https://addons.mozilla.org/firefox/16/ ChatZilla's .xpi] to your computer and decompress it (NOTE: if your built-in unzip program won't do this, try changing the file's extension to .zip).
An extension is a series of files and directories containing JavaScript, XUL, CSS, XML, RDF, and other custom text files. Some of these files define scripts to be executed, such as those we wrote above. Other files contain information ''about'' the extension, metadata telling the browser how to integrate and install things, what the extension is called, its version number, etc. We need to create these other files now.
===Extension files and directory structure===
Start by creating the following directory structure:
addtabbeside/
chrome/
content/
Because this is a first extension, we will skip some [http://developer.mozilla.org/en/docs/Building_an_Extension#Setting_up_the_Development_Environment other directories and files] that more complete extensions would include (e.g., localization, skins).
====addtabbeside/chrome/content====
The first thing to do is to copy the '''addtabbeside.js''' file we wrote earlier to '''addtabbdeside/chrome/content/addtabbeside.js'''.
This code now needs to get merged into the browser so it can access elements within the application. We do this by providing a [http://developer.mozilla.org/en/docs/XUL_Overlays XUL Overlay] file. A XUL Overlay is a .xul file that specifies XUL fragments to insert at specific merge points within a "master" document. Often this is used to add new UI to an application (e.g., a new menu item), but in our case we'll use it to merge our addtabbeside.js script into the browser.
ItWe need to create 's one thing to say you'd like to change the browser's behaviour, but quite another to actually do itaddtabbdeside/chrome/content/overlay. The change you have in mind might be quite simple, in the end (ours is). But you still have to figure out where that simple code needs to go. That can be difficult. However, difficult isnxul'''t the same as impossible.
How do you begin<pre><? First, let's start at the top and find some UI notation we can search for in the codexml version="1. In our case, we can focus on the various methods for creating a new tab0" encoding="UTF-8"?> <overlay id="addtabbeside-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/x-javascript" src="addtabbeside.js"/> </overlay></pre>
* CTRL+T* Right-Click an existing tab and select New Tab* File > New Tab====addtabbeside/====
The second Having added our script and third methods are useful, as they provide us with a unique string we can search for in the code. Before we can change anythingoverlay files, we have now need to search and read existing code in order add a couple of metadata files to help Firefox understand where to begin--this is the standard pattern for open source and Mozilla developmentinstall/load our extension.
==Search 1 - finding The first is '''addtabbeside/chrome.manifest''', which is a UI string==[http://developer.mozilla.org/en/docs/Chrome_Manifest Chrome Manifest]. This file helps Firefox translate between chrome:// URIs and actual files on disk. It also allows us to specify where our overlay will be merged into the browser:
We # Chrome package addtabbeside has it're looking for a unique string--"New Tab"==, so we'll use [http:s content in ./chrome/content content addtabbeside chrome/content/lxr # Overlay the browser.mozillaxul file with overlay.org LXR's] '''Text Search''' featurexul overlay chrome://browser/content/browser. Here are the results you get when you search for "New Tab"xul chrome://addtabbeside/content/overlay.xul
<blockquote>httpThe first line registers the location for our content (i.e., .xul, .js). The second line registers our overlay, and says that overlay.xul will be merged with browser.xul. Mozilla uses chrome://lxr.mozilla.orgURIs to refer to aspects of the interface, and chrome://seamonkeybrowser/search?string=New+Tab<content/blockquote>browser.xul ''is'' the browser (try typing it into the address bar).
Lots of results, many of which point The second metadata file we need to comments in the codecreate is '''addtabbeside/install.rdf'''. HoweverThis is an [http://developer.mozilla.org/en/docs/Install_Manifests install manifest] that tells the Firefox add-on manager how to install the extension, with information like who wrote it, the first result looks interesting:version number, compatibility information, etc.
<blockquotepre><?xml version="1.0" encoding="UTF-8"?><RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://lxrwww.mozilla.org/seamonkey2004/sourceem-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:id>addtabbeside@senecac.on.ca</toolkitem:id> <em:name>Add Tab Beside</localesem:name> <em:version>0.1</enem:version> <em:creator>David Humphrey</em:creator> <em:description>New tabs are created beside the current tab instead of at the end of the tab list.</em:description> <em:targetApplication> <Description> <em:id>{ec8030f7-USc20a-464f-9b0e-13a3a9e97384}</chromeem:id> <!-- firefox --> <em:minVersion>2.0</globalem:minVersion> <!--<em:maxVersion>3.0a9pre</em:maxVersion>--> <!-- trunk build Oct 14, 2007 --> <em:maxVersion>3.0+</tabbrowserem:maxVersion> <!-- work for v3.dtd#20 and above --> </Description> </em:targetApplication> </Description></RDF></blockquotepre>
Here we see ===Testing the DTD file describing the key/value pairs for the en-US localized strings. Mozilla uses this technique to allow localizers to translate strings in an application into many different languages without having to change hard-coded strings in the code (you can read more about localization, DTDs, and Entities [http://developer.mozilla.org/en/docs/XUL_Tutorial:Localization here]) extension===
Looking closely at Eventually we'''tabbrowserll package our extension properly into a redistributable .xpi.dtd' However, while we're testing it' s nice to be able to use it in an expanded form so we see that our English string, "New Tab", has the following entity: can make changes.
<!ENTITY newTabTo this end, create a file named '''addtabbeside@senecac.label "New Tab">on.ca''' and put it in your development profile's extensions directory (NOTE: replace ''Username'' with your username and ''dev-profile'' with your development profile name):
This is good information, because it allows us to repeat our search with an entity instead of a string, which should help us get closer to the code we C:\Documents and Settings\'re after'Username''\Application Data\Mozilla\Firefox\Profiles\''dev-profile''\extensions\addtabbeside@senecac.on.ca
==Search 2 - finding an ENTITY==Or in Mac OSX
Repeating the search with the '''newTab.label''' ENTITY value instead of the "New Tab" string makes a big difference--we have many fewer hits: ~/Library/Application Support/Firefox/Profiles/
<blockquote>httpThis file should contain a single line of text--the full path to your extension, for example://lxr.mozilla.org/seamonkey/search?string=newTab.label</blockquote>
Not surprisingly, the first result is the same DTD file (i.e., tabbrowser.dtd) we already found. The second result looks interesting, though: <blockquote>http://lxr.mozilla.org/seamonkey/source/toolkit/content/widgets/tabbrowser.xml#80</blockquote> Here we see the code to generate the pop-up context menu for a tab (i.e., what you get when you right-click on a tab in the browser):  <pre> <xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;" xbl:inherits="oncommand=onnewtab"/> </pre> Having found the appropriate entity value, we also notice the use of a function name, '''onnewtab'''. This line of code says that the xul:menuitem will inherit the '''oncommand''' value from its parent (you can read more about XBL attribute inheritance [http://developer.mozilla.org/en/docs/XUL_Tutorial:XBL_Attribute_Inheritance here]). In other words, when this menu item is clicked, call the '''onnewtab''' function. ==Search 3 - finding a Function== Armed with this new information, we are even closer to finding the right spot to begin working. We've gone from UI string to XML ENTITY to function. All we have to do now is find that function: <blockquote>http://lxr.mozilla.org/seamonkey/search?string=onnewtab</blockquote> This returns many results for things we aren't interested in, including files rooted in /suite, /db, etc. Since we are interested in finding this behaviour in Firefox, we need to focus on the files rooted in '''/browser'''. One looks particularly interesting: <blockquote>http://lxr.mozilla.org/seamonkey/source/browser/base/content/browser.xul#503</blockquote> In this case, the tabbrowser widget has the onnewtab property set to another function, '''BrowserOpenTab();''' (i.e., Firefox seems to handle tab creation in a non-standard way, providing its own method instead of using the default). Since we want to find the definition of this function, we search for '''"function BrowserOpenTab("''', which returns two results: <blockquote>http://lxr.mozilla.org/seamonkey/search?string=function+browseropentab%28</blockquote> Again, we're interested in Firefox (i.e., browser) instead of SeaMonkey (i.e., suite), so we skip to the second result: <blockquote>http://lxr.mozilla.org/seamonkey/source/browser/base/content/browser.js#1802</blockquote> This shows us that we need to be looking for yet another function, '''loadOneTab()'''. Another search: <blockquote>http://lxr.mozilla.org/seamonkey/search?string=loadonetab</blockquote> The first result is not surprising, and we're back to the tabbrowser widget. The '''loadOneTab''' method calls another method to actually create and insert the new tab:  var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner, aAllowThirdPartyFixup); Since '''addTab''' is a method of '''this''' we can search within the current document (CTRL+F) to find the '''addTab''' method. Finally we've found the right spot! <blockquote>http://lxr.mozilla.org/seamonkey/source/toolkit/content/widgets/tabbrowser.xml#1160</blockquote>  this.mTabContainer.appendChild(t); Now all that we have to do is modify it to insert rather than append. =The 'How': the necessary changes to the code= There are different ways you could go about making this change, and someone with more experience using tabbrowser might recommend a different strategy or outcome. I decided to work on something that I knew nothing about in order to highlight the process one goes through, or at least the process I went through, when working with someone else's code. Since my goal is to show you how to do this, I also discuss my errors and mistakes below--they are an important part of the process too. ==First Attempt== The goal is to make as small a change as possible, since the existing code works well--I just want it to work slightly different. I'm also not interested in reading all of the code in order to make such a small change. I want to leverage as much of what is already there as I can. I assume that the '''appendChild()''' method is responsible for the behaviour I don't like (i.e., adding new tabs to the end of the list). I'm not sure what to replace it with, so I do another search inside tabbrowser.xml (i.e., using CTRL+F) looking for other methods/attributes of '''mTabContainer'''. I come-up with some interesting options:  index = this.mTabContainer.selectedIndex; ... this.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes.item(aIndex)); ... var position = this.mTabContainer.childNodes.length-1; I decide that I can probably accomplish my goal using these alone, and so start working on a solution. Here is my first attempt, showing the changes to '''mozilla/toolkit/content/widgets/tabbrowser.xml''' and the '''addTab''' method:  // Insert tab after current tab, not at end. if (this.mTabContainer.childNodes.length == 0) { this.mTabContainer.appendChild(t); } else { var currentTabIndex = this.mTabContainer.selectedIndex; this.mTabContainer.insertBefore(t, currentTabIndex + 1); } I then repackage the toolkit.jar file (change ''objdir'' to your objdir name):   $ cd mozilla/''objdir''/toolkit/content $ make then run the browser to test (NOTE: ''minefield'' is my testing profile):  $ ../../dist/bin/firefox.exe -p minefield --no-remote I try to create a new tab using '''File > New Tab''' and nothing happens. ==Second Attempt== Clearly my code has some problems, since I've completely broken addTab. I decide to look for clues in the '''Error Console''' (Tools > Error Console) and notice the following exception whenever I try to add a new tab: <code>Error: uncaught exception: [Exception... "Could not convert JavaScript argument" nsresult: "0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)" location: "JS frame :: chrome://global/content/bindings/tabbrowser.xml :: addTab :: line 1161" data: no]</code> I make a guess that childNodes.length is not zero, but 1 by default (i.e., there is always at least one tab, even if it isn't visible). A quick modification to the code, and I test againC:\temp\addtabbeside
Start your browser and make sure your extension is loaded (check '''Tools > Add-ons''') and working properly by creating some tabs to see where they get positioned. if If you're having problems, check the '''Error Console''' (this.mTabContainer.childNodes.length '''== 1Tools > Error Console''') { ..for hints.
==Third Attempt=Packaging the extension===
This worksOnce debugging and testing is complete, but only the first time I you'll want to [http://developer.mozilla.org/en/docs/Extension_Packaging create an installable .xpi file] to give to your users. As was previously mentioned, .xpi files are just .zip files with a new tabdifferent extension. Clearly I still have some misconceptions about how All .xpi files must contain the '''mTabContainerinstall.selectedIndexrdf''' and '''mTabContainerfile in the root (i.e., when you unzip it, install.insertBefore(rdf should be in the top directory)''' really work.
I can't yet see how my code is wrong, but the exception I'm getting clearly indicates that I've got some sort of type conversion problem. I decide Follow these steps to look again at the code examples in tabbrowser.xml that I'm using as a guide, specifically '''insertChild()'''create an installable .xpi:
After # Create a few seconds the error is obviousnew zip file named '''addtabbeside.zip''' (NOTE: Iyou can use .xpi instead of .zip if your application will allow it).# Add your extension files and directories to '''addtabbeside.zip''', making sure that '''ve used an Integer where a Tab was requiredinstall. Here rdf''' is in the root (i.e., don't zip the addtabbeside directory itself, just it's contents).# Rename the corrected code:resulting file to '''addtabbeside.xpi'''
// Insert tab after current tab, not at end. if (this.mTabContainer.childNodes.length == 1) { this.mTabContainer.appendChild(You can try installing your extension in a browser that doesn't); } else { var currentTabIndex = this.mTabContainer.selectedIndex; this.mTabContainer.insertBefore(t, have it by simply dragging '''thisaddtabbeside.mTabContainer.childNodes.item(currentTabIndex + 1)xpi''');into it. }This should trigger the add-on manager and give you the option to install your extension and restart the browser.
==Success, and some bugs==
After repackaging the toolkit.jar file and running the browser, I'm able The rewrite to confirm that this last change an extension has been successful. Opening a new tab now works in the way I originally describedsuccess. I make a few more tests In both cases we've managed to insure that I haven't broken anything else, for example, what happens if I am on achieve the last tab and not in the middlesame goal using almost completely different methods. This works, which makes me realize that using Using an extension we'''append()''' is probably not necessary at all, and I can safely shorten my code down ve made it possible to share our changes with any Firefox user worldwide without having to the following:ship a custom build.
// Insert tab after current tabHowever, as was the case in our previous attempt, not at endour code has a bug. var currentTabIndex = this.mTabContainer.selectedIndexMoving existing tabs doesn't update our position state, since we only modify '''mPreviousIndex''' when a new tab is selected; thismoved tabs remain selected, but change their order (i.mTabContainere.insertBefore(, TabSelect won't, thisget dispatched on a move).mTabContainer.childNodes.item(currentTabIndex + 1));
This means that six lines Luckily we've already stumbled upon the solution to this problem--the '''TabMove''' event. Here is the updated version of code become two'''addtabbeside.js''', and with that reduction in number of lines, hopefully a reduction the changes in new bugs I've added (NOTEbold: within reason, favour fewer rather than more lines of code).
Speaking of bugs, a closer read of '''addTab''' (see [http: var AddTabBeside = { //lxrState info on the last tab to be selected.mozilla.org mPreviousIndex: 0, onLoad: function() { /seamonkey/sourceAdd a listener for the TabOpen event, which gets called as /toolkit/content/widgets/part of addTab in tabbrowser.xml#1219 line 1219] var container = gBrowser.tabContainer; container.addEventListener("TabOpen", this.onTabOpen, false) would indicate that ; // Also add a listener for TabSelect so we've introduced know when focus changes to a few with our new positioning code:tab container.addEventListener("TabSelect", this.onTabSelect, false); '''// wire up And a progress listener for the new browser object.TabMove to fix an edge case''' var position = '''container.addEventListener("TabMove", this.mTabContainer.childNodes.length-1onTabSelect, false);''' var tabListener = this // Finally, add a listener for shutdown window.mTabProgressListeneraddEventListener(t"unload", bthis.onUnload, blankfalse); }, onUnload: function() { // Remove our listeners var container = gBrowser.tabContainer; container.removeEventListener("TabOpen", this.onTabOpen, false); container.removeEventListener("TabSelect", this.mTabListeners[position] = tabListeneronTabSelect, false); '''container.removeEventListener("TabMove", this.mTabFilters[position] = filteronTabSelect, false);''' }, onTabSelect: function (e) { // When a different tab is selected, remember which one. This is // necessary because when a new tab is created, it will get pushed // to the end of the list, but we need to know where to put it. this.mPreviousIndex = gBrowser. ttabContainer._tPos = positionselectedIndex; }, Where the assumption before was that onTabOpen: function (e) { // Get the newly created tab was at , which will be last in the list var newTab = e.target; // Move this new tab to the end right of the listpreviously selected tab, // checking to see how many tabs there are currently. By default // there is 1 tab, and the new code breaks thatfirst time onTabOpen is called, there will // be 2 (the default plus the newly created tab). ThereforeIn this case, we also need to update the value of '''position''don't // move the new tab, since it is already in the right spot. In all // wire up a progress listener for other cases, move the tab to the right of the new browser objectcurrent tab. if (gBrowser.tabContainer.childNodes.length > 2) { var position = currentTabIndex gBrowser.moveTabTo(newTab, this.mPreviousIndex + 1); } }, };
No other obvious defects are visible from // Insure that our changescode gets loaded at start-up window.addEventListener("load", function(e) { AddTabBeside.onLoad(e); }, false);
=Reflections=
The change I was making was simple enough that I didnMany of the steps we did in order to create the extension't bother looking at any documentation or using s installation and registration files will be the JavaScript debuggersame each time. I found out afterward that tabbrowser has good Rather than doing it by hand, you can use [http://developerted.mozillamielczarek.org/encode/docsmozilla/XUL:tabbrowser documentation extensionwiz/ an on MDC-line wizard]. Another trick worth trying when you're making lots of JavaScript changes like this is to add the following line to your build a basic extension.mozconfig file:  ac_add_options --enable-chrome-format=flat
This will cause The process of developing extensions is greatly improved with the .jar files to be expanded so that you can edit the .xml/.js/.xul files in place and skip the repackaging step above (see addition of [http://wwwkb.mozillamozillazine.org/build/jar-packaging.html). If you also use Setting_up_extension_development_environment some preferences] to the browser, as well as the [http://ted.mielczarek.org/code/mozilla/extensiondev/index.html Extension Developer's extension] . This extension will automate many of the steps described here. It will also allow you can to reload the browser's chrome at runtime. This is a great way to test changes to your XUL/JS files without restarting having to restart the browser.
You can read complete details on how to set-up an extension developer's environment [http://kb.mozillazine.org/Setting_up_extension_development_environment here].
=Resources=
* [http://developer.mozilla.org/en/docs/Extensions Extensions on MDC]
* [http://kb.mozillazine.org/Extension_development Extension Development on Mozillazine]
* [http://developer.mozilla.org/en/docs/XUL_Event_Propagation XUL Event Propagation]

Navigation menu