ext 0523

78
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.

Upload: littlebtc

Post on 15-Jan-2015

1.292 views

Category:

Technology


3 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Ext 0523

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.

Page 2: Ext 0523

Review

• Do you remember…?

– XUL and controls?

– Some JavaScript technique?

– install.rdf and chrome.manifest?

– Some XPCOM introduction?

Page 3: Ext 0523

Preference System

Make options for your extension

Page 4: Ext 0523

Why preferences?

• Provide options to user

– Which can be set in an option window

– Or in the program operation

• Record user's choice

Page 5: Ext 0523

about:config

Page 6: Ext 0523

Available types

• String

• Integer

• Boolean: true / false

Page 7: Ext 0523

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("extensions.extensionname.preferencename", false);– Name: has a widely used naming convention

– Value: Boolean / Integer / String

Page 8: Ext 0523

Take an example

• See defaults/preferences/ tutorial.js

pref("extensions.tutorial.option1", false);

pref("extensions.tutorial.option2", "");

pref("extensions.tutorial.option3", 32);

pref("extensions.tutorial.default_editor", "");

Page 9: Ext 0523

See about:config

Page 10: Ext 0523

Preference System:<prefwindow>

• Way to make a "option window"

• 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.

Page 11: Ext 0523

Set install.rdf, again

<em:optionsURL>chrome://tutorial/content/options.xul</em:optionsURL>

• This will point the "Options" button of your extension to the corresponding XUL pages:

Page 12: Ext 0523

<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="paneGeneral" label="General" src="chrome://path/to/paneOverlay.xul"/>

Page 13: Ext 0523

<preferences> <preference>

• <preferences>: Container

• <preference>: "Magic Attribute" to hold the actual preference, attributes:

– id

– name: Corresponding preference name

– type: bool/string/unichar/int/file

• In controls, use preference="id" to connect the magic!

Page 14: Ext 0523

Example: options.xul

<?xml version="1.0" encoding="UTF-8"?>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<?xml-stylesheet href="chrome://mozapps/content/preferences/preferences.css"?>

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

<prefwindow xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<prefpane id="panel1" label="panel1">

<preferences>

<preference name="extensions.tutorial.option1" id="extensions.tutorial.option1" type="bool" />

<preference name="extensions.tutorial.option2" id="extensions.tutorial.option2" type="unichar" />

</preferences>

<checkbox id="option1" preference="extensions.tutorial.option1" label="Check me!" />

<hbox>

<label value="Type Your Name:" accesskey="N" control="option2" />

<textbox id="option2" preference="extensions.tutorial.option2" />

</hbox>

</prefpane>

<prefpane id="panel2" label="panel2" src="chrome://tutorial/content/panel2.xul"/>

</prefwindow>

Page 15: Ext 0523

Example: panel2.xul

<?xml version="1.0" encoding="UTF-8"?>

<overlay id="Pane2Overlay"

xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<prefpane id="panel2">

<preferences>

<preference name="extensions.tutorial.option3" id="extensions.tutorial.option3" type="int" />

</preferences>

<hbox>

<label value="Type a Integer:" accesskey="I" control="option3" />

<textbox id="option3" preference="extensions.tutorial.option3" type="number" />

</hbox>

</prefpane>

</overlay>

Page 16: Ext 0523

Result

Page 17: Ext 0523

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["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch);

// Get the "extensions.tutorial." branch

var prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService);

prefs = prefs.getBranch("extensions.tutorial.");

Page 18: Ext 0523

Part 2: Read/Write in XPCOM

• Then, read/write

• getBoolPref(), setBoolPref()

• getCharPref(), setCharPref()

– BEWARE: Not Unicode Compatible!

• getIntPref() and setIntPref()

Page 19: Ext 0523

Part 2: Read/Write in XPCOM

Examples to run in JavaScript Shell:// Get the "extensions.tutorial." branch

var prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService);

prefs = prefs.getBranch("extensions.tutorial.");

// 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);

Page 20: Ext 0523

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("option2", Components.interfaces.nsISupportsString).data;

alert(option2);

// Example 2: setting Unicode value

var str = Components.classes["@mozilla.org/supports-string;1"]

.createInstance(Components.interfaces.nsISupportsString);

str.data = "火狐";

prefs.setComplexValue("option2",

Components.interfaces.nsISupportsString, str);

Page 21: Ext 0523

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 "Exit Firefox":

Page 22: Ext 0523

File Processing

Access local files

https://developer.mozilla.org/en/Code_snippets/File_I%2f%2fO

Page 23: Ext 0523

The nsIFile / nsILocalFile

• nsIFile: Cross-platform File I/O interface• nsILocalFile: extends nsIFile, can access local

filesystem• It is a "LOCATION INDICATOR"• 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…

Page 24: Ext 0523

"Create" a nsILocalFile

• With a absolute path:var file = Components.classes["@mozilla.org/file/local;1"]

.createInstance(Components.interfaces.nsILocalFile);

file.initWithPath("C:\\");

• "Path" should be in a "native" waylike /home/littlebtc in Linux, C:\ in Windows

• file:// URI? Explained later

Page 25: Ext 0523

"Create" nsIFile pointing to special folders

• Get an object located to Profile Directory:// get profile directory

var file = Components.classes["@mozilla.org/file/directory_service;1"]

.getService(Components.interfaces.nsIProperties)

.get("ProfD", Components.interfaces.nsIFile);

• You can replace the ProfD with:With… Where With… Where

CurProcD (Firefox) Installation Dir

Desk DesktopC:\Users\Littlebtc\Desktop

Home User's DirC:\Users\Littlebtc/home/littlebtc/

Progs "Programs" directory inWindows Start Menu

resource:app XULRunner App Directory

Page 26: Ext 0523

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()

Page 27: Ext 0523

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!)

Page 28: Ext 0523

How to…

• "Open the file" by the OS?• file.launch();

• Do something like "Open containing folder" in download manager?• file.reveal();

• Both of them are not work on Unix-like!

Page 29: Ext 0523

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

Page 30: Ext 0523

How to…

• How to "run the file" 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…

Page 31: Ext 0523

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("DIR"); // Still, append first

if( !file.exists() || !file.isDirectory() ) { // if it doesn't exist, createfile.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,

0777);}

Page 32: Ext 0523

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

Page 33: Ext 0523

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!

Page 34: Ext 0523

File I/O demo 1

• A run dialog like Windows, use undocumented <filefield>

Page 35: Ext 0523

run.xul

<?xml version="1.0" encoding="UTF-8"?>

<?xml-stylesheet href="chrome://global/skin" type="text/css"?>

<dialog title="Run application" buttons="accept,cancel" ondialogaccept="runApp.go();"

xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script type="application/x-javascript" src="chrome://tutorial/content/run.js"/>

<hbox>

<label value="Select an file:" accesskey="F" /> <filefield id="file" />

<button label="Browse..." oncommand="runApp.browse();" />

</hbox>

<grid>

<columns> <column flex="1" /> <column flex="2" /></columns>

<rows>

<row> <label value="File name:" /><label value="" id="file_name" /> </row>

<row> <label value="File size:" /><label value="" id="file_size" /> </row>

</rows>

</grid>

</dialog>

Page 36: Ext 0523

run.js (partial)browse: function() {

// Initialize file picker

var fp = Cc["@mozilla.org/filepicker;1"].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';

}

},

Page 37: Ext 0523

run.js (partial 2)

go: function() {

if (this.file && this.file.exists() && this.file.isFile()) {

this.file.launch();

}

}

Page 38: Ext 0523

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["@mozilla.org/network/io-service;1"].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["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newFileURI(file); // Assuming file is a nsIFIle

– var file = file_uri.QueryInterface(Components.interfaces.nsIFileURL).file;

Page 39: Ext 0523

When you will need nsIURI / nsIFile

• Downloading fileshttps://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 listenershttps://developer.mozilla.org/en/Code_snippets/Progress_Listeners

• And much more…

Page 40: Ext 0523

Tree

A somehow complex XUL structure

Page 41: Ext 0523

What is a tree?

• A complex item lists

• An item may have some childs

Page 42: Ext 0523

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

Page 43: Ext 0523

The easiest: Content Tree

• (Try XUL Editor!)• From https://developer.mozilla.org/En/XUL/Tree

<tree flex="1" rows="2"><treecols><treecol id="sender" label="Sender" flex="1"/> <treecol id="subject" label="Subject" flex="2"/>

</treecols><treechildren><treeitem><treerow>

<treecell label="[email protected]"/> <treecell label="Top secret plans"/></treerow>

</treeitem><treeitem><treerow>

<treecell label="[email protected]"/> <treecell label="Let's do lunch"/></treerow>

</treeitem></treechildren>

</tree>

Page 44: Ext 0523

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

Page 45: Ext 0523

A tree with nested itemsFrom https://developer.mozilla.org/En/XUL/Tree

<tree id="myTree" flex="1" hidecolumnpicker="false" seltype="single" class="tree" rows="5">

<treecols id="myTree2-treeCols">

<treecol id="myTree2-treeCol0" primary="true" flex="2" label="Column A" persist="width" ordinal="1"/>

<splitter class="tree-splitter" ordinal="2"/>

<treecol id="myTree2-treeCol1" flex="1" label="Column B" persist="width" ordinal="3"/>

</treecols>

<treechildren>

<treeitem>

<treerow>

<treecell label="1"/> <treecell label="a"/>

</treerow>

</treeitem>

<!-- Make sure to set container="true" -->

<treeitem container="true" open="true">

<treerow>

<treecell label="2"/> <treecell label="b"/>

</treerow>

<treechildren>

<treeitem>

<treerow>

<treecell label="2a"/> <treecell label="ba"/>

</treerow>

</treeitem>

</treechildren>

</treeitem>

</treechildren>

</tree>

Page 46: Ext 0523

Wait, a lot of new thing…?

• <splitter>: Between columns, to make them resizable

• Nested items: <treechildren> under <treeitem>

• hidecolumnpicker="false"– Whether to hide the column picker on the right

– seltype="single": you can select only one itemor seltype="multiple" for multiple selection support

• persist="attributename1 attributename2"– Make Firefox "remember" user's settings on specific

attribute

Page 47: Ext 0523

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

Page 48: Ext 0523

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? :Dhttps://developer.mozilla.org/en/XUL_Tutorial/Custom_Tree_

Viewshttp://www.xuldev.org/blog/?p=127 (Thank you again,

Gomita!)

Page 49: Ext 0523

Tree Selection / Box Object

• Tree Selection: Get currently selected itemshttps://developer.mozilla.org/en/XUL_Tutorial/Tree_Selection– A little like text selection we had mentioned at the

previous lecture (?)

• Box Objects: Handle Tree's displayNeeded, for example, if you need to know the which cell the cursor is athttps://developer.mozilla.org/en/XUL_Tutorial/Tree_Box_Objects

Page 50: Ext 0523

LUNCH TIME AGAIN!Take a break, there is a final boss: SQLite!

Page 51: Ext 0523

Storage: SQLite

Great storage choice

Page 52: Ext 0523

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

Page 53: Ext 0523

SQLite is in …

• Firefox, Safari, Google Chrome

• Google Gears

• Photoshop lightroom, Adobe AIR

• Skype, McAfee, Songbird

• PHP, Python…

• iPhone, Symbian, Android…

• Almost everywhere!!!!

Page 54: Ext 0523

Now, make a new database!

Page 55: Ext 0523

Now, make a new database!

• Find your profile dir and copy the path Orz

Page 56: Ext 0523

Now, make a new database!

• Close SQLite manager and restart:

Page 57: Ext 0523

Create a table

Page 58: Ext 0523

Table is ready!

Page 59: Ext 0523

Add a record

Page 60: Ext 0523

Simple way to display SQLite DB

• Use templatehttps://developer.mozilla.org/en/XUL/Template_Guide/SQLite_Templates

• Simple, but useful for our demo! :D

• We will use the template technique and <tree> in the following demo

Page 61: Ext 0523

students.xul

<tree flex="1"><treecols>

<treecol id="col-no" label="No" flex="1"/><treecol id="col-name" label="Name" flex="1"/><treecol id="col-score" label="Score" flex="1"/>

</treecols><treechildren datasources="profile:tutorial.sqlite" ref="*" querytype="storage">

<template><query>

SELECT * FROM students ORDER BY score DESC</query><action>

<treeitem uri="?"><treerow>

<treecell label="?no"/><treecell label="?name"/><treecell label="?score"/>

</treerow></treeitem>

</action></template>

</treechildren></tree>

Page 62: Ext 0523

Explain

• <treechildren datasources="profile:tutorial.sqlite" ref="*" querytype="storage">

• 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="?": (?)

• Read score field from ?score

Page 63: Ext 0523

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!

Page 64: Ext 0523

Final Demo:"Add a record" function

Page 65: Ext 0523

A SQL preview

• INSERT INTO students (name, score) VALUES ("Littlebtc", 30)

• Insert a data into students table

• Two fields: name and score

• Their values: Littlebtc and 30

Page 66: Ext 0523

students.xul, again

<hbox>

<label value="Student Name:" accesskey="N" control="new_name" />

<textbox id="new_name" style="width: 8em;"/>

<label value="Score:" accesskey="S" control="new_score" />

<textbox id="new_score" type="number" style="width: 4em;" max="100" min="0"/>

<button label="Add" oncommand="students.add();"/>

</hbox>

Page 67: Ext 0523

student.js: student.add();

/* use nsIPromptService to alert */

/* https://developer.mozilla.org/en/Code_snippets/Dialogs_and_Prompts */

var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]

.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;

}

Page 68: Ext 0523

…continuedtry { // 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

Page 69: Ext 0523

Connect to DB

/* Find the tutorial.sqlite */

var file = Components.classes["@mozilla.org/file/directory_service;1"]

.getService(Components.interfaces.nsIProperties)

.get("ProfD", Components.interfaces.nsIFile);

file.append("tutorial.sqlite");

/* Open database */

var storageService = Cc["@mozilla.org/storage/service;1"]

.getService(Ci.mozIStorageService);

var db = storageService.openDatabase(file);

Page 70: Ext 0523

Execute a "statement"

var sql = "INSERT INTO students (name, score) VALUES (?1, ?2)";

var statement = db.createStatement(sql);statement.bindUTF8StringParameter(0, name);statement.bindInt32Parameter(1, score);/* Execute the statement */statement.execute();/* Finallize */statement.finalize();

• "Statement" is a SQL command in mozStorage• Why we should use the "binding" way?

Prevent SQL injection, make database safe• https://developer.mozilla.org/en/mozIStorageStatemen

t#Binding_Functions

Page 71: Ext 0523

Finally…

• Don't forget to close your DB!

db.close();

Page 72: Ext 0523

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!');

Page 73: Ext 0523

Working Example

Page 74: Ext 0523

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 "SQL Injection" for details

• SQLite is simple but powerful – have fun!

Page 75: Ext 0523

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

Page 76: Ext 0523

THANK YOU!It is 5:34am, I had finished my slide

Page 77: Ext 0523

…….NOT YET!!!

Page 78: Ext 0523