In the rather straightforward bug 562433, Firefox’s location.host and location.hostname need to return the empty string for host-less URIs instead of throwing an exception. What ends up wasting my time with such 1-minute fixes is figuring out the right test location and the right command to run the test. At least documenting this should save a few minutes next time:
TEST_PATH=dom/tests/mochitest/bugs/test_bug562433.html make -C /c/mozilla/obj mochitest-plain
See the Mochitest automated testing framework documentation for details.
Once in a while you may run into an Excel spreadsheet in which the first two rows have been used for column labels as a way to wrap text. Cleaning this up involves the straightforward task of merging the first two cells of each column. This rather tedious task is thankfully easily automated with an Excel VBA macro. See the code samples below:
Sample 1:
Sub MergeFirstTwoCellsInEachColumn()
' Bound the range selection as Ctrl+End would.
' See http://www.ozgrid.com/forum/showthread.php?t=17070
Dim lastCol As Integer
Dim row1LastCol As Integer
Dim row2LastCol As Integer
row1LastCol = Range("A1").currentRegion.End(xlToRight).Column
row2LastCol = Range("B1").currentRegion.End(xlToRight).Column
lastCol = WorksheetFunction.Max(row1LastCol, row2LastCol)
' Select the first two rows
Rows("1:2").Select
' Merge the first two cells of each column
For i = 1 To lastCol
Dim columnCells
columnCells = Selection.Columns(i).Cells
Dim finalColumnText
' Concatenate contents of cells in rows 1 and 2 in this column
' separating them with a space
finalColumnText = Trim(columnCells(1, 1) & " " & columnCells(2, 1))
' Clear both cells and store the new value in the first cell
Selection.Columns(i).Value = ""
Selection.Columns(i).Cells(1, 1) = finalColumnText
' Merge the cells
Selection.Columns(i).Merge
Next i
End Sub
One side effect of this code is that it merges cells in the columns up to the end of the range that would be selected if you pressed Ctrl + End. The columns to the right of this range are not merged, in effect leaving two rows in the spreadsheet for the headings. Enter Sample 2:
Sub MergeFirstTwoCellsInEachColumn2()
' Select the first two rows
' Bound the range selection as Ctrl+End would.
' See http://www.ozgrid.com/forum/showthread.php?t=17070
Dim lastCol As Integer
Dim row1LastCol As Integer
Dim row2LastCol As Integer
row1LastCol = Range("A1").currentRegion.End(xlToRight).Column
row2LastCol = Range("B1").currentRegion.End(xlToRight).Column
lastCol = WorksheetFunction.Max(row1LastCol, row2LastCol)
' Select the first two rows
Rows("1:2").Select
' Combine the contents of the first two cells of each column
For i = 1 To lastCol
Dim columnCells
Dim finalColumnText
columnCells = Selection.Columns(i).Cells
' Concatenate contents of cells in rows 1 and 2 in this column
' separating them with a space
finalColumnText = Trim(columnCells(1, 1) & " " & columnCells(2, 1))
' Store the new value in the first cell
Selection.Columns(i).Cells(1, 1) = finalColumnText
Next i
' Delete row 2
Rows("2").Delete
End Sub
In MergeFirstTwoCellsInEachColumn2, the new column headings are simply written to the top cell and when this has been done for all columns, row two is deleted. This is perhaps the more elegant solution for most spreadsheets. A few comments on the code:
row1LastCol = Range("A1").currentRegion.End(xlToRight).Column
This line determines the last non-blank column in row 1. The currentRegion property of the Range object returns a “range bounded by any combination of blank rows and blank columns”. When called on the Range(“A1”) object, it effectively selects the same region as Ctrl + End. It’s then straightforward to inspect the End property and get the corresponding Column.
lastCol = WorksheetFunction.Max(row1LastCol, row2LastCol)
lastCol is the greater of the last columns in the first two rows (needed in case both rows don’t have the same column number as the last column). This lastCol value is important because without it, Excel will chew lots of CPU running the macro on all the columns beyond those spanned by the input data.
While reading through the JPF core source code (and other JPF extensions as well), you may come across classes like JavaClass, ClassParser, etc. These classes are part of the Byte Code Engineering Library and are in the org.apache.bcel package. In case you are interested in easily reading the corresponding source in your favorite editor (Eclipse in my case), download the appropriate source from the BCEL download page.
The NASA AMES site has the necessary documentation for this procedure (and all else JPF). Nonetheless, I’m documenting the process here to save me some time the next time I need to do this since all commands will be in one spot.
For this project, I was running a Windows 7 box but these instructions should be easy to port to Linux other platforms. You will need to install the latest version of the Java Runtime Environment (JRE) and the Java Development Kit (JDK). Without the JDK installed (and included in your PATH environment variable), you will get the famous “javac is not recognized as an internal or external command.” These tools can be found on Sun’s website. Proceed to the command line and verify that these commands list the most up to date recent Java version available (JDK 6 Update 20 as of this posting).
javac -version
java -version
You will also need Mercurial to install JPF. For windows users, it may be easier to install TortoiseHg since it will automatically add the hg command to the PATH [needs confirmation]. On my system, I installed the JPF core into a JPF folder I created in my home folder. To do so, drop to the command line then:
cd %HOMEPATH%
mkdir jpf
cd jpf
hg clone http://babelfish.arc.nasa.gov/hg/jpf/jpf-core
To build the source and run the test suite:
cd jpf-core
bin/ant test
This ant script requires the JAVA_HOME environment variable to be set (on Windows anyway). I set it to my JDK installation path.
Note: for my initial checkout, I cloned http://babelfish.arc.nasa.gov/hg/jpf but expecting to get the whole tree, but the subrepositories were empty! All the extensions must be separately cloned. Before running the Racer example mentioned on the JPF page, you need to set up the site.properties file. This file should be placed in a folder called “.jpf” in the user’s home directory. Attempting to run the example before setting up this file will give you the error message: “gov.nasa.jpf.JPFClassLoaderException: no classpath entry for gov.nasa.jpf.JPF found (check site.properties)”.
cd %HOMEPATH%
mkdir .jpf
cd .jpf
echo # JPF site configuration > site.properties
echo # >> site.properties
echo jpf-core = ${user.home}/jpf/jpf-core >> site.properties
echo # >> site.properties
echo # numeric extension >> site.properties
echo jpf-numeric = ${user.home}/jpf/jpf-numeric >> site.properties
echo extensions+=${jpf-numeric} >> site.properties
echo # >> site.properties
echo # annotation-based program properties extension >> site.properties
echo jpf-aprop = ${user.home}/jpf/jpf-aprop >> site.properties
echo extensions+=,${jpf-aprop} >> site.properties
echo # >> site.properties
echo #... and all your other installed projects >> site.properties
echo # >> site.properties
Verify that site.properties has been successfully created. Now we can run the example:
java -jar build/RunJPF.jar src/examples/Racer.jpf
Et voila! You should now see output like:
JavaPathfinder v5.0 - (C) 1999-2007 RIACS/NASA Ames Research Center
====================================================== system under test
application: Racer.java
====================================================== search started: 7/1/10 5:51 PM
10
10
====================================================== error #1
gov.nasa.jpf.listener.PreciseRaceDetector
race for: "int Racer.d"
main at Racer.main(Racer.java:16)
"int c = 420 / racer.d; // (4)" : getfield
Thread-0 at Racer.run(Racer.java:7)
"d = 0; // (2)" : putfield
====================================================== snapshot #1
thread index=0,name=main,status=RUNNING,this=java.lang.Thread@0,target=null,priority=5,lockCount=0,suspendCount=0
call stack:
at Racer.main(Racer.java:16)
thread index=1,name=Thread-0,status=RUNNING,this=java.lang.Thread@272,target=Racer@271,priority=5,lockCount=0,suspendCount=0
call stack:
at Racer.run(Racer.java:7)
====================================================== results
error #1: gov.nasa.jpf.listener.PreciseRaceDetector "race for: "int Racer.d" main at Racer.main(Race..."
====================================================== statistics
elapsed time: 0:00:00
states: new=9, visited=1, backtracked=4, end=2
search: maxDepth=5, constraints=0
choice generators: thread=8, data=0
heap: gc=8, new=278, free=32
instructions: 2956
max memory: 15MB
loaded code: classes=68, methods=988
====================================================== search finished: 7/1/10 5:51 PM
One of the more interesting facets of programming is probably the close relationship between a program’s correctness and the set of assumptions about its execution environment and/or data. In the web world, the significance of these assumptions takes on a new height since they determine not just the correctness of the application/browser, but also how secure it is (“hackability?”). I decided to do a little test and what faster way to get em browsers running code than by bombarding em with WM_* messages. Here’s a little code snippet that I wrote up for this test (warning: will blow your computer to smithereens, compile and run at your own risk):
// SendMsg.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "windows.h"
#define START_MESSAGE WM_CLOSE
#define END_MESSAGE (START_MESSAGE + 0x0001)
int _tmain(int argc, _TCHAR* argv[])
{
WPARAM wParam = 0;
LPARAM lParam = 0;
HWND hWnd = FindWindow(NULL, TEXT("Mozilla Firefox"));
if (!hWnd) {
MessageBox(NULL, TEXT("Could not find firefox window"), TEXT("Error"), MB_OK);
return -1;
}
int errors = 0;
for (DWORD i = 0; i < 10; i++) {
if (!PostMessage(hWnd, START_MESSAGE, wParam, lParam)) {
wchar_t buffer[4096];
DWORD lasterror = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, lasterror, 0, buffer, 4096, NULL);
MessageBox(NULL, buffer, TEXT("Error"), MB_OK);
errors++;
}
}
if (errors)
return -1;
return 0;
}
The objective of this code is to send the WM_CLOSE message to a browser a given number of times (10 in this case). It starts out with a call to FindWindow to get a handle to the Firefox window. Since the window title is hard coded, a new blank tab must be active in Firefox. Once we have the handle, all that’s left is to call PostMessage the desired number of times. We use GetLastError and FormatMessage for feedback if things do not proceed as planned. I settled on WM_CLOSE since its effect should be the most noticeable ;). Below are some of the results I found:
Interestingly enough, the concept of getting a window handle based the window title worked well for Firefox, Opera, and IE8. It didn’t work in Chrome, but I didn’t want to invest the time to figure out how to get the handle there. Opera 10.53 suffers from the same problem as Firefox as pictured below (the only difference being that the browser ends up closing when one of the timeouts hits 0 so you’d better be sure you have saved all your work).
So the concept of a “Modal” dialog box seems to depend on having exactly one way to launch the box, which is definitely a questionable assumption as shown. IE8 displayed only 1 close prompt box. Kudos to them. I wonder if this type of bug effectively disappears with multiprocess browsing?
In closing, I should note that I also tried the WM_QUIT message (only 1 of which is necessary). Firefox just died as did just about everything else on my system, including explorer.exe and all the other EXEs unfortunate enough to have been running at the time (I was doing some mass message posting initially and it was only after losing a bunch of data that I decided it was in my best interest to post these messages to a specific window).
For anyone interested in running this program, note that Opera 10.53 will CRASH if you send it WM_QUIT! Crash report explains that “Opera.exe 3374 caused exception C0000005 at address 677DD1F3 (Base: 1250000).” I’ve sent in a crash report so that should hopefully get fixed soon. This shouldn’t affect web content since it can’t pull these PostMessage stunts.
Note that this program is easily modified to automate the sending of a range of messages by changing to loop limits to [START_MESSAGE, END_MESSAGE]. I will hopefully have enough time to build a full blown GUI tool for this.
Bug 492625 was a rather easy fix, but one from which I tried to gleam as much as I could from nonetheless. Some /storage/* header files used defines with leading underscores. I used the following regular expression in the search and replace:
Find: (\s+)(_+)(moz\w.+_h)(_+)
Replace With: \1\3
What was interesting was that MXR suggested that there were 72 changes to be made, but the above regex only caught 63. It was only after doing all the manual labor of combing through the list that I realized that I could have simply done a regex search for lines starting with #define (duh) to determine the source of the discrepancy. It turns out that there were some #defines without the leading underscore but having a trailing one.
It was also interesting trying to find the document defining the C++ standard. I ended up at the JTC1/SC22/WG21 – The C++ Standards Committee page which had the relevant text in the “17.6.3.3.2 Global names” section. Stack overflow was the key pointer as to exactly where the relevant info was:
Certain sets of names and function signatures are always reserved to the implementation:
- Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
- Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
Building firefox with debug symbols has been a rather tricky endeavor for me for quite some time. The documentation on how to do this seems to indicate that adding these lines to your mozconfig file should be sufficient:
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debugger-info-modules=yes
should do the trick. However, I have (for months now) been running into this make error: no rule to make nspr4.pdb needed by export whenever I try to build with debug symbols. Bug 338224 has a pending patch for this issue. In the mean time, the following trick seems to solve the problem for me: force the -Zi compiler option into the compiler flags variable. For the nspr files, the PDB file will be generated as desired. For the other files, the -Zi option will be redundant but this fix gets you up and running ASAP. Here’s the associated patch file:
diff --git a/nsprpub/configure.in b/nsprpub/configure.in
--- a/nsprpub/configure.in
+++ b/nsprpub/configure.in
@@ -2827,18 +2827,18 @@ if test -n "$_SAVE_DEBUG_FLAGS"; then
fi
if test -n "$MOZ_OPTIMIZE"; then
CFLAGS="$CFLAGS $_OPTIMIZE_FLAGS"
CXXFLAGS="$CXXFLAGS $_OPTIMIZE_FLAGS"
fi
if test -n "$MOZ_DEBUG_SYMBOLS"; then
- CFLAGS="$CFLAGS $_DEBUG_FLAGS"
- CXXFLAGS="$CXXFLAGS $_DEBUG_FLAGS"
+ CFLAGS="$CFLAGS $_DEBUG_FLAGS -Zi"
+ CXXFLAGS="$CXXFLAGS $_DEBUG_FLAGS -Zi"
fi
if test -n "$MOZ_OPTIMIZE"; then
OBJDIR_TAG=_OPT
else
OBJDIR_TAG=_DBG
fi
Here is the mozconfig file I used with this patch:
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../obj
ac_add_options --disable-xpconnect-idispatch
ac_add_options --disable-activex
ac_add_options --disable-activex-scripting
ac_add_options --disable-accessibility
ac_add_options --enable-optimize=no
ac_add_options --enable-debug-symbols=yes
I just installed Visual C++ Express 2010 (the most recent version online) in order to build Firefox. However, MozillaBuild has not yet been officially updated to handle Visual C++ 2010. Only 2008 is currently supported as per the build prerequisites. Thankfully, they mention that the trunk version of MozillaBuild should support VC++ Express 2010. The only thing that wasn’t so obvious here (for someone dashing around trying to get building ASAP) was where on earth to look for this. After some totally unnecessary and mostly unhelpful Google-ing around, I ended up at http://hg.mozilla.org/mozilla-build/rev/ad3bd5686474. The necessary start-msvc10.bat and guess-msvc.bat files did the trick.
Bug 484026 from last November was an interesting peek into the Firefox bookmarks code. I looked into Dan Mill’s claim that getItemGUID was creating new GUID annotations for deleted items. I started off by creating an xpcshell test for the bug. Interestingly, the test passed! The problem had apparently been fixed at some point.
My first thought on how to proceed was that perhaps the error was being thrown from the wrong method, hence my erroneous attempt to patch nsNavBookmarks.cpp (probably an instance of “if it isn’t broken…”). That would explain why Marco Bonardo didn’t think the change was such a great idea – i.e. you might as well go changing every other possible caller if you aren’t fixing the source.
Nonetheless, it was interesting to note the use of scopers and how declaring this mozStorageStatementScoper eliminated the need for if (statement) statement->reset()
calls at every function exit point! I’m sure the idea goes way back, but whoever figured out that a scoping object’s destructor could handle this is a genius in my humble (C++ wise) opinion.
So, even though I didn’t get some C++ action as I had anticipated, I still picked up a thing or two – such as the coding standard used for the bookmarks service xpcshell tests, and how to use the do_throw and do_check_eq functions.
Bug 505228 (from Sep 2009) is now checked in. The patch fixes a problem with the TraceMonkey JSON encoder where the nesting of arrays is correct for all but the first line of the output string when the “space” argument is specified. See the ECMAScript wiki for more information on JSON support. My initial patch simply (and naively) called the WriteIndent function. I didn’t catch this mistake until I wrote an automated test for the bug. This was the first bug for which I wrote an xpcshell test – it’s amazing how difficult it was for me to figure out how to run these (rather embarrassing in retrospect, but oh well, at least I stayed the course). The JSON xpcshell tests can be run using:
cd ~/mozilla/obj
make -C dom/src/json/test/ xpcshell-tests
To check just the specific file in question (the one to which I added the test), I used
make SOLO_FILE="test_replacer.js" -C dom/src/json/test/ check-one
This was helpful since I could run a smaller subset of tests while developing, and then later run the whole JSON test suite.