2014-11-24 20:00:27 +01:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2014-12-17 21:33:53 +01:00
µBlock - a browser extension to block requests .
2014-11-24 20:00:27 +01:00
Copyright ( C ) 2014 The µBlock authors
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see { http : //www.gnu.org/licenses/}.
Home : https : //github.com/gorhill/uBlock
* /
2015-01-21 14:59:23 +01:00
/* jshint esnext: true, bitwise: false */
/* global self, Components, punycode */
2015-01-15 13:24:35 +01:00
2014-11-24 20:00:27 +01:00
// For background page
/******************************************************************************/
( function ( ) {
'use strict' ;
/******************************************************************************/
const { classes : Cc , interfaces : Ci , utils : Cu } = Components ;
2015-01-09 07:58:07 +01:00
const { Services } = Cu . import ( 'resource://gre/modules/Services.jsm' , null ) ;
2014-11-24 20:00:27 +01:00
/******************************************************************************/
2015-01-14 23:45:55 +01:00
var vAPI = self . vAPI = self . vAPI || { } ;
2014-11-24 20:00:27 +01:00
vAPI . firefox = true ;
2015-02-12 19:19:17 +01:00
vAPI . fennec = Components . classes [ "@mozilla.org/xre/app-info;1" ]
. getService ( Components . interfaces . nsIXULAppInfo )
. ID == "{aa3c5121-dab2-40e2-81ca-7ea25febc110}" ;
2014-11-24 20:00:27 +01:00
/******************************************************************************/
2014-12-02 08:35:25 +01:00
vAPI . app = {
2014-12-18 21:24:11 +01:00
name : 'µBlock' ,
2015-01-29 14:12:21 +01:00
version : location . hash . slice ( 1 )
2014-12-02 08:35:25 +01:00
} ;
/******************************************************************************/
2014-12-29 17:54:18 +01:00
vAPI . app . restart = function ( ) {
2015-01-04 13:58:17 +01:00
// Listening in bootstrap.js
Cc [ '@mozilla.org/childprocessmessagemanager;1' ]
. getService ( Ci . nsIMessageSender )
. sendAsyncMessage ( location . host + '-restart' ) ;
2014-12-29 17:54:18 +01:00
} ;
2014-12-07 20:51:49 +01:00
2014-12-16 13:44:34 +01:00
/******************************************************************************/
2014-12-28 21:26:06 +01:00
// List of things that needs to be destroyed when disabling the extension
// Only functions should be added to it
2014-12-16 13:44:34 +01:00
2015-01-12 20:39:23 +01:00
var cleanupTasks = [ ] ;
2014-12-07 20:51:49 +01:00
/******************************************************************************/
2014-11-27 20:45:54 +01:00
var SQLite = {
open : function ( ) {
var path = Services . dirsvc . get ( 'ProfD' , Ci . nsIFile ) ;
path . append ( 'extension-data' ) ;
2014-12-28 21:26:06 +01:00
if ( ! path . exists ( ) ) {
2014-11-27 20:45:54 +01:00
path . create ( Ci . nsIFile . DIRECTORY _TYPE , parseInt ( '0774' , 8 ) ) ;
}
2014-12-28 21:26:06 +01:00
if ( ! path . isDirectory ( ) ) {
2014-11-27 20:45:54 +01:00
throw Error ( 'Should be a directory...' ) ;
}
2014-12-18 11:59:04 +01:00
path . append ( location . host + '.sqlite' ) ;
2014-11-27 20:45:54 +01:00
this . db = Services . storage . openDatabase ( path ) ;
this . db . executeSimpleSQL (
'CREATE TABLE IF NOT EXISTS settings' +
'(name TEXT PRIMARY KEY NOT NULL, value TEXT);'
) ;
2014-12-16 13:44:34 +01:00
2015-01-12 17:45:09 +01:00
cleanupTasks . push ( function ( ) {
2014-12-18 11:59:04 +01:00
// VACUUM somewhere else, instead on unload?
2014-12-16 13:44:34 +01:00
SQLite . run ( 'VACUUM' ) ;
SQLite . db . asyncClose ( ) ;
} ) ;
2014-11-27 20:45:54 +01:00
} ,
2014-12-28 21:26:06 +01:00
2014-11-27 20:45:54 +01:00
run : function ( query , values , callback ) {
2014-12-28 21:26:06 +01:00
if ( ! this . db ) {
2014-11-27 20:45:54 +01:00
this . open ( ) ;
}
var result = { } ;
query = this . db . createAsyncStatement ( query ) ;
2014-12-28 21:26:06 +01:00
if ( Array . isArray ( values ) && values . length ) {
2014-11-27 20:45:54 +01:00
var i = values . length ;
2014-12-28 21:26:06 +01:00
while ( i -- ) {
2014-11-27 20:45:54 +01:00
query . bindByIndex ( i , values [ i ] ) ;
}
}
query . executeAsync ( {
handleResult : function ( rows ) {
2014-12-28 21:26:06 +01:00
if ( ! rows || typeof callback !== 'function' ) {
2014-11-27 20:45:54 +01:00
return ;
}
var row ;
2014-12-28 21:26:06 +01:00
while ( row = rows . getNextRow ( ) ) {
2014-11-27 20:45:54 +01:00
// we assume that there will be two columns, since we're
// using it only for preferences
result [ row . getResultByIndex ( 0 ) ] = row . getResultByIndex ( 1 ) ;
}
} ,
handleCompletion : function ( reason ) {
2014-12-28 21:26:06 +01:00
if ( typeof callback === 'function' && reason === 0 ) {
2014-11-27 20:45:54 +01:00
callback ( result ) ;
}
} ,
handleError : function ( error ) {
console . error ( 'SQLite error ' , error . result , error . message ) ;
}
} ) ;
}
} ;
/******************************************************************************/
vAPI . storage = {
QUOTA _BYTES : 100 * 1024 * 1024 ,
2014-12-28 21:26:06 +01:00
2014-12-02 08:35:25 +01:00
sqlWhere : function ( col , params ) {
2014-12-28 21:26:06 +01:00
if ( params > 0 ) {
2015-01-21 14:59:23 +01:00
params = new Array ( params + 1 ) . join ( '?, ' ) . slice ( 0 , - 2 ) ;
2014-12-02 08:35:25 +01:00
return ' WHERE ' + col + ' IN (' + params + ')' ;
2014-11-27 20:45:54 +01:00
}
return '' ;
} ,
2014-12-28 21:26:06 +01:00
2014-11-27 20:45:54 +01:00
get : function ( details , callback ) {
2014-12-28 21:26:06 +01:00
if ( typeof callback !== 'function' ) {
2014-11-27 20:45:54 +01:00
return ;
}
var values = [ ] , defaults = false ;
2014-12-28 21:26:06 +01:00
if ( details !== null ) {
if ( Array . isArray ( details ) ) {
2014-11-27 20:45:54 +01:00
values = details ;
2014-12-28 21:26:06 +01:00
} else if ( typeof details === 'object' ) {
2014-11-27 20:45:54 +01:00
defaults = true ;
values = Object . keys ( details ) ;
2014-12-28 21:26:06 +01:00
} else {
2014-11-27 20:45:54 +01:00
values = [ details . toString ( ) ] ;
}
}
SQLite . run (
'SELECT * FROM settings' + this . sqlWhere ( 'name' , values . length ) ,
values ,
function ( result ) {
var key ;
2014-12-28 21:26:06 +01:00
for ( key in result ) {
2014-11-27 20:45:54 +01:00
result [ key ] = JSON . parse ( result [ key ] ) ;
}
2014-12-28 21:26:06 +01:00
if ( defaults ) {
for ( key in details ) {
if ( result [ key ] === undefined ) {
2014-11-27 20:45:54 +01:00
result [ key ] = details [ key ] ;
}
}
}
callback ( result ) ;
}
) ;
} ,
2014-12-28 21:26:06 +01:00
2014-11-27 20:45:54 +01:00
set : function ( details , callback ) {
2014-12-02 08:35:25 +01:00
var key , values = [ ] , placeholders = [ ] ;
2014-11-27 20:45:54 +01:00
2014-12-28 21:26:06 +01:00
for ( key in details ) {
2015-01-18 19:50:20 +01:00
if ( ! details . hasOwnProperty ( key ) ) {
continue ;
}
2014-11-27 20:45:54 +01:00
values . push ( key ) ;
values . push ( JSON . stringify ( details [ key ] ) ) ;
2014-12-02 08:35:25 +01:00
placeholders . push ( '?, ?' ) ;
2014-11-27 20:45:54 +01:00
}
2014-12-28 21:26:06 +01:00
if ( ! values . length ) {
2014-11-27 20:45:54 +01:00
return ;
}
SQLite . run (
'INSERT OR REPLACE INTO settings (name, value) SELECT ' +
2014-12-02 08:35:25 +01:00
placeholders . join ( ' UNION SELECT ' ) ,
2014-11-27 20:45:54 +01:00
values ,
callback
) ;
} ,
2014-12-28 21:26:06 +01:00
2014-11-27 20:45:54 +01:00
remove : function ( keys , callback ) {
2014-12-28 21:26:06 +01:00
if ( typeof keys === 'string' ) {
2014-11-27 20:45:54 +01:00
keys = [ keys ] ;
}
SQLite . run (
'DELETE FROM settings' + this . sqlWhere ( 'name' , keys . length ) ,
keys ,
callback
) ;
} ,
2014-12-28 21:26:06 +01:00
2014-11-27 20:45:54 +01:00
clear : function ( callback ) {
2014-12-20 11:57:05 +01:00
SQLite . run ( 'DELETE FROM settings' ) ;
SQLite . run ( 'VACUUM' , null , callback ) ;
2014-11-27 20:45:54 +01:00
} ,
2014-12-28 21:26:06 +01:00
2014-11-27 20:45:54 +01:00
getBytesInUse : function ( keys , callback ) {
2014-12-28 21:26:06 +01:00
if ( typeof callback !== 'function' ) {
2014-11-27 20:45:54 +01:00
return ;
}
SQLite . run (
2014-12-02 08:35:25 +01:00
'SELECT "size" AS size, SUM(LENGTH(value)) FROM settings' +
2014-11-27 20:45:54 +01:00
this . sqlWhere ( 'name' , Array . isArray ( keys ) ? keys . length : 0 ) ,
keys ,
function ( result ) {
callback ( result . size ) ;
}
) ;
}
} ;
/******************************************************************************/
2014-12-02 08:35:25 +01:00
var windowWatcher = {
2014-12-16 18:09:55 +01:00
onReady : function ( e ) {
2014-12-28 21:26:06 +01:00
if ( e ) {
2014-12-16 18:09:55 +01:00
this . removeEventListener ( e . type , windowWatcher . onReady ) ;
2014-12-02 08:35:25 +01:00
}
2014-12-17 10:33:02 +01:00
var wintype = this . document . documentElement . getAttribute ( 'windowtype' ) ;
2014-12-02 08:35:25 +01:00
2014-12-28 21:26:06 +01:00
if ( wintype !== 'navigator:browser' ) {
2014-12-02 08:35:25 +01:00
return ;
}
2015-02-12 19:19:17 +01:00
if ( this . gBrowser && this . gBrowser . tabContainer ) {
// desktop Firefox
var tC = this . gBrowser . tabContainer ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
this . gBrowser . addTabsProgressListener ( tabWatcher ) ;
tC . addEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
tC . addEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
} else if ( this . BrowserApp && this . BrowserApp . deck ) {
// Fennec
var deck = this . BrowserApp . deck ;
deck . addEventListener ( 'DOMTitleChanged' , tabWatcher . onFennecLocationChange ) ;
deck . addEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
deck . addEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
}
2014-12-02 08:35:25 +01:00
2014-12-19 21:24:30 +01:00
vAPI . contextMenu . register ( this . document ) ;
2014-12-02 08:35:25 +01:00
2014-12-16 13:44:34 +01:00
// when new window is opened TabSelect doesn't run on the selected tab?
2014-12-02 08:35:25 +01:00
} ,
2014-12-28 21:26:06 +01:00
2014-12-02 08:35:25 +01:00
observe : function ( win , topic ) {
2014-12-28 21:26:06 +01:00
if ( topic === 'domwindowopened' ) {
2014-12-16 18:09:55 +01:00
win . addEventListener ( 'DOMContentLoaded' , this . onReady ) ;
2014-12-02 08:35:25 +01:00
}
}
} ;
/******************************************************************************/
2015-01-27 11:13:33 +01:00
var tabWatcher = {
onTabClose : function ( { target : tab } ) {
var tabId = vAPI . tabs . getTabId ( tab ) ;
vAPI . tabs . onClosed ( tabId ) ;
delete vAPI . toolbarButton . tabs [ tabId ] ;
} ,
onTabSelect : function ( { target : tab } ) {
2015-02-12 19:19:17 +01:00
var URI = null ;
if ( tab . currentURI ) {
// on Fennec the target is actually the linked browser
URI = tab . currentURI ;
} else {
// desktop Firefox
URI = tab . linkedBrowser . currentURI ;
}
2015-01-27 11:13:33 +01:00
var aboutPath = URI . schemeIs ( 'about' ) && URI . path ;
var tabId = vAPI . tabs . getTabId ( tab ) ;
if ( ! aboutPath || ( aboutPath !== 'blank' && aboutPath !== 'newtab' ) ) {
vAPI . setIcon ( tabId , tab . ownerDocument . defaultView ) ;
return ;
}
vAPI . tabs . onNavigation ( {
frameId : 0 ,
tabId : tabId ,
url : URI . asciiSpec
} ) ;
} ,
2014-12-02 08:35:25 +01:00
onLocationChange : function ( browser , webProgress , request , location , flags ) {
2014-12-28 21:26:06 +01:00
if ( ! webProgress . isTopLevel ) {
2014-12-02 08:35:25 +01:00
return ;
}
var tabId = vAPI . tabs . getTabId ( browser ) ;
2015-01-21 17:13:32 +01:00
// LOCATION_CHANGE_SAME_DOCUMENT = "did not load a new document"
2015-02-12 19:19:28 +01:00
if ( flags & Ci . nsIWebProgressListener . LOCATION _CHANGE _SAME _DOCUMENT ) {
2015-01-14 23:45:55 +01:00
vAPI . tabs . onUpdated ( tabId , { url : location . asciiSpec } , {
2014-12-02 08:35:25 +01:00
frameId : 0 ,
tabId : tabId ,
2015-01-14 23:45:55 +01:00
url : browser . currentURI . asciiSpec
2014-12-02 08:35:25 +01:00
} ) ;
2015-01-21 17:13:32 +01:00
return ;
2014-12-02 08:35:25 +01:00
}
2015-01-21 17:13:32 +01:00
// https://github.com/gorhill/uBlock/issues/105
// Allow any kind of pages
vAPI . tabs . onNavigation ( {
frameId : 0 ,
tabId : tabId ,
url : location . asciiSpec
} ) ;
2015-02-12 19:19:17 +01:00
} ,
onFennecLocationChange : function ( e ) {
// Fennec "equivalent" to onLocationChange
// note that DOMTitleChanged is selected as it fires very early
// (before DOMContentLoaded), and it does fire even if there is no title
var tabId = vAPI . tabs . getTabId ( e . target ) ;
if ( tabId === - 1 ) {
// probably not top level
return ;
}
vAPI . tabs . onNavigation ( {
frameId : 0 ,
tabId : tabId ,
url : e . target . location . href
} ) ;
} ,
2014-12-02 08:35:25 +01:00
} ;
/******************************************************************************/
2015-01-20 00:42:58 +01:00
vAPI . isNoTabId = function ( tabId ) {
2015-01-26 20:26:45 +01:00
return tabId . toString ( ) === '-1' ;
2015-01-20 00:42:58 +01:00
} ;
2015-01-26 20:26:45 +01:00
vAPI . noTabId = '-1' ;
2015-01-20 00:42:58 +01:00
/******************************************************************************/
2014-12-02 08:35:25 +01:00
2015-01-27 11:13:33 +01:00
vAPI . tabs = { } ;
/******************************************************************************/
2014-12-02 08:35:25 +01:00
vAPI . tabs . registerListeners = function ( ) {
2015-01-27 11:13:33 +01:00
// onNavigation and onUpdated handled with tabWatcher.onLocationChange
// onClosed - handled in tabWatcher.onTabClose
// onPopup - handled in httpObserver.handlePopup
2014-12-02 08:35:25 +01:00
2014-12-28 21:26:06 +01:00
for ( var win of this . getWindows ( ) ) {
2014-12-16 18:09:55 +01:00
windowWatcher . onReady . call ( win ) ;
2014-12-02 08:35:25 +01:00
}
2014-12-16 13:44:34 +01:00
2014-12-16 18:09:55 +01:00
Services . ww . registerNotification ( windowWatcher ) ;
2014-12-16 13:44:34 +01:00
2015-01-12 17:45:09 +01:00
cleanupTasks . push ( function ( ) {
2014-12-16 13:44:34 +01:00
Services . ww . unregisterNotification ( windowWatcher ) ;
2014-12-28 21:26:06 +01:00
for ( var win of vAPI . tabs . getWindows ( ) ) {
2014-12-20 11:59:58 +01:00
vAPI . contextMenu . unregister ( win . document ) ;
2014-12-16 20:49:11 +01:00
win . removeEventListener ( 'DOMContentLoaded' , windowWatcher . onReady ) ;
2014-12-16 13:44:34 +01:00
2015-02-12 19:19:17 +01:00
if ( win . gBrowser && win . gBrowser . tabContainer ) {
// desktop Firefox
var tC = win . gBrowser . tabContainer ;
win . gBrowser . removeTabsProgressListener ( tabWatcher ) ;
tC . removeEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
tC . removeEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
// close extension tabs
for ( var tab of win . gBrowser . tabs ) {
var URI = tab . linkedBrowser . currentURI ;
2014-12-16 13:44:34 +01:00
2015-02-12 19:19:17 +01:00
if ( URI . schemeIs ( 'chrome' ) && URI . host === location . host ) {
win . gBrowser . removeTab ( tab ) ;
}
}
} else if ( win . BrowserApp && win . BrowserApp . deck ) {
// Fennec
var deck = win . BrowserApp . deck ;
deck . removeEventListener ( 'DOMTitleChanged' , tabWatcher . onFennecLocationChange ) ;
deck . removeEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
deck . removeEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
// close extension tabs
for ( var tab of win . BrowserApp . tabs ) {
var URI = tab . browser . currentURI ;
2014-12-16 13:44:34 +01:00
2015-02-12 19:19:17 +01:00
if ( URI . schemeIs ( 'chrome' ) && URI . host === location . host ) {
win . BrowserApp . closeTab ( tab ) ;
}
2014-12-16 13:44:34 +01:00
}
}
}
} ) ;
2014-12-02 08:35:25 +01:00
} ;
/******************************************************************************/
vAPI . tabs . getTabId = function ( target ) {
2015-02-12 19:19:17 +01:00
if ( vAPI . fennec ) {
// Fennec
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( target . browser ) {
// target is a tab
return target . id ;
}
2014-12-19 21:26:10 +01:00
2015-02-12 19:19:17 +01:00
// target is a browser or contentDocument
for ( var win of vAPI . tabs . getWindows ( ) ) {
for ( var tab of win . BrowserApp . tabs ) {
if ( target === tab . browser ||
target === tab . window . document ) {
return tab . id ;
}
}
}
2015-01-26 20:26:45 +01:00
return - 1 ;
2015-02-12 19:19:17 +01:00
} else {
// desktop Firefox
2014-12-19 21:26:10 +01:00
2015-02-12 19:19:17 +01:00
if ( target . linkedPanel ) {
// target is a tab
return target . linkedPanel ;
}
2015-01-26 20:26:45 +01:00
2015-02-12 19:19:17 +01:00
// target is a browser
var i , gBrowser = target . ownerDocument . defaultView . gBrowser ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( ! gBrowser ) {
return - 1 ;
}
// This should be more efficient from version 35
if ( gBrowser . getTabForBrowser ) {
i = gBrowser . getTabForBrowser ( target ) ;
return i ? i . linkedPanel : - 1 ;
}
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( ! gBrowser . browsers ) {
return - 1 ;
}
i = gBrowser . browsers . indexOf ( target ) ;
if ( i !== - 1 ) {
i = gBrowser . tabs [ i ] . linkedPanel ;
}
return i ;
}
2014-12-02 08:35:25 +01:00
} ;
/******************************************************************************/
vAPI . tabs . get = function ( tabId , callback ) {
2015-02-12 19:19:17 +01:00
var tab , windows , win ;
2014-12-02 08:35:25 +01:00
2014-12-28 21:26:06 +01:00
if ( tabId === null ) {
2015-02-12 19:19:17 +01:00
win = Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
if ( win . gBrowser ) {
// desktop Firefox
tab = win . gBrowser . selectedTab ;
} else if ( win . BrowserApp ) {
// Fennec
tab = win . BrowserApp . selectedTab ;
}
2014-12-02 08:35:25 +01:00
tabId = vAPI . tabs . getTabId ( tab ) ;
2014-12-28 21:26:06 +01:00
} else {
2014-12-02 08:35:25 +01:00
windows = this . getWindows ( ) ;
2015-02-12 19:19:17 +01:00
if ( vAPI . fennec ) {
// Fennec
for ( win of windows ) {
tab = win . BrowserApp . getTabForId ( tabId ) ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( tab ) {
break ;
}
}
} else {
// desktop Firefox
for ( win of windows ) {
tab = win . gBrowser . tabContainer . querySelector (
'tab[linkedpanel="' + tabId + '"]'
) ;
if ( tab ) {
break ;
}
2014-12-02 08:35:25 +01:00
}
}
}
2014-12-16 22:31:03 +01:00
// for internal use
2015-01-08 21:11:43 +01:00
if ( typeof callback !== 'function' ) {
2014-12-16 22:31:03 +01:00
return tab ;
}
2014-12-28 21:26:06 +01:00
if ( ! tab ) {
2014-12-02 08:35:25 +01:00
callback ( ) ;
return ;
}
2015-02-12 19:19:17 +01:00
if ( tab . linkedBrowser ) {
// desktop Firefox
var browser = tab . linkedBrowser ;
var gBrowser = win . gBrowser ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( ! windows ) {
windows = this . getWindows ( ) ;
}
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
callback ( {
id : tabId ,
index : gBrowser . browsers . indexOf ( browser ) ,
windowId : windows . indexOf ( win ) ,
active : tab === gBrowser . selectedTab ,
url : browser . currentURI . asciiSpec ,
title : tab . label
} ) ;
} else if ( tab . browser ) {
// Fennec
var browser = tab . browser ;
var BrowserApp = win . BrowserApp ;
if ( ! windows ) {
windows = this . getWindows ( ) ;
}
callback ( {
id : tabId ,
index : BrowserApp . tabs . indexOf ( tab ) ,
windowId : windows . indexOf ( win ) ,
active : tab === BrowserApp . selectedTab ,
url : browser . currentURI . asciiSpec ,
title : browser . contentDocument . title
} ) ;
}
2014-12-02 08:35:25 +01:00
} ;
/******************************************************************************/
vAPI . tabs . getAll = function ( window ) {
2014-12-25 14:53:30 +01:00
var win , tab , tabs = [ ] ;
2014-12-02 08:35:25 +01:00
2014-12-28 21:26:06 +01:00
for ( win of this . getWindows ( ) ) {
if ( window && window !== win ) {
2014-12-02 08:35:25 +01:00
continue ;
}
2015-02-12 19:19:17 +01:00
var tabList ;
if ( win . gBrowser ) {
// desktop Firefox
tabList = win . gBrowser . tabs ;
} else {
// Fennec
tabList = win . BrowserApp . tabs ;
}
for ( tab of tabList ) {
2014-12-02 08:35:25 +01:00
tabs . push ( tab ) ;
}
}
return tabs ;
} ;
/******************************************************************************/
vAPI . tabs . getWindows = function ( ) {
var winumerator = Services . wm . getEnumerator ( 'navigator:browser' ) ;
var windows = [ ] ;
2014-12-28 21:26:06 +01:00
while ( winumerator . hasMoreElements ( ) ) {
2014-12-02 08:35:25 +01:00
var win = winumerator . getNext ( ) ;
2014-12-28 21:26:06 +01:00
if ( ! win . closed ) {
2014-12-02 08:35:25 +01:00
windows . push ( win ) ;
}
}
return windows ;
} ;
/******************************************************************************/
// properties of the details object:
// url: 'URL', // the address that will be opened
// tabId: 1, // the tab is used if set, instead of creating a new one
// index: -1, // undefined: end of the list, -1: following tab, or after index
// active: false, // opens the tab in background - true and undefined: foreground
// select: true // if a tab is already opened with that url, then select it instead of opening a new one
vAPI . tabs . open = function ( details ) {
2014-12-28 21:26:06 +01:00
if ( ! details . url ) {
2014-12-02 08:35:25 +01:00
return null ;
}
// extension pages
2014-12-28 21:26:06 +01:00
if ( /^[\w-]{2,}:/ . test ( details . url ) === false ) {
2014-12-02 08:35:25 +01:00
details . url = vAPI . getURL ( details . url ) ;
}
var tab , tabs ;
2014-12-28 21:26:06 +01:00
if ( details . select ) {
2015-01-15 13:24:35 +01:00
var URI = Services . io . newURI ( details . url , null , null ) ;
2014-12-02 08:35:25 +01:00
tabs = this . getAll ( ) ;
2014-12-28 21:26:06 +01:00
for ( tab of tabs ) {
2015-02-12 19:19:17 +01:00
var browser ;
if ( tab . linkedBrowser ) {
// desktop Firefox
browser = tab . linkedBrowser ;
} else {
// Fennec
browser = tab . browser ;
}
2014-12-02 08:35:25 +01:00
2015-01-15 13:24:35 +01:00
// Or simply .equals if we care about the fragment
if ( URI . equalsExceptRef ( browser . currentURI ) ) {
2015-02-12 19:19:17 +01:00
var win = browser . ownerDocument . defaultView ;
if ( win . gBrowser ) {
// desktop Firefox
win . gBrowser . selectedTab = tab ;
} else {
// Fennec
win . BrowserApp . selectTab ( tab ) ;
}
2014-12-02 08:35:25 +01:00
return ;
}
}
}
2014-12-28 21:26:06 +01:00
if ( details . active === undefined ) {
2014-12-02 08:35:25 +01:00
details . active = true ;
}
2015-02-12 19:19:17 +01:00
var win = Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
if ( win . gBrowser ) {
// desktop Firefox
var gBrowser = win . gBrowser ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( details . index === - 1 ) {
details . index = gBrowser . browsers . indexOf ( gBrowser . selectedBrowser ) + 1 ;
}
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
if ( details . tabId ) {
tabs = tabs || this . getAll ( ) ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
for ( tab of tabs ) {
if ( vAPI . tabs . getTabId ( tab ) === details . tabId ) {
tab . linkedBrowser . loadURI ( details . url ) ;
return ;
}
}
}
tab = gBrowser . loadOneTab ( details . url , { inBackground : ! details . active } ) ;
if ( details . index !== undefined ) {
gBrowser . moveTabTo ( tab , details . index ) ;
}
} else {
// Fennec
var BrowserApp = win . BrowserApp ;
if ( details . index === - 1 ) {
details . index = BrowserApp . tabs . indexOf ( gBrowser . selectedTab ) + 1 ;
}
if ( details . tabId ) {
tabs = tabs || this . getAll ( ) ;
tab = BrowserApp . getTabForId ( details . tabId ) ;
if ( tab ) {
tab . browser . loadURI ( details . url ) ;
2014-12-02 08:35:25 +01:00
return ;
}
}
2015-02-12 19:19:17 +01:00
tab = BrowserApp . addTab ( details . url , { selected : details . active } ) ;
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
// note that it's impossible to move tabs on Fennec, so don't bother
2014-12-02 08:35:25 +01:00
}
} ;
/******************************************************************************/
2015-01-02 18:41:41 +01:00
vAPI . tabs . remove = function ( tabIds ) {
2014-12-28 21:26:06 +01:00
if ( ! Array . isArray ( tabIds ) ) {
2014-12-02 08:35:25 +01:00
tabIds = [ tabIds ] ;
}
2015-02-12 19:19:17 +01:00
if ( vAPI . fennec ) {
// Fennec
for ( var win of this . getWindows ( ) ) {
var tabs = win . BrowserApp . tabs ;
if ( ! tabs ) {
continue ;
}
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
for ( var tab of tabs ) {
if ( tab . id in tabIds ) {
win . BrowserApp . closeTab ( tab ) ;
}
}
2014-12-02 08:35:25 +01:00
}
2015-02-12 19:19:17 +01:00
} else {
// desktop Firefox
tabIds = tabIds . map ( function ( tabId ) {
return 'tab[linkedpanel="' + tabId + '"]' ;
} ) . join ( ',' ) ;
for ( var win of this . getWindows ( ) ) {
var tabs = win . gBrowser . tabContainer . querySelectorAll ( tabIds ) ;
if ( ! tabs ) {
continue ;
}
2014-12-02 08:35:25 +01:00
2015-02-12 19:19:17 +01:00
for ( var tab of tabs ) {
win . gBrowser . removeTab ( tab ) ;
}
2014-12-02 08:35:25 +01:00
}
}
} ;
/******************************************************************************/
2015-01-08 21:11:43 +01:00
vAPI . tabs . reload = function ( tabId ) {
var tab = this . get ( tabId ) ;
if ( tab ) {
2015-02-12 19:19:17 +01:00
if ( tab . browser ) {
// Fennec
tab . browser . reload ( ) ;
} else {
// desktop Firefox
tab . ownerDocument . defaultView . gBrowser . reloadTab ( tab ) ;
}
2015-01-08 21:11:43 +01:00
}
} ;
/******************************************************************************/
2014-12-16 13:44:34 +01:00
vAPI . tabs . injectScript = function ( tabId , details , callback ) {
2014-12-16 22:31:03 +01:00
var tab = vAPI . tabs . get ( tabId ) ;
2014-12-16 13:44:34 +01:00
2014-12-28 21:26:06 +01:00
if ( ! tab ) {
2014-12-16 22:31:03 +01:00
return ;
}
2015-01-27 13:31:17 +01:00
if ( typeof details . file !== 'string' ) {
return ;
2014-12-28 10:56:09 +01:00
}
2015-01-27 13:31:17 +01:00
details . file = vAPI . getURL ( details . file ) ;
2014-12-16 22:31:03 +01:00
tab . linkedBrowser . messageManager . sendAsyncMessage (
2014-12-18 11:59:04 +01:00
location . host + ':broadcast' ,
2014-12-16 22:31:03 +01:00
JSON . stringify ( {
broadcast : true ,
2014-12-28 21:26:06 +01:00
channelName : 'vAPI' ,
2014-12-16 22:31:03 +01:00
msg : {
cmd : 'injectScript' ,
details : details
}
} )
) ;
2014-12-28 21:26:06 +01:00
if ( typeof callback === 'function' ) {
2014-12-16 22:31:03 +01:00
setTimeout ( callback , 13 ) ;
}
2014-12-16 13:44:34 +01:00
} ;
/******************************************************************************/
2014-12-26 21:41:44 +01:00
vAPI . setIcon = function ( tabId , iconStatus , badge ) {
// If badge is undefined, then setIcon was called from the TabSelect event
2015-01-11 18:41:52 +01:00
var win = badge === undefined
2014-12-26 21:41:44 +01:00
? iconStatus
: Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
2015-01-11 18:41:52 +01:00
var tb = vAPI . toolbarButton ;
2015-02-12 19:19:17 +01:00
var selectedTab , curTabId ;
if ( win . gBrowser ) {
// desktop Firefox
selectedTab = win . gBrowser . selectedTab ;
} else {
// Fennec
selectedTab = win . BrowserApp . selectedTab ;
}
curTabId = vAPI . tabs . getTabId ( selectedTab ) ;
2014-12-16 13:44:34 +01:00
// from 'TabSelect' event
2014-12-28 21:26:06 +01:00
if ( tabId === undefined ) {
2014-12-16 13:44:34 +01:00
tabId = curTabId ;
2014-12-28 21:26:06 +01:00
} else if ( badge !== undefined ) {
2015-01-27 11:13:33 +01:00
tb . tabs [ tabId ] = { badge : badge , img : iconStatus === 'on' } ;
2014-12-16 13:44:34 +01:00
}
2014-12-28 21:26:06 +01:00
if ( tabId !== curTabId ) {
2014-12-16 13:44:34 +01:00
return ;
}
2015-01-11 18:41:52 +01:00
var button = win . document . getElementById ( tb . id ) ;
2014-12-24 23:11:36 +01:00
2014-12-28 21:26:06 +01:00
if ( ! button ) {
2014-12-24 23:11:36 +01:00
return ;
}
2015-01-11 18:41:52 +01:00
var icon = tb . tabs [ tabId ] ;
2014-12-16 13:44:34 +01:00
button . setAttribute ( 'badge' , icon && icon . badge || '' ) ;
2015-01-11 18:41:52 +01:00
if ( ! icon || ! icon . img ) {
icon = '' ;
}
else {
icon = 'url(' + vAPI . getURL ( 'img/browsericons/icon16.svg' ) + ')' ;
2014-12-16 16:10:50 +01:00
}
2015-01-11 18:41:52 +01:00
button . style . listStyleImage = icon ;
2014-12-07 20:51:49 +01:00
} ;
/******************************************************************************/
2014-11-24 20:00:27 +01:00
vAPI . messaging = {
2014-12-16 13:44:34 +01:00
get globalMessageManager ( ) {
return Cc [ '@mozilla.org/globalmessagemanager;1' ]
. getService ( Ci . nsIMessageListenerManager ) ;
} ,
frameScript : vAPI . getURL ( 'frameScript.js' ) ,
2014-11-24 20:00:27 +01:00
listeners : { } ,
defaultHandler : null ,
NOOPFUNC : function ( ) { } ,
UNHANDLED : 'vAPI.messaging.notHandled'
} ;
/******************************************************************************/
vAPI . messaging . listen = function ( listenerName , callback ) {
this . listeners [ listenerName ] = callback ;
} ;
/******************************************************************************/
2014-12-25 14:53:30 +01:00
vAPI . messaging . onMessage = function ( { target , data } ) {
var messageManager = target . messageManager ;
2014-12-16 13:44:34 +01:00
2014-12-28 21:26:06 +01:00
if ( ! messageManager ) {
2014-12-16 13:44:34 +01:00
// Message came from a popup, and its message manager is not usable.
// So instead we broadcast to the parent window.
2014-12-25 14:53:30 +01:00
messageManager = target
2014-12-16 13:44:34 +01:00
. webNavigation . QueryInterface ( Ci . nsIDocShell )
. chromeEventHandler . ownerDocument . defaultView . messageManager ;
}
2015-02-01 16:15:35 +01:00
var channelNameRaw = data . channelName ;
var pos = channelNameRaw . indexOf ( '|' ) ;
var channelName = channelNameRaw . slice ( pos + 1 ) ;
2014-11-24 20:00:27 +01:00
var callback = vAPI . messaging . NOOPFUNC ;
2015-02-01 16:15:35 +01:00
if ( data . requestId !== undefined ) {
callback = CallbackWrapper . factory (
messageManager ,
channelName ,
channelNameRaw . slice ( 0 , pos ) ,
data . requestId
) . callback ;
2014-11-24 20:00:27 +01:00
}
var sender = {
tab : {
2014-12-25 14:53:30 +01:00
id : vAPI . tabs . getTabId ( target )
2014-11-24 20:00:27 +01:00
}
} ;
// Specific handler
var r = vAPI . messaging . UNHANDLED ;
2014-12-28 21:26:06 +01:00
var listener = vAPI . messaging . listeners [ channelName ] ;
2014-11-24 20:00:27 +01:00
if ( typeof listener === 'function' ) {
2014-12-25 14:53:30 +01:00
r = listener ( data . msg , sender , callback ) ;
2014-11-24 20:00:27 +01:00
}
if ( r !== vAPI . messaging . UNHANDLED ) {
return ;
}
// Default handler
2014-12-25 14:53:30 +01:00
r = vAPI . messaging . defaultHandler ( data . msg , sender , callback ) ;
2014-11-24 20:00:27 +01:00
if ( r !== vAPI . messaging . UNHANDLED ) {
return ;
}
2014-12-25 14:53:30 +01:00
console . error ( 'µBlock> messaging > unknown request: %o' , data ) ;
2014-11-24 20:00:27 +01:00
// Unhandled:
// Need to callback anyways in case caller expected an answer, or
// else there is a memory leak on caller's side
callback ( ) ;
} ;
/******************************************************************************/
vAPI . messaging . setup = function ( defaultHandler ) {
// Already setup?
if ( this . defaultHandler !== null ) {
return ;
}
if ( typeof defaultHandler !== 'function' ) {
defaultHandler = function ( ) { return vAPI . messaging . UNHANDLED ; } ;
}
this . defaultHandler = defaultHandler ;
2014-12-16 13:44:34 +01:00
this . globalMessageManager . addMessageListener (
2014-12-18 11:59:04 +01:00
location . host + ':background' ,
2014-12-07 20:51:49 +01:00
this . onMessage
) ;
2014-12-16 13:44:34 +01:00
2014-12-28 10:56:09 +01:00
this . globalMessageManager . loadFrameScript ( this . frameScript , true ) ;
2014-12-16 13:44:34 +01:00
2015-01-12 17:45:09 +01:00
cleanupTasks . push ( function ( ) {
2014-12-16 13:44:34 +01:00
var gmm = vAPI . messaging . globalMessageManager ;
gmm . removeDelayedFrameScript ( vAPI . messaging . frameScript ) ;
gmm . removeMessageListener (
2014-12-18 11:59:04 +01:00
location . host + ':background' ,
2014-12-16 13:44:34 +01:00
vAPI . messaging . onMessage
) ;
} ) ;
2014-11-24 20:00:27 +01:00
} ;
/******************************************************************************/
2014-11-27 20:45:54 +01:00
vAPI . messaging . broadcast = function ( message ) {
2014-12-16 13:44:34 +01:00
this . globalMessageManager . broadcastAsyncMessage (
2014-12-18 11:59:04 +01:00
location . host + ':broadcast' ,
2014-11-27 20:45:54 +01:00
JSON . stringify ( { broadcast : true , msg : message } )
) ;
2014-11-24 20:00:27 +01:00
} ;
/******************************************************************************/
2015-02-01 16:15:35 +01:00
// This allows to avoid creating a closure for every single message which
// expects an answer. Having a closure created each time a message is processed
2015-02-17 22:05:23 +01:00
// has been always bothering me. Another benefit of the implementation here
2015-02-01 16:15:35 +01:00
// is to reuse the callback proxy object, so less memory churning.
//
// https://developers.google.com/speed/articles/optimizing-javascript
// "Creating a closure is significantly slower then creating an inner
2015-02-17 22:05:23 +01:00
// function without a closure, and much slower than reusing a static
2015-02-01 16:15:35 +01:00
// function"
//
// http://hacksoflife.blogspot.ca/2015/01/the-four-horsemen-of-performance.html
2015-02-17 22:05:23 +01:00
// "the dreaded 'uniformly slow code' case where every function takes 1%
// of CPU and you have to make one hundred separate performance optimizations
2015-02-01 16:15:35 +01:00
// to improve performance at all"
2015-02-01 18:25:14 +01:00
//
// http://jsperf.com/closure-no-closure/2
2015-02-01 16:15:35 +01:00
var CallbackWrapper = function ( messageManager , channelName , listenerId , requestId ) {
this . callback = this . proxy . bind ( this ) ; // bind once
this . init ( messageManager , channelName , listenerId , requestId ) ;
} ;
CallbackWrapper . junkyard = [ ] ;
CallbackWrapper . factory = function ( messageManager , channelName , listenerId , requestId ) {
var wrapper = CallbackWrapper . junkyard . pop ( ) ;
if ( wrapper ) {
wrapper . init ( messageManager , channelName , listenerId , requestId ) ;
return wrapper ;
}
return new CallbackWrapper ( messageManager , channelName , listenerId , requestId ) ;
} ;
CallbackWrapper . prototype . init = function ( messageManager , channelName , listenerId , requestId ) {
this . messageManager = messageManager ;
this . channelName = channelName ;
this . listenerId = listenerId ;
this . requestId = requestId ;
} ;
CallbackWrapper . prototype . proxy = function ( response ) {
var message = JSON . stringify ( {
requestId : this . requestId ,
channelName : this . channelName ,
msg : response !== undefined ? response : null
} ) ;
if ( this . messageManager . sendAsyncMessage ) {
this . messageManager . sendAsyncMessage ( this . listenerId , message ) ;
} else {
this . messageManager . broadcastAsyncMessage ( this . listenerId , message ) ;
}
// Mark for reuse
2015-02-17 22:05:23 +01:00
this . messageManager =
this . channelName =
this . requestId =
2015-02-01 16:15:35 +01:00
this . listenerId = null ;
CallbackWrapper . junkyard . push ( this ) ;
} ;
/******************************************************************************/
2014-12-24 23:11:36 +01:00
var httpObserver = {
2015-01-02 18:41:41 +01:00
classDescription : 'net-channel-event-sinks for ' + location . host ,
classID : Components . ID ( '{dc8d6319-5f6e-4438-999e-53722db99e84}' ) ,
contractID : '@' + location . host + '/net-channel-event-sinks;1' ,
2014-12-24 23:11:36 +01:00
ABORT : Components . results . NS _BINDING _ABORTED ,
2015-01-02 18:41:41 +01:00
ACCEPT : Components . results . NS _SUCCEEDED ,
2015-01-21 01:39:13 +01:00
// Request types: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy#Constants
2015-01-02 18:41:41 +01:00
MAIN _FRAME : Ci . nsIContentPolicy . TYPE _DOCUMENT ,
2015-01-13 17:54:54 +01:00
VALID _CSP _TARGETS : 1 << Ci . nsIContentPolicy . TYPE _DOCUMENT |
1 << Ci . nsIContentPolicy . TYPE _SUBDOCUMENT ,
2015-01-02 18:41:41 +01:00
typeMap : {
2015-01-26 20:26:45 +01:00
1 : 'other' ,
2015-01-02 18:41:41 +01:00
2 : 'script' ,
3 : 'image' ,
4 : 'stylesheet' ,
5 : 'object' ,
6 : 'main_frame' ,
7 : 'sub_frame' ,
2015-01-21 01:39:13 +01:00
11 : 'xmlhttprequest' ,
12 : 'object' ,
14 : 'font'
2015-01-02 18:41:41 +01:00
} ,
2014-12-24 23:11:36 +01:00
lastRequest : {
url : null ,
type : null ,
tabId : null ,
frameId : null ,
2015-01-02 18:41:41 +01:00
parentFrameId : null ,
2015-01-11 18:41:52 +01:00
openerURL : null
2015-01-02 18:41:41 +01:00
} ,
get componentRegistrar ( ) {
return Components . manager . QueryInterface ( Ci . nsIComponentRegistrar ) ;
} ,
get categoryManager ( ) {
return Cc [ '@mozilla.org/categorymanager;1' ]
. getService ( Ci . nsICategoryManager ) ;
2014-12-24 23:11:36 +01:00
} ,
2014-12-28 21:26:06 +01:00
2015-01-04 13:58:17 +01:00
QueryInterface : ( function ( ) {
2015-01-11 18:41:52 +01:00
var { XPCOMUtils } = Cu . import ( 'resource://gre/modules/XPCOMUtils.jsm' , null ) ;
2015-01-04 13:58:17 +01:00
return XPCOMUtils . generateQI ( [
Ci . nsIFactory ,
Ci . nsIObserver ,
Ci . nsIChannelEventSink ,
Ci . nsISupportsWeakReference
] ) ;
} ) ( ) ,
2015-01-02 18:41:41 +01:00
createInstance : function ( outer , iid ) {
if ( outer ) {
throw Components . results . NS _ERROR _NO _AGGREGATION ;
}
return this . QueryInterface ( iid ) ;
} ,
2014-12-28 21:26:06 +01:00
2014-12-28 10:56:09 +01:00
register : function ( ) {
2015-01-02 18:41:41 +01:00
Services . obs . addObserver ( this , 'http-on-opening-request' , true ) ;
Services . obs . addObserver ( this , 'http-on-examine-response' , true ) ;
this . componentRegistrar . registerFactory (
this . classID ,
this . classDescription ,
this . contractID ,
this
) ;
this . categoryManager . addCategoryEntry (
'net-channel-event-sinks' ,
this . contractID ,
this . contractID ,
false ,
true
) ;
2014-12-28 10:56:09 +01:00
} ,
2014-12-28 21:26:06 +01:00
2014-12-28 10:56:09 +01:00
unregister : function ( ) {
2015-01-02 18:41:41 +01:00
Services . obs . removeObserver ( this , 'http-on-opening-request' ) ;
Services . obs . removeObserver ( this , 'http-on-examine-response' ) ;
this . componentRegistrar . unregisterFactory ( this . classID , this ) ;
this . categoryManager . deleteCategoryEntry (
'net-channel-event-sinks' ,
this . contractID ,
false
) ;
2014-12-28 10:56:09 +01:00
} ,
2014-12-28 21:26:06 +01:00
2015-01-02 18:41:41 +01:00
handlePopup : function ( URI , tabId , sourceTabId ) {
if ( ! sourceTabId ) {
return false ;
}
2015-01-15 13:24:35 +01:00
if ( ! URI . schemeIs ( 'http' ) && ! URI . schemeIs ( 'https' ) ) {
2015-01-02 18:41:41 +01:00
return false ;
}
var result = vAPI . tabs . onPopup ( {
tabId : tabId ,
sourceTabId : sourceTabId ,
2015-01-14 23:45:55 +01:00
url : URI . asciiSpec
2015-01-02 18:41:41 +01:00
} ) ;
return result === true ;
} ,
2015-01-26 20:26:45 +01:00
handleRequest : function ( channel , URI , details ) {
2015-01-02 18:41:41 +01:00
var onBeforeRequest = vAPI . net . onBeforeRequest ;
var type = this . typeMap [ details . type ] || 'other' ;
if ( onBeforeRequest . types . has ( type ) === false ) {
return false ;
2014-12-24 23:11:36 +01:00
}
2015-01-02 18:41:41 +01:00
var result = onBeforeRequest . callback ( {
frameId : details . frameId ,
2015-01-26 20:26:45 +01:00
hostname : URI . asciiHost ,
2015-01-21 01:39:13 +01:00
parentFrameId : details . parentFrameId ,
tabId : details . tabId ,
type : type ,
2015-01-26 20:26:45 +01:00
url : URI . asciiSpec
2015-01-02 18:41:41 +01:00
} ) ;
if ( ! result || typeof result !== 'object' ) {
return false ;
}
if ( result . cancel === true ) {
channel . cancel ( this . ABORT ) ;
return true ;
2015-01-21 01:39:13 +01:00
}
if ( result . redirectUrl ) {
2015-01-02 18:41:41 +01:00
channel . redirectionLimit = 1 ;
channel . redirectTo (
Services . io . newURI ( result . redirectUrl , null , null )
) ;
return true ;
}
2014-12-24 23:11:36 +01:00
2015-01-02 18:41:41 +01:00
return false ;
} ,
2014-12-24 23:11:36 +01:00
2015-01-02 18:41:41 +01:00
observe : function ( channel , topic ) {
if ( ! ( channel instanceof Ci . nsIHttpChannel ) ) {
2014-12-24 23:11:36 +01:00
return ;
}
2015-01-02 18:41:41 +01:00
var URI = channel . URI ;
var channelData , result ;
if ( topic === 'http-on-examine-response' ) {
if ( ! ( channel instanceof Ci . nsIWritablePropertyBag ) ) {
return ;
}
2014-12-24 23:11:36 +01:00
try {
2015-01-27 16:37:02 +01:00
/ * [
type ,
tabId ,
sourceTabId - given if it was a popup ,
frameId ,
parentFrameId
] * /
2015-01-02 18:41:41 +01:00
channelData = channel . getProperty ( location . host + 'reqdata' ) ;
2014-12-24 23:11:36 +01:00
} catch ( ex ) {
return ;
}
2015-01-13 17:54:54 +01:00
if ( ! channelData ) {
return ;
}
2015-01-17 21:52:36 +01:00
if ( ( 1 << channelData [ 0 ] & this . VALID _CSP _TARGETS ) === 0 ) {
2014-12-24 23:11:36 +01:00
return ;
}
2014-12-26 21:41:44 +01:00
topic = 'Content-Security-Policy' ;
2014-12-24 23:11:36 +01:00
try {
2015-01-02 18:41:41 +01:00
result = channel . getResponseHeader ( topic ) ;
2014-12-24 23:11:36 +01:00
} catch ( ex ) {
result = null ;
}
result = vAPI . net . onHeadersReceived . callback ( {
2015-01-21 01:39:13 +01:00
hostname : URI . asciiHost ,
2015-01-27 17:56:04 +01:00
parentFrameId : channelData [ 4 ] ,
2015-01-21 01:39:13 +01:00
responseHeaders : result ? [ { name : topic , value : result } ] : [ ] ,
tabId : channelData [ 1 ] ,
url : URI . asciiSpec
2014-12-24 23:11:36 +01:00
} ) ;
2014-12-28 21:26:06 +01:00
if ( result ) {
2015-01-02 18:41:41 +01:00
channel . setResponseHeader (
2014-12-26 21:41:44 +01:00
topic ,
result . responseHeaders . pop ( ) . value ,
2014-12-24 23:11:36 +01:00
true
) ;
}
return ;
}
// http-on-opening-request
var lastRequest = this . lastRequest ;
2015-01-26 20:26:45 +01:00
if ( lastRequest . url === null ) {
2015-02-28 07:38:41 +01:00
this . handleRequest ( channel , URI , {
tabId : vAPI . noTabId ,
type : channel . loadInfo && channel . loadInfo . contentPolicyType || 1
} ) ;
2014-12-24 23:11:36 +01:00
return ;
}
// Important! When loading file via XHR for mirroring,
// the URL will be the same, so it could fall into an infinite loop
lastRequest . url = null ;
2015-01-02 18:41:41 +01:00
var sourceTabId = null ;
2014-12-24 23:11:36 +01:00
2015-01-12 17:45:09 +01:00
// Popup candidate (only for main_frame type)
2015-01-11 18:41:52 +01:00
if ( lastRequest . openerURL ) {
2015-01-02 18:41:41 +01:00
for ( var tab of vAPI . tabs . getAll ( ) ) {
var tabURI = tab . linkedBrowser . currentURI ;
2014-12-24 23:11:36 +01:00
2015-01-15 13:24:35 +01:00
// Probably isn't the best method to identify the source tab
2015-01-15 15:58:14 +01:00
if ( tabURI . spec !== lastRequest . openerURL ) {
2015-01-21 14:59:23 +01:00
continue ;
2015-01-15 15:58:14 +01:00
}
sourceTabId = vAPI . tabs . getTabId ( tab ) ;
if ( sourceTabId !== lastRequest . tabId ) {
2015-01-02 18:41:41 +01:00
break ;
}
2015-01-15 15:58:14 +01:00
sourceTabId = null ;
2015-01-02 18:41:41 +01:00
}
2014-12-24 23:11:36 +01:00
2015-01-02 18:41:41 +01:00
if ( this . handlePopup ( channel . URI , lastRequest . tabId , sourceTabId ) ) {
channel . cancel ( this . ABORT ) ;
return ;
}
}
2014-12-24 23:11:36 +01:00
2015-01-26 20:26:45 +01:00
if ( this . handleRequest ( channel , URI , lastRequest ) ) {
2014-12-24 23:11:36 +01:00
return ;
}
2015-01-12 17:45:09 +01:00
// If request is not handled we may use the data in on-modify-request
2015-01-02 18:41:41 +01:00
if ( channel instanceof Ci . nsIWritablePropertyBag ) {
channel . setProperty (
location . host + 'reqdata' ,
2015-01-27 16:37:02 +01:00
[
lastRequest . type ,
lastRequest . tabId ,
sourceTabId ,
lastRequest . frameId ,
lastRequest . parentFrameId
]
2014-12-24 23:11:36 +01:00
) ;
}
2015-01-02 18:41:41 +01:00
} ,
// contentPolicy.shouldLoad doesn't detect redirects, this needs to be used
asyncOnChannelRedirect : function ( oldChannel , newChannel , flags , callback ) {
var result = this . ACCEPT ;
// If error thrown, the redirect will fail
try {
// skip internal redirects?
/ * i f ( f l a g s & 4 ) {
console . log ( 'internal redirect skipped' ) ;
return ;
} * /
2015-01-15 13:24:35 +01:00
var URI = newChannel . URI ;
2015-01-02 18:41:41 +01:00
2015-01-15 13:24:35 +01:00
if ( ! URI . schemeIs ( 'http' ) && ! URI . schemeIs ( 'https' ) ) {
2015-01-02 18:41:41 +01:00
return ;
}
if ( ! ( oldChannel instanceof Ci . nsIWritablePropertyBag ) ) {
return ;
}
2015-01-27 16:37:02 +01:00
// TODO: what if a behind-the-scene request is being redirected?
// This data is present only for tabbed requests, so if this throws,
// the redirection won't be evaluated and canceled (if necessary)
2015-01-02 18:41:41 +01:00
var channelData = oldChannel . getProperty ( location . host + 'reqdata' ) ;
2015-01-27 16:37:02 +01:00
if ( this . handlePopup ( URI , channelData [ 1 ] , channelData [ 2 ] ) ) {
2015-01-02 18:41:41 +01:00
result = this . ABORT ;
return ;
}
var details = {
2015-01-27 16:37:02 +01:00
type : channelData [ 0 ] ,
tabId : channelData [ 1 ] ,
frameId : channelData [ 3 ] ,
parentFrameId : channelData [ 4 ]
2015-01-02 18:41:41 +01:00
} ;
2015-01-26 20:26:45 +01:00
if ( this . handleRequest ( newChannel , URI , details ) ) {
2015-01-02 18:41:41 +01:00
result = this . ABORT ;
return ;
}
2015-01-12 17:45:09 +01:00
// Carry the data on in case of multiple redirects
2015-01-02 18:41:41 +01:00
if ( newChannel instanceof Ci . nsIWritablePropertyBag ) {
newChannel . setProperty ( location . host + 'reqdata' , channelData ) ;
}
} catch ( ex ) {
// console.error(ex);
} finally {
callback . onRedirectVerifyCallback ( result ) ;
}
2014-12-24 23:11:36 +01:00
}
2014-12-02 08:35:25 +01:00
} ;
/******************************************************************************/
2014-12-28 10:56:09 +01:00
vAPI . net = { } ;
/******************************************************************************/
2014-12-09 21:56:17 +01:00
vAPI . net . registerListeners = function ( ) {
2015-01-11 18:41:52 +01:00
// Since it's not used
this . onBeforeSendHeaders = null ;
2014-12-24 23:11:36 +01:00
this . onBeforeRequest . types = new Set ( this . onBeforeRequest . types ) ;
2014-12-07 20:51:49 +01:00
2014-12-24 23:11:36 +01:00
var shouldLoadListenerMessageName = location . host + ':shouldLoad' ;
var shouldLoadListener = function ( e ) {
2015-01-02 18:41:41 +01:00
var details = e . data ;
2014-12-24 23:11:36 +01:00
var lastRequest = httpObserver . lastRequest ;
2015-01-02 18:41:41 +01:00
lastRequest . url = details . url ;
lastRequest . type = details . type ;
2014-12-24 23:11:36 +01:00
lastRequest . tabId = vAPI . tabs . getTabId ( e . target ) ;
2015-01-02 18:41:41 +01:00
lastRequest . frameId = details . frameId ;
lastRequest . parentFrameId = details . parentFrameId ;
2015-01-11 18:41:52 +01:00
lastRequest . openerURL = details . openerURL ;
2014-12-09 21:56:17 +01:00
} ;
2014-12-07 20:51:49 +01:00
2014-12-16 13:44:34 +01:00
vAPI . messaging . globalMessageManager . addMessageListener (
2014-12-24 23:11:36 +01:00
shouldLoadListenerMessageName ,
shouldLoadListener
2014-12-09 21:56:17 +01:00
) ;
2014-12-16 13:44:34 +01:00
2014-12-28 10:56:09 +01:00
httpObserver . register ( ) ;
2014-12-24 23:11:36 +01:00
2015-01-12 17:45:09 +01:00
cleanupTasks . push ( function ( ) {
2014-12-16 13:44:34 +01:00
vAPI . messaging . globalMessageManager . removeMessageListener (
2014-12-24 23:11:36 +01:00
shouldLoadListenerMessageName ,
shouldLoadListener
2014-12-16 13:44:34 +01:00
) ;
2014-12-24 23:11:36 +01:00
2014-12-28 10:56:09 +01:00
httpObserver . unregister ( ) ;
2014-12-16 13:44:34 +01:00
} ) ;
2014-12-09 21:56:17 +01:00
} ;
2014-12-07 20:51:49 +01:00
/******************************************************************************/
2015-01-11 18:41:52 +01:00
vAPI . toolbarButton = {
id : location . host + '-button' ,
type : 'view' ,
viewId : location . host + '-panel' ,
label : vAPI . app . name ,
tooltiptext : vAPI . app . name ,
tabs : { /*tabId: {badge: 0, img: boolean}*/ }
} ;
/******************************************************************************/
vAPI . toolbarButton . init = function ( ) {
2015-01-16 11:42:34 +01:00
try {
var { CustomizableUI } = Cu . import ( 'resource:///modules/CustomizableUI.jsm' , null ) ;
} catch ( ex ) {
return ;
}
2015-01-11 18:41:52 +01:00
this . defaultArea = CustomizableUI . AREA _NAVBAR ;
this . styleURI = [
'#' + this . id + ' {' ,
'list-style-image: url(' ,
vAPI . getURL ( 'img/browsericons/icon16-off.svg' ) ,
');' ,
'}' ,
'#' + this . viewId + ', #' + this . viewId + ' > iframe {' ,
'width: 160px;' ,
'height: 290px;' ,
'overflow: hidden !important;' ,
'}'
] ;
var platformVersion = Services . appinfo . platformVersion ;
if ( Services . vc . compare ( platformVersion , '36.0' ) < 0 ) {
this . styleURI . push (
'#' + this . id + '[badge]:not([badge=""])::after {' ,
'position: absolute;' ,
'margin-left: -16px;' ,
'margin-top: 3px;' ,
'padding: 1px 2px;' ,
'font-size: 9px;' ,
'font-weight: bold;' ,
'color: #fff;' ,
'background: #666;' ,
'content: attr(badge);' ,
'}'
) ;
2015-02-28 11:58:09 +01:00
} else {
this . CUIEvents = { } ;
this . CUIEvents . onCustomizeEnd = function ( ) {
var wId = vAPI . toolbarButton . id ;
var buttonInPanel = CustomizableUI . getWidget ( wId ) . areaType === CustomizableUI . TYPE _MENU _PANEL ;
for ( var win of vAPI . tabs . getWindows ( ) ) {
var button = win . document . getElementById ( wId ) ;
if ( buttonInPanel ) {
button . classList . remove ( 'badged-button' ) ;
continue ;
}
if ( button === null ) {
continue ;
}
button . classList . add ( 'badged-button' ) ;
}
if ( buttonInPanel ) {
return ;
}
// Anonymous elements need some time to be reachable
setTimeout ( this . updateBadgeStyle , 0 ) ;
} . bind ( this . CUIEvents ) ;
this . CUIEvents . updateBadgeStyle = function ( ) {
var css = [
'background: #666' ,
'color: #fff'
] . join ( ';' ) ;
for ( var win of vAPI . tabs . getWindows ( ) ) {
var button = win . document . getElementById ( vAPI . toolbarButton . id ) ;
if ( button === null ) {
continue ;
}
var badge = button . ownerDocument . getAnonymousElementByAttribute (
button ,
'class' ,
'toolbarbutton-badge'
) ;
if ( ! badge ) {
return ;
}
badge . style . cssText = css ;
}
} ;
this . onCreated = function ( button ) {
button . setAttribute ( 'badge' , '' ) ;
setTimeout ( this . CUIEvents . onCustomizeEnd , 0 ) ;
} ;
CustomizableUI . addListener ( this . CUIEvents ) ;
2015-01-11 18:41:52 +01:00
}
this . styleURI = Services . io . newURI (
'data:text/css,' + encodeURIComponent ( this . styleURI . join ( '' ) ) ,
null ,
null
) ;
this . closePopup = function ( { target } ) {
CustomizableUI . hidePanelForNode (
target . ownerDocument . getElementById ( vAPI . toolbarButton . viewId )
) ;
} ;
CustomizableUI . createWidget ( this ) ;
vAPI . messaging . globalMessageManager . addMessageListener (
location . host + ':closePopup' ,
this . closePopup
) ;
2015-01-12 17:45:09 +01:00
cleanupTasks . push ( function ( ) {
2015-02-28 11:58:09 +01:00
if ( this . CUIEvents ) {
CustomizableUI . removeListener ( this . CUIEvents ) ;
}
2015-01-11 18:41:52 +01:00
CustomizableUI . destroyWidget ( this . id ) ;
vAPI . messaging . globalMessageManager . removeMessageListener (
location . host + ':closePopup' ,
this . closePopup
) ;
for ( var win of vAPI . tabs . getWindows ( ) ) {
var panel = win . document . getElementById ( this . viewId ) ;
panel . parentNode . removeChild ( panel ) ;
win . QueryInterface ( Ci . nsIInterfaceRequestor )
. getInterface ( Ci . nsIDOMWindowUtils )
. removeSheet ( this . styleURI , 1 ) ;
}
} . bind ( this ) ) ;
} ;
/******************************************************************************/
vAPI . toolbarButton . onBeforeCreated = function ( doc ) {
var panel = doc . createElement ( 'panelview' ) ;
panel . setAttribute ( 'id' , this . viewId ) ;
var iframe = doc . createElement ( 'iframe' ) ;
iframe . setAttribute ( 'type' , 'content' ) ;
doc . getElementById ( 'PanelUI-multiView' )
. appendChild ( panel )
. appendChild ( iframe ) ;
var updateTimer = null ;
var delayedResize = function ( ) {
if ( updateTimer ) {
return ;
}
2015-02-12 15:24:45 +01:00
updateTimer = setTimeout ( resizePopup , 10 ) ;
2015-01-11 18:41:52 +01:00
} ;
var resizePopup = function ( ) {
2015-02-12 15:24:45 +01:00
updateTimer = null ;
2015-01-11 18:41:52 +01:00
var body = iframe . contentDocument . body ;
panel . parentNode . style . maxWidth = 'none' ;
2015-02-12 15:24:45 +01:00
// https://github.com/gorhill/uBlock/issues/730
// Voodoo programming: this recipe works
panel . style . height = iframe . style . height = body . clientHeight . toString ( ) + 'px' ;
panel . style . width = iframe . style . width = body . clientWidth . toString ( ) + 'px' ;
if ( iframe . clientHeight !== body . clientHeight || iframe . clientWidth !== body . clientWidth ) {
delayedResize ( ) ;
}
2015-01-11 18:41:52 +01:00
} ;
var onPopupReady = function ( ) {
var win = this . contentWindow ;
if ( ! win || win . location . host !== location . host ) {
return ;
}
2015-01-13 17:20:16 +01:00
new win . MutationObserver ( delayedResize ) . observe ( win . document . body , {
2015-01-11 18:41:52 +01:00
attributes : true ,
characterData : true ,
subtree : true
} ) ;
delayedResize ( ) ;
} ;
iframe . addEventListener ( 'load' , onPopupReady , true ) ;
doc . defaultView . QueryInterface ( Ci . nsIInterfaceRequestor )
. getInterface ( Ci . nsIDOMWindowUtils )
. loadSheet ( this . styleURI , 1 ) ;
} ;
/******************************************************************************/
vAPI . toolbarButton . onViewShowing = function ( { target } ) {
target . firstChild . setAttribute ( 'src' , vAPI . getURL ( 'popup.html' ) ) ;
} ;
/******************************************************************************/
vAPI . toolbarButton . onViewHiding = function ( { target } ) {
target . parentNode . style . maxWidth = '' ;
target . firstChild . setAttribute ( 'src' , 'about:blank' ) ;
} ;
/******************************************************************************/
vAPI . toolbarButton . init ( ) ;
/******************************************************************************/
2014-12-19 21:24:30 +01:00
vAPI . contextMenu = {
contextMap : {
frame : 'inFrame' ,
link : 'onLink' ,
image : 'onImage' ,
audio : 'onAudio' ,
video : 'onVideo' ,
editable : 'onEditableArea'
}
} ;
/******************************************************************************/
vAPI . contextMenu . displayMenuItem = function ( e ) {
var doc = e . target . ownerDocument ;
var gContextMenu = doc . defaultView . gContextMenu ;
2015-01-11 18:41:52 +01:00
if ( ! gContextMenu . browser ) {
return ;
}
2014-12-19 21:24:30 +01:00
var menuitem = doc . getElementById ( vAPI . contextMenu . menuItemId ) ;
2015-01-15 13:24:35 +01:00
var currentURI = gContextMenu . browser . currentURI ;
2014-12-19 21:24:30 +01:00
2015-01-21 17:13:32 +01:00
// https://github.com/gorhill/uBlock/issues/105
// TODO: Should the element picker works on any kind of pages?
2015-01-15 13:24:35 +01:00
if ( ! currentURI . schemeIs ( 'http' ) && ! currentURI . schemeIs ( 'https' ) ) {
2014-12-19 21:24:30 +01:00
menuitem . hidden = true ;
return ;
}
var ctx = vAPI . contextMenu . contexts ;
2014-12-28 21:26:06 +01:00
if ( ! ctx ) {
2014-12-19 21:24:30 +01:00
menuitem . hidden = false ;
return ;
}
var ctxMap = vAPI . contextMenu . contextMap ;
2014-12-28 21:26:06 +01:00
for ( var context of ctx ) {
if ( context === 'page' && ! gContextMenu . onLink && ! gContextMenu . onImage
2014-12-19 21:24:30 +01:00
&& ! gContextMenu . onEditableArea && ! gContextMenu . inFrame
2014-12-28 21:26:06 +01:00
&& ! gContextMenu . onVideo && ! gContextMenu . onAudio ) {
2014-12-19 21:24:30 +01:00
menuitem . hidden = false ;
return ;
}
2014-12-28 21:26:06 +01:00
if ( gContextMenu [ ctxMap [ context ] ] ) {
2014-12-19 21:24:30 +01:00
menuitem . hidden = false ;
return ;
}
}
menuitem . hidden = true ;
} ;
2014-12-16 13:44:34 +01:00
/******************************************************************************/
2014-12-19 21:24:30 +01:00
vAPI . contextMenu . register = function ( doc ) {
2014-12-28 21:26:06 +01:00
if ( ! this . menuItemId ) {
2014-12-19 21:24:30 +01:00
return ;
}
2015-02-12 19:19:17 +01:00
if ( vAPI . fennec ) {
// Fennec
// TODO
/ *
var nativeWindow = doc . defaultView . NativeWindow ;
contextId = nativeWindow . contextmenus . add (
this . menuLabel ,
nativeWindow . contextmenus . linkOpenableContext , // TODO https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/NativeWindow/contextmenus/add
this . onCommand ) ;
* /
} else {
// desktop Firefox
var contextMenu = doc . getElementById ( 'contentAreaContextMenu' ) ;
var menuitem = doc . createElement ( 'menuitem' ) ;
menuitem . setAttribute ( 'id' , this . menuItemId ) ;
menuitem . setAttribute ( 'label' , this . menuLabel ) ;
menuitem . setAttribute ( 'image' , vAPI . getURL ( 'img/browsericons/icon16.svg' ) ) ;
menuitem . setAttribute ( 'class' , 'menuitem-iconic' ) ;
menuitem . addEventListener ( 'command' , this . onCommand ) ;
contextMenu . addEventListener ( 'popupshowing' , this . displayMenuItem ) ;
contextMenu . insertBefore ( menuitem , doc . getElementById ( 'inspect-separator' ) ) ;
}
2014-12-19 21:24:30 +01:00
} ;
2014-12-16 13:44:34 +01:00
/******************************************************************************/
2014-12-19 21:24:30 +01:00
vAPI . contextMenu . unregister = function ( doc ) {
2014-12-28 21:26:06 +01:00
if ( ! this . menuItemId ) {
2014-12-19 21:24:30 +01:00
return ;
}
2015-02-12 19:19:17 +01:00
if ( vAPI . fennec ) {
// Fennec
// TODO
} else {
// desktop Firefox
var menuitem = doc . getElementById ( this . menuItemId ) ;
var contextMenu = menuitem . parentNode ;
menuitem . removeEventListener ( 'command' , this . onCommand ) ;
contextMenu . removeEventListener ( 'popupshowing' , this . displayMenuItem ) ;
contextMenu . removeChild ( menuitem ) ;
}
2014-12-19 21:24:30 +01:00
} ;
/******************************************************************************/
vAPI . contextMenu . create = function ( details , callback ) {
this . menuItemId = details . id ;
this . menuLabel = details . title ;
this . contexts = details . contexts ;
2014-12-28 21:26:06 +01:00
if ( Array . isArray ( this . contexts ) && this . contexts . length ) {
2014-12-19 21:24:30 +01:00
this . contexts = this . contexts . indexOf ( 'all' ) === - 1 ? this . contexts : null ;
2014-12-28 21:26:06 +01:00
} else {
2014-12-19 21:24:30 +01:00
// default in Chrome
this . contexts = [ 'page' ] ;
}
this . onCommand = function ( ) {
var gContextMenu = this . ownerDocument . defaultView . gContextMenu ;
var details = {
2015-01-16 09:01:40 +01:00
menuItemId : this . id
2014-12-19 21:24:30 +01:00
} ;
2014-12-28 21:26:06 +01:00
if ( gContextMenu . inFrame ) {
2015-01-16 09:01:40 +01:00
details . tagName = 'iframe' ;
2014-12-19 21:24:30 +01:00
details . frameUrl = gContextMenu . focusedWindow . location . href ;
2015-01-16 09:01:40 +01:00
} else if ( gContextMenu . onImage ) {
details . tagName = 'img' ;
details . srcUrl = gContextMenu . mediaURL ;
} else if ( gContextMenu . onAudio ) {
details . tagName = 'audio' ;
details . srcUrl = gContextMenu . mediaURL ;
} else if ( gContextMenu . onVideo ) {
details . tagName = 'video' ;
2014-12-19 21:24:30 +01:00
details . srcUrl = gContextMenu . mediaURL ;
2014-12-28 21:26:06 +01:00
} else if ( gContextMenu . onLink ) {
2015-01-16 09:01:40 +01:00
details . tagName = 'a' ;
2014-12-19 21:24:30 +01:00
details . linkUrl = gContextMenu . linkURL ;
}
callback ( details , {
id : vAPI . tabs . getTabId ( gContextMenu . browser ) ,
2015-01-14 23:45:55 +01:00
url : gContextMenu . browser . currentURI . asciiSpec
2014-12-19 21:24:30 +01:00
} ) ;
} ;
2014-12-28 21:26:06 +01:00
for ( var win of vAPI . tabs . getWindows ( ) ) {
2014-12-19 21:24:30 +01:00
this . register ( win . document ) ;
}
} ;
/******************************************************************************/
2014-12-09 21:56:17 +01:00
vAPI . lastError = function ( ) {
return null ;
2014-12-07 20:51:49 +01:00
} ;
/******************************************************************************/
2014-12-20 11:08:33 +01:00
// This is called only once, when everything has been loaded in memory after
// the extension was launched. It can be used to inject content scripts
// in already opened web pages, to remove whatever nuisance could make it to
// the web pages before uBlock was ready.
vAPI . onLoadAllCompleted = function ( ) { } ;
/******************************************************************************/
2015-01-15 01:43:10 +01:00
// Likelihood is that we do not have to punycode: given punycode overhead,
// it's faster to check and skip than do it unconditionally all the time.
var punycodeHostname = punycode . toASCII ;
var isNotASCII = /[^\x21-\x7F]/ ;
vAPI . punycodeHostname = function ( hostname ) {
return isNotASCII . test ( hostname ) ? punycodeHostname ( hostname ) : hostname ;
} ;
2015-01-14 23:45:55 +01:00
vAPI . punycodeURL = function ( url ) {
2015-01-15 13:24:35 +01:00
if ( isNotASCII . test ( url ) ) {
return Services . io . newURI ( url , null , null ) . asciiSpec ;
2015-01-15 01:43:10 +01:00
}
2015-01-15 13:24:35 +01:00
return url ;
2015-01-14 23:45:55 +01:00
} ;
/******************************************************************************/
2015-01-15 13:24:35 +01:00
// clean up when the extension is disabled
window . addEventListener ( 'unload' , function ( ) {
for ( var cleanup of cleanupTasks ) {
cleanup ( ) ;
}
// frameModule needs to be cleared too
var frameModule = { } ;
Cu . import ( vAPI . getURL ( 'frameModule.js' ) , frameModule ) ;
frameModule . contentObserver . unregister ( ) ;
Cu . unload ( vAPI . getURL ( 'frameModule.js' ) ) ;
} ) ;
/******************************************************************************/
2014-11-24 20:00:27 +01:00
} ) ( ) ;
/******************************************************************************/