Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Ext 0523


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Ext 0523

  1. 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. 2. Review • Do you remember…? – XUL and controls? – Some JavaScript technique? – install.rdf and chrome.manifest? – Some XPCOM introduction?
  3. 3. Preference System Make options for your extension
  4. 4. Why preferences? • Provide options to user – Which can be set in an option window – Or in the program operation • Record user's choice
  5. 5. about:config
  6. 6. Available types • String • Integer • Boolean: true / false
  7. 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. 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;);
  9. 9. See about:config
  10. 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.
  11. 11. Set install.rdf, again <em:optionsURL>chrome://tutorial/content/opt ions.xul</em:optionsURL> • This will point the quot;Optionsquot; button of your extension to the corresponding XUL pages:
  12. 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. 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!
  14. 14. Example: options.xul <?xml version=quot;1.0quot; encoding=quot;UTF-8quot;?> <?xml-stylesheet href=quot;chrome://global/skin/quot; type=quot;text/cssquot;?> <?xml-stylesheet href=quot;chrome://mozapps/content/preferences/preferences.cssquot;?> <?xml-stylesheet href=quot;chrome://browser/skin/preferences/preferences.cssquot;?> <prefwindow xmlns=quot;;> <prefpane id=quot;panel1quot; label=quot;panel1quot;> <preferences> <preference name=quot;extensions.tutorial.option1quot; id=quot;extensions.tutorial.option1quot; type=quot;boolquot; /> <preference name=quot;extensions.tutorial.option2quot; id=quot;extensions.tutorial.option2quot; type=quot;unicharquot; /> </preferences> <checkbox id=quot;option1quot; preference=quot;extensions.tutorial.option1quot; label=quot;Check me!quot; /> <hbox> <label value=quot;Type Your Name:quot; accesskey=quot;Nquot; control=quot;option2quot; /> <textbox id=quot;option2quot; preference=quot;extensions.tutorial.option2quot; /> </hbox> </prefpane> <prefpane id=quot;panel2quot; label=quot;panel2quot; src=quot;chrome://tutorial/content/panel2.xulquot;/> </prefwindow>
  15. 15. Example: panel2.xul <?xml version=quot;1.0quot; encoding=quot;UTF-8quot;?> <overlay id=quot;Pane2Overlayquot; xmlns=quot;;> <prefpane id=quot;panel2quot;> <preferences> <preference name=quot;extensions.tutorial.option3quot; id=quot;extensions.tutorial.option3quot; type=quot;intquot; /> </preferences> <hbox> <label value=quot;Type a Integer:quot; accesskey=quot;Iquot; control=quot;option3quot; /> <textbox id=quot;option3quot; preference=quot;extensions.tutorial.option3quot; type=quot;numberquot; /> </hbox> </prefpane> </overlay>
  16. 16. Result
  17. 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;;1quot;] .getService(Components.interfaces.nsIPrefBranch); // Get the quot;extensions.tutorial.quot; branch var prefs = Components.classes[quot;;1quot;] .getService(Components.interfaces.nsIPrefService); prefs = prefs.getBranch(quot;extensions.tutorial.quot;);
  18. 18. Part 2: Read/Write in XPCOM • Then, read/write • getBoolPref(), setBoolPref() • getCharPref(), setCharPref() – BEWARE: Not Unicode Compatible! • getIntPref() and setIntPref()
  19. 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;;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. 20. Part 2: Read/Write in XPCOM • Read/Write Unicode Strings: (from // 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;;1quot;] .createInstance(Components.interfaces.nsISupportsString); = quot;火狐quot;; prefs.setComplexValue(quot;option2quot;, Components.interfaces.nsISupportsString, str);
  21. 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;:
  22. 22. File Processing Access local files
  23. 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. 24. quot;Createquot; a nsILocalFile • With a absolute path: var file = Components.classes[quot;;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. 25. quot;Createquot; nsIFile pointing to special folders • Get an object located to Profile Directory: // get profile directory var file = Components.classes[quot;;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. 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. 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. 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. 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. 30. How to… • How to quot;run the filequot; if it is a executable? – file.launch(); (Not recommended) – Use nsIProcess: Running_applications – nsIProcess cannot accept Unicode parameters on Windows… 
  31. 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. 32. File contents read/write • File_I%2f%2fO • A little complex, why? When it comes to the encoding, it will be complex 
  33. 33. File Picker / <filefield> • File Open / File Save / Select Folder Dialog • See • <filefield> in the <prefwindow>, like Firefox options (UNDOCUMENTED): • We will have a small demo!
  34. 34. File I/O demo 1 • A run dialog like Windows, use undocumented <filefield>
  35. 35. run.xul <?xml version=quot;1.0quot; encoding=quot;UTF-8quot;?> <?xml-stylesheet href=quot;chrome://global/skinquot; type=quot;text/cssquot;?> <dialog title=quot;Run applicationquot; buttons=quot;accept,cancelquot; ondialogaccept=quot;runApp.go();quot; xmlns=quot;;> <script type=quot;application/x-javascriptquot; src=quot;chrome://tutorial/content/run.jsquot;/> <hbox> <label value=quot;Select an file:quot; accesskey=quot;Fquot; /> <filefield id=quot;filequot; /> <button label=quot;Browse...quot; oncommand=quot;runApp.browse();quot; /> </hbox> <grid> <columns> <column flex=quot;1quot; /> <column flex=quot;2quot; /></columns> <rows> <row> <label value=quot;File name:quot; /><label value=quot;quot; id=quot;file_namequot; /> </row> <row> <label value=quot;File size:quot; /><label value=quot;quot; id=quot;file_sizequot; /> </row> </rows> </grid> </dialog>
  36. 36. run.js (partial) browse: function() { // Initialize file picker var fp = Cc[quot;;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 =; 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'; } },
  37. 37. run.js (partial 2) go: function() { if (this.file && this.file.exists() && this.file.isFile()) { this.file.launch(); } }
  38. 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;;1quot;] .getService(Components.interfaces.nsIIOService) .newURI('', null, null); – uri.spec will contain the string representation of the URI • nsIURI <-> nsIFile – var file_uri = Components.classes[quot;;1quot;] .getService(Components.interfaces.nsIIOService) .newFileURI(file); // Assuming file is a nsIFIle – var file = file_uri.QueryInterface(Components.interfaces.nsIFileURL) .file;
  39. 39. When you will need nsIURI / nsIFile • Downloading files Downloading_Files (Basic) (Advanced, by Gomita, a good intro to nsIWebBrowserPersist) • Use Places API (Bookmarks, Tags, History) • Progress listeners Progress_Listeners • And much more…
  40. 40. Tree A somehow complex XUL structure
  41. 41. What is a tree? • A complex item lists • An item may have some childs
  42. 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
  43. 43. The easiest: Content Tree • (Try XUL Editor!) • From <tree flex=quot;1quot; rows=quot;2quot;> <treecols> <treecol id=quot;senderquot; label=quot;Senderquot; flex=quot;1quot;/> <treecol id=quot;subjectquot; label=quot;Subjectquot; flex=quot;2quot;/> </treecols> <treechildren> <treeitem> <treerow> <treecell label=quot;joe@somewhere.comquot;/> <treecell label=quot;Top secret plansquot;/> </treerow> </treeitem> <treeitem> <treerow> <treecell label=quot;mel@whereever.comquot;/> <treecell label=quot;Let's do lunchquot;/> </treerow> </treeitem> </treechildren> </tree>
  44. 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
  45. 45. A tree with nested items From <tree id=quot;myTreequot; flex=quot;1quot; hidecolumnpicker=quot;falsequot; seltype=quot;singlequot; class=quot;treequot; rows=quot;5quot;> <treecols id=quot;myTree2-treeColsquot;> <treecol id=quot;myTree2-treeCol0quot; primary=quot;truequot; flex=quot;2quot; label=quot;Column Aquot; persist=quot;widthquot; ordinal=quot;1quot;/> <splitter class=quot;tree-splitterquot; ordinal=quot;2quot;/> <treecol id=quot;myTree2-treeCol1quot; flex=quot;1quot; label=quot;Column Bquot; persist=quot;widthquot; ordinal=quot;3quot;/> </treecols> <treechildren> <treeitem> <treerow> <treecell label=quot;1quot;/> <treecell label=quot;aquot;/> </treerow> </treeitem> <!-- Make sure to set container=quot;truequot; --> <treeitem container=quot;truequot; open=quot;truequot;> <treerow> <treecell label=quot;2quot;/> <treecell label=quot;bquot;/> </treerow> <treechildren> <treeitem> <treerow> <treecell label=quot;2aquot;/> <treecell label=quot;baquot;/> </treerow> </treeitem> </treechildren> </treeitem> </treechildren> </tree>
  46. 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. 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. 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 Views (Thank you again, Gomita!)
  49. 49. Tree Selection / Box Object • Tree Selection: Get currently selected items 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 ee_Box_Objects
  50. 50. Take a break, there is a final boss: SQLite! LUNCH TIME AGAIN!
  51. 51. Storage: SQLite Great storage choice
  52. 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. 53. SQLite is in … • Firefox, Safari, Google Chrome • Google Gears • Photoshop lightroom, Adobe AIR • Skype, McAfee, Songbird • PHP, Python… • iPhone, Symbian, Android… • Almost everywhere!!!!
  54. 54. Now, make a new database!
  55. 55. Now, make a new database! • Find your profile dir and copy the path Orz
  56. 56. Now, make a new database! • Close SQLite manager and restart:
  57. 57. Create a table
  58. 58. Table is ready!
  59. 59. Add a record
  60. 60. Simple way to display SQLite DB • Use template e_Guide/SQLite_Templates • Simple, but useful for our demo! :D • We will use the template technique and <tree> in the following demo
  61. 61. students.xul <tree flex=quot;1quot;> <treecols> <treecol id=quot;col-noquot; label=quot;Noquot; flex=quot;1quot;/> <treecol id=quot;col-namequot; label=quot;Namequot; flex=quot;1quot;/> <treecol id=quot;col-scorequot; label=quot;Scorequot; flex=quot;1quot;/> </treecols> <treechildren datasources=quot;profile:tutorial.sqlitequot; ref=quot;*quot; querytype=quot;storagequot;> <template> <query> SELECT * FROM students ORDER BY score DESC </query> <action> <treeitem uri=quot;?quot;> <treerow> <treecell label=quot;?noquot;/> <treecell label=quot;?namequot;/> <treecell label=quot;?scorequot;/> </treerow> </treeitem> </action> </template> </treechildren> </tree>
  62. 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. 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!
  64. 64. Final Demo: quot;Add a recordquot; function
  65. 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
  66. 66. students.xul, again <hbox> <label value=quot;Student Name:quot; accesskey=quot;Nquot; control=quot;new_namequot; /> <textbox id=quot;new_namequot; style=quot;width: 8em;quot;/> <label value=quot;Score:quot; accesskey=quot;Squot; control=quot;new_scorequot; /> <textbox id=quot;new_scorequot; type=quot;numberquot; style=quot;width: 4em;quot; max=quot;100quot; min=quot;0quot;/> <button label=quot;Addquot; oncommand=quot;students.add();quot;/> </hbox>
  67. 67. student.js: student.add(); /* use nsIPromptService to alert */ /* pts */ var prompts = Cc[quot;;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. 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. 69. Connect to DB /* Find the tutorial.sqlite */ var file = Components.classes[quot;;1quot;] .getService(Components.interfaces.nsIProperties) .get(quot;ProfDquot;, Components.interfaces.nsIFile); file.append(quot;tutorial.sqlitequot;); /* Open database */ var storageService = Cc[quot;;1quot;] .getService(Ci.mozIStorageService); var db = storageService.openDatabase(file);
  70. 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 • t#Binding_Functions
  71. 71. Finally… • Don't forget to close your DB! db.close();
  72. 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!');
  73. 73. Working Example
  74. 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. 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
  76. 76. It is 5:34am, I had finished my slide  THANK YOU!
  77. 77. …….NOT YET!!!