1. Mozilla Firefox
Extension Development
Course 2: Advanced
Littlebtc
OSSF Workshop / MozTW Acitivity
The text of the slide is under CC-BY-SA-3.0.
Most photos are under CC-BY-SA compatible license,
while screenshots / logos are under Fair Use.
Code is under MIT license.
2. Review
• Do you remember…?
– XUL and controls?
– Some JavaScript technique?
– install.rdf and chrome.manifest?
– Some XPCOM introduction?
7. Default Preferences
• Can be included in the Extension Dir / XPI file
• Will be loaded as default value of preferences
• Place .js files in [ext-dir]/defaults/preferences/
• Example of .js file content of default
preferences :
pref(quot;extensions.extensionname.preferencenamequot;, false);
– Name: has a widely used naming convention
– Value: Boolean / Integer / String
8. Take an example
• See defaults/preferences/ tutorial.js
pref(quot;extensions.tutorial.option1quot;, false);
pref(quot;extensions.tutorial.option2quot;, quot;quot;);
pref(quot;extensions.tutorial.option3quot;, 32);
pref(quot;extensions.tutorial.default_editorquot;, quot;quot;);
10. Preference System:
<prefwindow>
• Way to make a quot;option windowquot;
• It is XUL, again
• But it has a special system, in that system,
you don't need to write too much
JavaScript/XPCOM to support changing
preferences.
12. <prefpane>
• Must be a child of <prefwindow>
• Reprensent a pane (tab) in the prefwindow
• Attributes:
– label
– image
– onpaneload: a onload-like on this panel
– src: Content can be external XUL
<prefpane id=quot;paneGeneralquot; label=quot;Generalquot;
src=quot;chrome://path/to/paneOverlay.xulquot;/>
13. <preferences> <preference>
• <preferences>: Container
• <preference>: quot;Magic Attributequot; to hold the
actual preference, attributes:
– id
– name: Corresponding preference name
– type: bool/string/unichar/int/file
• In controls, use preference=quot;idquot; to connect
the magic!
17. Part 2: Read/Write in XPCOM
• XPCOM: nsIPrefService / nsIPrefBranch
• Changes Preferences in JavaScript
• First step: Read branch
// Get the root branch
var prefs = Components.classes[quot;@mozilla.org/preferences-service;1quot;]
.getService(Components.interfaces.nsIPrefBranch);
// Get the quot;extensions.tutorial.quot; branch
var prefs = Components.classes[quot;@mozilla.org/preferences-service;1quot;]
.getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch(quot;extensions.tutorial.quot;);
18. Part 2: Read/Write in XPCOM
• Then, read/write
• getBoolPref(), setBoolPref()
• getCharPref(), setCharPref()
– BEWARE: Not Unicode Compatible!
• getIntPref() and setIntPref()
19. Part 2: Read/Write in XPCOM
Examples to run in JavaScript Shell:
// Get the quot;extensions.tutorial.quot; branch
var prefs = Components.classes[quot;@mozilla.org/preferences-service;1quot;]
.getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch(quot;extensions.tutorial.quot;);
// Get the value of option3 (a number)
var option3 = prefs.getIntPref('option3');
alert(option3);
// Set the value of option1 (a boolean) to true
prefs.setBoolPref('option1', true);
var option1 = prefs.getBoolPref('option1');
alert(option1);
20. Part 2: Read/Write in XPCOM
• Read/Write Unicode Strings:
(from https://developer.mozilla.org/en/Code_snippets/Preferences)
// Example 1: getting Unicode value
var option2 = prefs.getComplexValue(quot;option2quot;,
Components.interfaces.nsISupportsString).data;
alert(option2);
// Example 2: setting Unicode value
var str = Components.classes[quot;@mozilla.org/supports-string;1quot;]
.createInstance(Components.interfaces.nsISupportsString);
str.data = quot;火狐quot;;
prefs.setComplexValue(quot;option2quot;,
Components.interfaces.nsISupportsString, str);
21. When you should use XPCOM
• First run:
– Make a default pref to detect first run
(Change it after first run processing is ended)
• Dialog to remember something:
– Like quot;Exit Firefoxquot;:
23. The nsIFile / nsILocalFile
• nsIFile: Cross-platform File I/O interface
• nsILocalFile: extends nsIFile, can access local
filesystem
• It is a quot;LOCATION INDICATORquot;
• What can nsILocalFile object do?
– Check whether the file exists
– Copy / Delete / Move files
– Create files / folders
– Work as a parameter to a lot of function
• Saving webpage into files, etc…
24. quot;Createquot; a nsILocalFile
• With a absolute path:
var file = Components.classes[quot;@mozilla.org/file/local;1quot;]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(quot;C:quot;);
• quot;Pathquot; should be in a quot;nativequot; way
like /home/littlebtc in Linux, C: in Windows
• file:// URI? Explained later
25. quot;Createquot; nsIFile pointing to special
folders
• Get an object located to Profile Directory:
// get profile directory
var file = Components.classes[quot;@mozilla.org/file/directory_service;1quot;]
.getService(Components.interfaces.nsIProperties)
.get(quot;ProfDquot;, Components.interfaces.nsIFile);
• You can replace the ProfD with:
With… Where With… Where
CurProcD (Firefox) Installation Desk Desktop
Dir C:UsersLittlebtcDesktop
Home User's Dir Progs quot;Programsquot; directory in
C:UsersLittlebtc Windows Start Menu
/home/littlebtc/
resource:app XULRunner App
Directory
26. How to…
Assuming we have a nsIFile object file, how to
• Get the native path representation of a nsIFile?
– file.path
• Know whether the path refers to a existed thing?
– file.exists()
– Return true or false
• Distinguish the path which this file object refers
to from a folder, a file, or a link, or others?
– Use file.isFile() file.isFolder()
file.isSymlink() file.isSpecial()
27. How to…
• Whether the path is
readable/writable/executable/hidden?
• Use file.isWritable() file.isReadable()
file.isExecutable() file.isHidden()
• Know the filesize (if it is a file)?
– file.fileSize
• The file name without path?
– file.leafName
• The parent of the path?
– file.parent (It is a nsIFile object, and can be null!)
28. How to…
• quot;Open the filequot; by the OS?
• file.launch();
• Do something like quot;Open containing folderquot; in
download manager?
• file.reveal();
• Both of them are not work on Unix-like!
29. How to…
• Delete the path?
– file.remove();
– For folder, you can only remove empty folder
• Copy path?
– file.copyTo(file2, 'newname');
– file2 represents the parent path for the target item,
if it is null, it will be copied to the same parent of file
• Move path?
– file.moveTo(file2, 'newname');
– file2 is the same as above
– You can only move empty folder
30. How to…
• How to quot;run the filequot; if it is a executable?
– file.launch(); (Not recommended)
– Use nsIProcess:
https://developer.mozilla.org/En/Code_snippets/
Running_applications
– nsIProcess cannot accept Unicode parameters on
Windows…
31. How to…
Assuming file is a nsIFile object refering to a folder path, how to:
• Access a file in this directory?
– file.append('filename');
Then file will be changed, will refer to this file, not the original
folder!
• Access multiple files? Clone object, then append
– var file1 = file.clone(); // Clone file object
file1.append('filename');
• How to create a subfolder?
– file.append(quot;DIRquot;); // Still, append first
if( !file.exists() || !file.isDirectory() ) {
// if it doesn't exist, create
file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,
0777);
}
32. File contents read/write
• https://developer.mozilla.org/en/Code_snippets/
File_I%2f%2fO
• A little complex, why? When it comes to the
encoding, it will be complex
33. File Picker / <filefield>
• File Open / File Save / Select Folder Dialog
• See
https://developer.mozilla.org/en/nsIFilePicker
• <filefield> in the <prefwindow>, like Firefox
options (UNDOCUMENTED):
http://www.xuldev.org/blog/?p=142
• We will have a small demo!
34. File I/O demo 1
• A run dialog like Windows, use undocumented
<filefield>
36. run.js (partial)
browse: function() {
// Initialize file picker
var fp = Cc[quot;@mozilla.org/filepicker;1quot;].createInstance(Ci.nsIFilePicker);
fp.init(window, 'Select a file', Ci.nsIFilePicker.modeOpen);
// Set Filter: file types
fp.appendFilter('Windows executable', '*.exe');
fp.appendFilter('All Files', '*.*');
// Show the dialog
var result = fp.show();
if (result == Ci.nsIFilePicker.returnOK) {
this.file = fp.file;
// This should not be happened, but this is a demo :P
if (!this.file.exists() || !this.file.isFile()) {
return;
}
// Include it into filefield
document.getElementById('file').file = this.file;
// Load file infos
document.getElementById('file_name').value = this.file.leafName;
document.getElementById('file_size').value = this.file.fileSize + ' bytes';
}
},
38. URI Problem
• In XPCOM, nsIURI is a common interface to represent
an URI (It can handle all types of URI)
• nsIURI <-> String URL: Use nsIIOService
– var uri =
Components.classes[quot;@mozilla.org/network/io-service;1quot;]
.getService(Components.interfaces.nsIIOService)
.newURI('http://moztw.org/', null, null);
– uri.spec will contain the string representation of the URI
• nsIURI <-> nsIFile
– var file_uri =
Components.classes[quot;@mozilla.org/network/io-service;1quot;]
.getService(Components.interfaces.nsIIOService)
.newFileURI(file); // Assuming file is a nsIFIle
– var file =
file_uri.QueryInterface(Components.interfaces.nsIFileURL)
.file;
39. When you will need nsIURI / nsIFile
• Downloading files
https://developer.mozilla.org/en/Code_snippets/
Downloading_Files (Basic)
http://www.xuldev.org/blog/?p=163 (Advanced,
by Gomita, a good intro to nsIWebBrowserPersist)
• Use Places API (Bookmarks, Tags, History)
https://developer.mozilla.org/en/Places
• Progress listeners
https://developer.mozilla.org/en/Code_snippets/
Progress_Listeners
• And much more…
41. What is a tree?
• A complex item lists
• An item may have some childs
42. Why is it so complex
• There are A LOT OF way to implement
• But none of them are NOT trivial
• Some is easier but not so easy to be extended
• Some is harder, but fully functional
44. The easiest: Content Tree
• <tree>: Sent a row number as rows attribute is
recommended?
• <treecols>, <treecol>: Define the columns
– Use flex to modify column width in <treecol>
– Use id to build an identifier of the column
• <treeitem>, <treerow>: Represents a row
• <treecell>: Represents a cell
– Attribute: label
46. Wait, a lot of new thing…?
• <splitter>: Between columns, to make them
resizable
• Nested items: <treechildren> under <treeitem>
• hidecolumnpicker=quot;falsequot;
– Whether to hide the column picker on the right
– seltype=quot;singlequot;: you can select only one item
or seltype=quot;multiplequot; for multiple selection support
• persist=quot;attributename1 attributename2quot;
– Make Firefox quot;rememberquot; user's settings on specific
attribute
47. OK, now, what is wrong?
• Though we can multiply the tree with DOM,
but it will the code will be somehow complex
due to the bad XUL structure
• So DOM way is not a popular way if it comes
to dynamic content trees
48. The most common:
Custom TreeView
• Implements nsITreeView to create a tree
• Set custom tree view into view property
• It can be fully functional and dynamic
– …But somehow hard: poorly documented and buggy if having
bad implementation
– You should check Firefox's code to understand
– It takes me almost 20 hours to make <tree> on NicoFox works
and another 20 hours to fix bugs…
• Are you ready? :D
https://developer.mozilla.org/en/XUL_Tutorial/Custom_Tree_
Views
http://www.xuldev.org/blog/?p=127 (Thank you again,
Gomita!)
49. Tree Selection / Box Object
• Tree Selection: Get currently selected items
https://developer.mozilla.org/en/XUL_Tutorial/Tr
ee_Selection
– A little like text selection we had mentioned at the
previous lecture (?)
• Box Objects: Handle Tree's display
Needed, for example, if you need to know the
which cell the cursor is at
https://developer.mozilla.org/en/XUL_Tutorial/Tr
ee_Box_Objects
50. Take a break, there is a final boss: SQLite!
LUNCH TIME AGAIN!
52. What is SQLite?
• A lightweight relational database
management system
• Its C/C++ library is very small (418KB in Firefox
3.5) and very easy-to-use
• Can use most of the SQL command
• Resulting database is a single file
• PUBLIC DOMAIN!(No any copyright restriction)
• Used by a lot of software / framework
53. SQLite is in …
• Firefox, Safari, Google Chrome
• Google Gears
• Photoshop lightroom, Adobe AIR
• Skype, McAfee, Songbird
• PHP, Python…
• iPhone, Symbian, Android…
• Almost everywhere!!!!
60. Simple way to display SQLite DB
• Use template
https://developer.mozilla.org/en/XUL/Templat
e_Guide/SQLite_Templates
• Simple, but useful for our demo! :D
• We will use the template technique and
<tree> in the following demo
62. Explain
• <treechildren datasources=quot;profile:tutorial.sqlitequot;
ref=quot;*quot; querytype=quot;storagequot;>
• Use SQLite database as a template datasources
• <template>: <query> and <action>
– <query>: SQL command
• SELECT * FROM students: Select all fields from students table
• ORDER BY score DESC: Order by score field in descendant order
– <action>: Applied template
• uri=quot;?quot;: (?)
• Read score field from ?score
63. mozStorage API
• Mozilla's way to multiply SQLite database
• So it is XPCOM again …
• But it is not so difficult!
• I don't want to talk in detail, so go straight to
examples!
65. A SQL preview
• INSERT INTO students (name, score) VALUES
(quot;Littlebtcquot;, 30)
• Insert a data into students table
• Two fields: name and score
• Their values: Littlebtc and 30
67. student.js: student.add();
/* use nsIPromptService to alert */
/*
https://developer.mozilla.org/en/Code_snippets/Dialogs_and_Prom
pts */
var prompts = Cc[quot;@mozilla.org/embedcomp/prompt-service;1quot;]
.getService(Ci.nsIPromptService);
/* Record user's choice */
var name = document.getElementById('new-name').value;
var score = document.getElementById('new-score').value;
/* Required to enter a name */
if (!name) {
prompts.alert(null, 'Error', 'Please Enter a Name.');
return;
}
68. …continued
try { // try-catch exception handling
/* We will see here later */
} catch(e) { // There may be some error in XPCOM, we can handle it
prompts.alert(null, 'Error', 'Database/XPCOM Error:' + e);
}
• XPCOM Will throw exception when error
• So we need a good way to handle it
• …Or it will be displayed in the error console
69. Connect to DB
/* Find the tutorial.sqlite */
var file = Components.classes[quot;@mozilla.org/file/directory_service;1quot;]
.getService(Components.interfaces.nsIProperties)
.get(quot;ProfDquot;, Components.interfaces.nsIFile);
file.append(quot;tutorial.sqlitequot;);
/* Open database */
var storageService = Cc[quot;@mozilla.org/storage/service;1quot;]
.getService(Ci.mozIStorageService);
var db = storageService.openDatabase(file);
70. Execute a quot;statementquot;
var sql = quot;INSERT INTO students (name, score) VALUES
(?1, ?2)quot;;
var statement = db.createStatement(sql);
statement.bindUTF8StringParameter(0, name);
statement.bindInt32Parameter(1, score);
/* Execute the statement */
statement.execute();
/* Finallize */
statement.finalize();
• quot;Statementquot; is a SQL command in mozStorage
• Why we should use the quot;bindingquot; way?
Prevent SQL injection, make database safe
• https://developer.mozilla.org/en/mozIStorageStatemen
t#Binding_Functions
72. After try{…} catch{…}
• We should update the tree and show a
message
/* Update the tree */
document.getElementById('students-treechildren')
.builder.rebuild();
/* Prompt the user */
prompts.alert(null, 'OK', 'Thank you!');
74. How to do for you later
• Understand how to CRUD (Create, Read,
Update, Delete) in SQLite
– SQL commands: INSERT, SELECT, UPDATE, DELETE
• Remember: Always Binding, NEVER directly
apply variables into SQL command strings
– Google quot;SQL Injectionquot; for details
• SQLite is simple but powerful – have fun!
75. This is the basic!
• Actually, SQLite storage access with <tree> is
usually implemented in Custom Tree View way,
both in Firefox and NicoFox
• That is another reason that I had spent 40 hours
on a tree
• Edit / Remove may be a very hard work