22 * This file is provided by the addon-developer-support repository at
33 * https://github.com/thundernest/addon-developer-support
44 *
5+ * Version: 1.30
6+ * - replace setCharPref by setStringPref to cope with URTF-8 encoding
7+ *
8+ * Version: 1.29
9+ * - open options window centered
10+ *
11+ * Version: 1.28
12+ * - do not crash on missing icon
13+ *
14+ * Version: 1.27
15+ * - add openOptionsDialog()
16+ *
17+ * Version: 1.26
18+ * - pass WL object to legacy preference window
19+ *
20+ * Version: 1.25
21+ * - adding waitForMasterPassword
22+ *
23+ * Version: 1.24
24+ * - automatically localize i18n locale strings in injectElements()
25+ *
26+ * Version: 1.22
27+ * - to reduce confusions, only check built-in URLs as add-on URLs cannot
28+ * be resolved if a temp installed add-on has bin zipped
29+ *
530 * Version: 1.21
631 * - print debug messages only if add-ons are installed temporarily from
732 * the add-on debug page
@@ -71,6 +96,19 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
7196 if ( this . debug ) console . error ( "WindowListener API: " + msg ) ;
7297 }
7398
99+ // async sleep function using Promise
100+ async sleep ( delay ) {
101+ let timer = Components . classes [ "@mozilla.org/timer;1" ] . createInstance ( Components . interfaces . nsITimer ) ;
102+ return new Promise ( function ( resolve , reject ) {
103+ let event = {
104+ notify : function ( timer ) {
105+ resolve ( ) ;
106+ }
107+ }
108+ timer . initWithCallback ( event , delay , Components . interfaces . nsITimer . TYPE_ONE_SHOT ) ;
109+ } ) ;
110+ }
111+
74112 getAPI ( context ) {
75113 // track if this is the background/main context
76114 this . isBackgroundContext = ( context . viewType == "background" ) ;
@@ -98,8 +136,17 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
98136 return {
99137 WindowListener : {
100138
139+ async waitForMasterPassword ( ) {
140+ // Wait until master password has been entered (if needed)
141+ while ( ! Services . logins . isLoggedIn ) {
142+ self . log ( "Waiting for master password." ) ;
143+ await self . sleep ( 1000 ) ;
144+ }
145+ self . log ( "Master password has been entered." ) ;
146+ } ,
147+
101148 aDocumentExistsAt ( uriString ) {
102- self . log ( "Checking if document at <" + uriString + "> used in registration actually exits ." ) ;
149+ self . log ( "Checking if document at <" + uriString + "> used in registration actually exists ." ) ;
103150 try {
104151 let uriObject = Services . io . newURI ( uriString ) ;
105152 let content = Cu . readUTF8URI ( uriObject ) ;
@@ -114,30 +161,18 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
114161 self . pathToOptionsPage = optionsUrl . startsWith ( "chrome://" )
115162 ? optionsUrl
116163 : context . extension . rootURI . resolve ( optionsUrl ) ;
117-
118- if ( self . debug && ! this . aDocumentExistsAt ( self . pathToOptionsPage ) ) {
119- self . error ( "Attempt to register non-existent options page: " + self . pathToOptionsPage
120- + ( ( optionsUrl != self . pathToOptionsPage ) ? "\n(user provided options page was: " + optionsUrl + ")" : "" ) ) ;
121- self . pathToOptionsPage = null ;
122- }
123164 } ,
124165
125166 registerDefaultPrefs ( defaultUrl ) {
126167 let url = context . extension . rootURI . resolve ( defaultUrl ) ;
127168
128- if ( self . debug && ! this . aDocumentExistsAt ( url ) ) {
129- self . error ( "Attempt to register non-existent default prefs script: " + url
130- + ( ( url != defaultUrl ) ? "\n(user provided script path was: " + defaultUrl + ")" : "" ) ) ;
131- return ;
132- }
133-
134169 let prefsObj = { } ;
135170 prefsObj . Services = ChromeUtils . import ( "resource://gre/modules/Services.jsm" ) . Services ;
136171 prefsObj . pref = function ( aName , aDefault ) {
137172 let defaults = Services . prefs . getDefaultBranch ( "" ) ;
138173 switch ( typeof aDefault ) {
139174 case "string" :
140- return defaults . setCharPref ( aName , aDefault ) ;
175+ return defaults . setStringPref ( aName , aDefault ) ;
141176
142177 case "number" :
143178 return defaults . setIntPref ( aName , aDefault ) ;
@@ -205,12 +240,6 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
205240 ? jsFile
206241 : context . extension . rootURI . resolve ( jsFile )
207242
208- if ( self . debug && ! this . aDocumentExistsAt ( path ) ) {
209- self . error ( "Attempt to register a non-existent injector script: " + path
210- + "\nfor window " + windowHref
211- + ( ( path != jsFile ) ? "\n(user provided script path was: " + jsFile + " )" : "" ) ) ;
212- return ;
213- }
214243 self . registeredWindows [ windowHref ] = path ;
215244 } else {
216245 self . error ( "Window <" + windowHref + "> has already been registered" ) ;
@@ -224,12 +253,6 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
224253 self . pathToStartupScript = aPath . startsWith ( "chrome://" )
225254 ? aPath
226255 : context . extension . rootURI . resolve ( aPath ) ;
227-
228- if ( self . debug && ! this . aDocumentExistsAt ( self . pathToStartupScript ) ) {
229- self . error ( "Attempt to register non-existent startup script: " + self . pathToStartupScript
230- + ( ( aPath != self . pathToStartupScript ) ? "\n(user provided script path was: " + aPath + ")" : "" ) ) ;
231- self . pathToStartupScript = null ;
232- }
233256 } ,
234257
235258 registerShutdownScript ( aPath ) {
@@ -239,28 +262,19 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
239262 self . pathToShutdownScript = aPath . startsWith ( "chrome://" )
240263 ? aPath
241264 : context . extension . rootURI . resolve ( aPath ) ;
265+ } ,
242266
243- if ( self . debug && ! this . aDocumentExistsAt ( self . pathToShutdownScript ) ) {
244- self . error ( "Attempt to register non-existent shutdown script: " + self . pathToShutdownScript
245- + ( ( aPath != self . pathToShutdownScript ) ? "(user provided script path was: " + aPath + ")" : "" ) ) ;
246- self . pathToShutdownScript = null ;
247- }
267+ openOptionsDialog ( windowId ) {
268+ let window = context . extension . windowManager . get ( windowId , context ) . window
269+ let WL = { }
270+ WL . extension = self . extension ;
271+ WL . messenger = Array . from ( self . extension . views ) . find (
272+ view => view . viewType === "background" ) . xulBrowser . contentWindow
273+ . wrappedJSObject . browser ;
274+ window . openDialog ( self . pathToOptionsPage , "AddonOptions" , "chrome,resizable,centerscreen" , WL ) ;
248275 } ,
249276
250277 async startListening ( ) {
251- // async sleep function using Promise
252- async function sleep ( delay ) {
253- let timer = Components . classes [ "@mozilla.org/timer;1" ] . createInstance ( Components . interfaces . nsITimer ) ;
254- return new Promise ( function ( resolve , reject ) {
255- let event = {
256- notify : function ( timer ) {
257- resolve ( ) ;
258- }
259- }
260- timer . initWithCallback ( event , delay , Components . interfaces . nsITimer . TYPE_ONE_SHOT ) ;
261- } ) ;
262- } ;
263-
264278 if ( ! self . isBackgroundContext )
265279 throw new Error ( "The WindowListener API may only be called from the background page." ) ;
266280
@@ -332,16 +346,27 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
332346 let id = self . menu_addonPrefs_id + "_" + self . uniqueRandomID ;
333347
334348 // Get the best size of the icon (16px or bigger)
335- let iconSizes = Object . keys ( self . extension . manifest . icons ) ;
349+ let iconSizes = self . extension . manifest . icons
350+ ? Object . keys ( self . extension . manifest . icons )
351+ : [ ] ;
336352 iconSizes . sort ( ( a , b ) => a - b ) ;
337353 let bestSize = iconSizes . filter ( e => parseInt ( e ) >= 16 ) . shift ( ) ;
338354 let icon = bestSize ? self . extension . manifest . icons [ bestSize ] : "" ;
339355
340356 let name = self . extension . manifest . name ;
341- let entry = window . MozXULElement . parseXULToFragment (
342- `<menuitem class="menuitem-iconic" id="${ id } " image="${ icon } " label="${ name } " />` ) ;
357+ let entry = icon
358+ ? window . MozXULElement . parseXULToFragment (
359+ `<menuitem class="menuitem-iconic" id="${ id } " image="${ icon } " label="${ name } " />` )
360+ : window . MozXULElement . parseXULToFragment (
361+ `<menuitem id="${ id } " label="${ name } " />` ) ;
362+
343363 element_addonPrefs . appendChild ( entry ) ;
344- window . document . getElementById ( id ) . addEventListener ( "command" , function ( ) { window . openDialog ( self . pathToOptionsPage , "AddonOptions" ) } ) ;
364+ let WL = { }
365+ WL . extension = self . extension ;
366+ WL . messenger = Array . from ( self . extension . views ) . find (
367+ view => view . viewType === "background" ) . xulBrowser . contentWindow
368+ . wrappedJSObject . browser ;
369+ window . document . getElementById ( id ) . addEventListener ( "command" , function ( ) { window . openDialog ( self . pathToOptionsPage , "AddonOptions" , "chrome,resizable,centerscreen" , WL ) } ) ;
345370 } catch ( e ) {
346371 Components . utils . reportError ( e )
347372 }
@@ -361,7 +386,7 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
361386 // On my system it takes 70ms.
362387 let loaded = false ;
363388 for ( let i = 0 ; i < 100 && ! loaded ; i ++ ) {
364- await sleep ( 100 ) ;
389+ await self . sleep ( 100 ) ;
365390 let targetWindow = mutation . target . contentWindow . wrappedJSObject ;
366391 if ( targetWindow && targetWindow . location . href == mutation . target . getAttribute ( "src" ) && targetWindow . document . readyState == "complete" ) {
367392 loaded = true ;
@@ -415,7 +440,8 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
415440 if ( window . hasOwnProperty ( this . uniqueRandomID ) && this . registeredWindows . hasOwnProperty ( window . location . href ) ) {
416441 try {
417442 let uniqueRandomID = this . uniqueRandomID ;
418-
443+ let extension = this . extension ;
444+
419445 // Add reference to window to add-on scope
420446 window [ this . uniqueRandomID ] . window = window ;
421447 window [ this . uniqueRandomID ] . document = window . document ;
@@ -461,6 +487,10 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
461487 return null ;
462488 }
463489
490+ function localize ( entity ) {
491+ let msg = entity . slice ( "__MSG_" . length , - 2 ) ;
492+ return extension . localeData . localizeMessage ( msg )
493+ }
464494
465495 function injectChildren ( elements , container ) {
466496 if ( debug ) console . log ( elements ) ;
@@ -535,7 +565,8 @@ var WindowListener = class extends ExtensionCommon.ExtensionAPI {
535565 }
536566
537567 if ( debug ) console . log ( "Injecting into root document:" ) ;
538- injectChildren ( Array . from ( window . MozXULElement . parseXULToFragment ( xulString , dtdFiles ) . children ) , window . document . documentElement ) ;
568+ let localicedXulString = xulString . replace ( / _ _ M S G _ ( .* ?) _ _ / g, localize ) ;
569+ injectChildren ( Array . from ( window . MozXULElement . parseXULToFragment ( localicedXulString , dtdFiles ) . children ) , window . document . documentElement ) ;
539570
540571 for ( let bar of toolbarsToResolve ) {
541572 let currentset = Services . xulStore . getValue (
0 commit comments