@ -41,6 +41,10 @@ var vAPI = self.vAPI = self.vAPI || {};
vAPI . firefox = true ;
vAPI . firefox = true ;
vAPI . fennec = Components . classes [ "@mozilla.org/xre/app-info;1" ]
. getService ( Components . interfaces . nsIXULAppInfo )
. ID == "{aa3c5121-dab2-40e2-81ca-7ea25febc110}" ;
/******************************************************************************/
/******************************************************************************/
vAPI . app = {
vAPI . app = {
@ -262,16 +266,23 @@ var windowWatcher = {
return ;
return ;
}
}
if ( ! this . gBrowser || ! this . gBrowser . tabContainer ) {
return ;
}
if ( this . gBrowser && this . gBrowser . tabContainer ) {
// desktop Firefox
var tC = this . gBrowser . tabContainer ;
var tC = this . gBrowser . tabContainer ;
this . gBrowser . addTabsProgressListener ( tabWatcher ) ;
this . gBrowser . addTabsProgressListener ( tabWatcher ) ;
tC . addEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
tC . addEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
tC . addEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
tC . addEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
} 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 ) ;
}
vAPI . contextMenu . register ( this . document ) ;
vAPI . contextMenu . register ( this . document ) ;
// when new window is opened TabSelect doesn't run on the selected tab?
// when new window is opened TabSelect doesn't run on the selected tab?
@ -294,7 +305,14 @@ var tabWatcher = {
} ,
} ,
onTabSelect : function ( { target : tab } ) {
onTabSelect : function ( { target : tab } ) {
var URI = tab . linkedBrowser . currentURI ;
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 ;
}
var aboutPath = URI . schemeIs ( 'about' ) && URI . path ;
var aboutPath = URI . schemeIs ( 'about' ) && URI . path ;
var tabId = vAPI . tabs . getTabId ( tab ) ;
var tabId = vAPI . tabs . getTabId ( tab ) ;
@ -334,7 +352,25 @@ var tabWatcher = {
tabId : tabId ,
tabId : tabId ,
url : location . asciiSpec
url : location . asciiSpec
} ) ;
} ) ;
} ,
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
} ) ;
} ,
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -367,11 +403,13 @@ vAPI.tabs.registerListeners = function() {
for ( var win of vAPI . tabs . getWindows ( ) ) {
for ( var win of vAPI . tabs . getWindows ( ) ) {
vAPI . contextMenu . unregister ( win . document ) ;
vAPI . contextMenu . unregister ( win . document ) ;
win . removeEventListener ( 'DOMContentLoaded' , windowWatcher . onReady ) ;
win . removeEventListener ( 'DOMContentLoaded' , windowWatcher . onReady ) ;
win . gBrowser . removeTabsProgressListener ( tabWatcher ) ;
if ( win . gBrowser && win . gBrowser . tabContainer ) {
// desktop Firefox
var tC = win . gBrowser . tabContainer ;
var tC = win . gBrowser . tabContainer ;
win . gBrowser . removeTabsProgressListener ( tabWatcher ) ;
tC . removeEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
tC . removeEventListener ( 'TabClose' , tabWatcher . onTabClose ) ;
tC . removeEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
tC . removeEventListener ( 'TabSelect' , tabWatcher . onTabSelect ) ;
@ -383,6 +421,23 @@ vAPI.tabs.registerListeners = function() {
win . gBrowser . removeTab ( tab ) ;
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 ;
if ( URI . schemeIs ( 'chrome' ) && URI . host === location . host ) {
win . BrowserApp . closeTab ( tab ) ;
}
}
}
}
}
} ) ;
} ) ;
} ;
} ;
@ -390,10 +445,34 @@ vAPI.tabs.registerListeners = function() {
/******************************************************************************/
/******************************************************************************/
vAPI . tabs . getTabId = function ( target ) {
vAPI . tabs . getTabId = function ( target ) {
if ( vAPI . fennec ) {
// Fennec
if ( target . browser ) {
// target is a tab
return target . id ;
}
// 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 ;
}
}
}
return - 1 ;
} else {
// desktop Firefox
if ( target . linkedPanel ) {
if ( target . linkedPanel ) {
// target is a tab
return target . linkedPanel ;
return target . linkedPanel ;
}
}
// target is a browser
var i , gBrowser = target . ownerDocument . defaultView . gBrowser ;
var i , gBrowser = target . ownerDocument . defaultView . gBrowser ;
if ( ! gBrowser ) {
if ( ! gBrowser ) {
@ -417,20 +496,39 @@ vAPI.tabs.getTabId = function(target) {
}
}
return i ;
return i ;
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/
vAPI . tabs . get = function ( tabId , callback ) {
vAPI . tabs . get = function ( tabId , callback ) {
var tab , windows ;
var tab , windows , win ;
if ( tabId === null ) {
if ( tabId === null ) {
tab = Services . wm . getMostRecentWindow ( 'navigator:browser' ) . gBrowser . selectedTab ;
win = Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
if ( win . gBrowser ) {
// desktop Firefox
tab = win . gBrowser . selectedTab ;
} else if ( win . BrowserApp ) {
// Fennec
tab = win . BrowserApp . selectedTab ;
}
tabId = vAPI . tabs . getTabId ( tab ) ;
tabId = vAPI . tabs . getTabId ( tab ) ;
} else {
} else {
windows = this . getWindows ( ) ;
windows = this . getWindows ( ) ;
for ( var win of windows ) {
if ( vAPI . fennec ) {
// Fennec
for ( win of windows ) {
tab = win . BrowserApp . getTabForId ( tabId ) ;
if ( tab ) {
break ;
}
}
} else {
// desktop Firefox
for ( win of windows ) {
tab = win . gBrowser . tabContainer . querySelector (
tab = win . gBrowser . tabContainer . querySelector (
'tab[linkedpanel="' + tabId + '"]'
'tab[linkedpanel="' + tabId + '"]'
) ;
) ;
@ -440,6 +538,7 @@ vAPI.tabs.get = function(tabId, callback) {
}
}
}
}
}
}
}
// for internal use
// for internal use
if ( typeof callback !== 'function' ) {
if ( typeof callback !== 'function' ) {
@ -451,8 +550,10 @@ vAPI.tabs.get = function(tabId, callback) {
return ;
return ;
}
}
if ( tab . linkedBrowser ) {
// desktop Firefox
var browser = tab . linkedBrowser ;
var browser = tab . linkedBrowser ;
var gBrowser = browser . ownerDocument . defaultView . gBrowser ;
var gBrowser = win . gBrowser ;
if ( ! windows ) {
if ( ! windows ) {
windows = this . getWindows ( ) ;
windows = this . getWindows ( ) ;
@ -461,11 +562,29 @@ vAPI.tabs.get = function(tabId, callback) {
callback ( {
callback ( {
id : tabId ,
id : tabId ,
index : gBrowser . browsers . indexOf ( browser ) ,
index : gBrowser . browsers . indexOf ( browser ) ,
windowId : windows . indexOf ( browser . ownerDocument . defaultView ) ,
windowId : windows . indexOf ( win ) ,
active : tab === gBrowser . selectedTab ,
active : tab === gBrowser . selectedTab ,
url : browser . currentURI . asciiSpec ,
url : browser . currentURI . asciiSpec ,
title : tab . label
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
} ) ;
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -478,7 +597,16 @@ vAPI.tabs.getAll = function(window) {
continue ;
continue ;
}
}
for ( tab of win . gBrowser . tabs ) {
var tabList ;
if ( win . gBrowser ) {
// desktop Firefox
tabList = win . gBrowser . tabs ;
} else {
// Fennec
tabList = win . BrowserApp . tabs ;
}
for ( tab of tabList ) {
tabs . push ( tab ) ;
tabs . push ( tab ) ;
}
}
}
}
@ -528,11 +656,25 @@ vAPI.tabs.open = function(details) {
tabs = this . getAll ( ) ;
tabs = this . getAll ( ) ;
for ( tab of tabs ) {
for ( tab of tabs ) {
var browser = tab . linkedBrowser ;
var browser ;
if ( tab . linkedBrowser ) {
// desktop Firefox
browser = tab . linkedBrowser ;
} else {
// Fennec
browser = tab . browser ;
}
// Or simply .equals if we care about the fragment
// Or simply .equals if we care about the fragment
if ( URI . equalsExceptRef ( browser . currentURI ) ) {
if ( URI . equalsExceptRef ( browser . currentURI ) ) {
browser . ownerDocument . defaultView . gBrowser . selectedTab = tab ;
var win = browser . ownerDocument . defaultView ;
if ( win . gBrowser ) {
// desktop Firefox
win . gBrowser . selectedTab = tab ;
} else {
// Fennec
win . BrowserApp . selectTab ( tab ) ;
}
return ;
return ;
}
}
}
}
@ -542,7 +684,10 @@ vAPI.tabs.open = function(details) {
details . active = true ;
details . active = true ;
}
}
var gBrowser = Services . wm . getMostRecentWindow ( 'navigator:browser' ) . gBrowser ;
var win = Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
if ( win . gBrowser ) {
// desktop Firefox
var gBrowser = win . gBrowser ;
if ( details . index === - 1 ) {
if ( details . index === - 1 ) {
details . index = gBrowser . browsers . indexOf ( gBrowser . selectedBrowser ) + 1 ;
details . index = gBrowser . browsers . indexOf ( gBrowser . selectedBrowser ) + 1 ;
@ -564,6 +709,28 @@ vAPI.tabs.open = function(details) {
if ( details . index !== undefined ) {
if ( details . index !== undefined ) {
gBrowser . moveTabTo ( tab , details . index ) ;
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 ) ;
return ;
}
}
tab = BrowserApp . addTab ( details . url , { selected : details . active } ) ;
// note that it's impossible to move tabs on Fennec, so don't bother
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -573,13 +740,28 @@ vAPI.tabs.remove = function(tabIds) {
tabIds = [ tabIds ] ;
tabIds = [ tabIds ] ;
}
}
if ( vAPI . fennec ) {
// Fennec
for ( var win of this . getWindows ( ) ) {
var tabs = win . BrowserApp . tabs ;
if ( ! tabs ) {
continue ;
}
for ( var tab of tabs ) {
if ( tab . id in tabIds ) {
win . BrowserApp . closeTab ( tab ) ;
}
}
}
} else {
// desktop Firefox
tabIds = tabIds . map ( function ( tabId ) {
tabIds = tabIds . map ( function ( tabId ) {
return 'tab[linkedpanel="' + tabId + '"]' ;
return 'tab[linkedpanel="' + tabId + '"]' ;
} ) . join ( ',' ) ;
} ) . join ( ',' ) ;
for ( var win of this . getWindows ( ) ) {
for ( var win of this . getWindows ( ) ) {
var tabs = win . gBrowser . tabContainer . querySelectorAll ( tabIds ) ;
var tabs = win . gBrowser . tabContainer . querySelectorAll ( tabIds ) ;
if ( ! tabs ) {
if ( ! tabs ) {
continue ;
continue ;
}
}
@ -588,6 +770,7 @@ vAPI.tabs.remove = function(tabIds) {
win . gBrowser . removeTab ( tab ) ;
win . gBrowser . removeTab ( tab ) ;
}
}
}
}
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -596,8 +779,14 @@ vAPI.tabs.reload = function(tabId) {
var tab = this . get ( tabId ) ;
var tab = this . get ( tabId ) ;
if ( tab ) {
if ( tab ) {
if ( tab . browser ) {
// Fennec
tab . browser . reload ( ) ;
} else {
// desktop Firefox
tab . ownerDocument . defaultView . gBrowser . reloadTab ( tab ) ;
tab . ownerDocument . defaultView . gBrowser . reloadTab ( tab ) ;
}
}
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -638,8 +827,17 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
var win = badge === undefined
var win = badge === undefined
? iconStatus
? iconStatus
: Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
: Services . wm . getMostRecentWindow ( 'navigator:browser' ) ;
var curTabId = vAPI . tabs . getTabId ( win . gBrowser . selectedTab ) ;
var tb = vAPI . toolbarButton ;
var tb = vAPI . toolbarButton ;
var selectedTab , curTabId ;
if ( win . gBrowser ) {
// desktop Firefox
selectedTab = win . gBrowser . selectedTab ;
} else {
// Fennec
selectedTab = win . BrowserApp . selectedTab ;
}
curTabId = vAPI . tabs . getTabId ( selectedTab ) ;
// from 'TabSelect' event
// from 'TabSelect' event
if ( tabId === undefined ) {
if ( tabId === undefined ) {
@ -1503,6 +1701,19 @@ vAPI.contextMenu.register = function(doc) {
return ;
return ;
}
}
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 contextMenu = doc . getElementById ( 'contentAreaContextMenu' ) ;
var menuitem = doc . createElement ( 'menuitem' ) ;
var menuitem = doc . createElement ( 'menuitem' ) ;
menuitem . setAttribute ( 'id' , this . menuItemId ) ;
menuitem . setAttribute ( 'id' , this . menuItemId ) ;
@ -1512,6 +1723,7 @@ vAPI.contextMenu.register = function(doc) {
menuitem . addEventListener ( 'command' , this . onCommand ) ;
menuitem . addEventListener ( 'command' , this . onCommand ) ;
contextMenu . addEventListener ( 'popupshowing' , this . displayMenuItem ) ;
contextMenu . addEventListener ( 'popupshowing' , this . displayMenuItem ) ;
contextMenu . insertBefore ( menuitem , doc . getElementById ( 'inspect-separator' ) ) ;
contextMenu . insertBefore ( menuitem , doc . getElementById ( 'inspect-separator' ) ) ;
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -1521,11 +1733,18 @@ vAPI.contextMenu.unregister = function(doc) {
return ;
return ;
}
}
if ( vAPI . fennec ) {
// Fennec
// TODO
} else {
// desktop Firefox
var menuitem = doc . getElementById ( this . menuItemId ) ;
var menuitem = doc . getElementById ( this . menuItemId ) ;
var contextMenu = menuitem . parentNode ;
var contextMenu = menuitem . parentNode ;
menuitem . removeEventListener ( 'command' , this . onCommand ) ;
menuitem . removeEventListener ( 'command' , this . onCommand ) ;
contextMenu . removeEventListener ( 'popupshowing' , this . displayMenuItem ) ;
contextMenu . removeEventListener ( 'popupshowing' , this . displayMenuItem ) ;
contextMenu . removeChild ( menuitem ) ;
contextMenu . removeChild ( menuitem ) ;
}
} ;
} ;
/******************************************************************************/
/******************************************************************************/