forked from mirrors/gecko-dev
		
	 eee83aa5d8
			
		
	
	
		eee83aa5d8
		
	
	
	
	
		
			
			--HG-- rename : browser/components/newtab/tests/xpcshell/test_NewTabURL.js => browser/components/newtab/tests/xpcshell/test_AboutNewTabService.js
		
			
				
	
	
		
			512 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			512 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
 | |
|                                    "@mozilla.org/browser/aboutnewtab-service;1",
 | |
|                                    "nsIAboutNewTabService");
 | |
| 
 | |
| Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 | |
| var {
 | |
|   EventManager,
 | |
|   ignoreEvent,
 | |
|   runSafe,
 | |
| } = ExtensionUtils;
 | |
| 
 | |
| // This function is pretty tightly tied to Extension.jsm.
 | |
| // Its job is to fill in the |tab| property of the sender.
 | |
| function getSender(context, target, sender)
 | |
| {
 | |
|   // The message was sent from a content script to a <browser> element.
 | |
|   // We can just get the |tab| from |target|.
 | |
|   if (target instanceof Ci.nsIDOMXULElement) {
 | |
|     // The message came from a content script.
 | |
|     let tabbrowser = target.ownerDocument.defaultView.gBrowser;
 | |
|     if (!tabbrowser) {
 | |
|       return;
 | |
|     }
 | |
|     let tab = tabbrowser.getTabForBrowser(target);
 | |
| 
 | |
|     sender.tab = TabManager.convert(context.extension, tab);
 | |
|   } else {
 | |
|     // The message came from an ExtensionPage. In that case, it should
 | |
|     // include a tabId property (which is filled in by the page-open
 | |
|     // listener below).
 | |
|     if ("tabId" in sender) {
 | |
|       sender.tab = TabManager.convert(context.extension, TabManager.getTab(sender.tabId));
 | |
|       delete sender.tabId;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // WeakMap[ExtensionPage -> {tab, parentWindow}]
 | |
| var pageDataMap = new WeakMap();
 | |
| 
 | |
| // This listener fires whenever an extension page opens in a tab
 | |
| // (either initiated by the extension or the user). Its job is to fill
 | |
| // in some tab-specific details and keep data around about the
 | |
| // ExtensionPage.
 | |
| extensions.on("page-load", (type, page, params, sender, delegate) => {
 | |
|   if (params.type == "tab" || params.type == "popup") {
 | |
|     let browser = params.docShell.chromeEventHandler;
 | |
| 
 | |
|     let parentWindow = browser.ownerDocument.defaultView;
 | |
|     page.windowId = WindowManager.getId(parentWindow);
 | |
| 
 | |
|     let tab = null;
 | |
|     if (params.type == "tab") {
 | |
|       tab = parentWindow.gBrowser.getTabForBrowser(browser);
 | |
|       sender.tabId = TabManager.getId(tab);
 | |
|       page.tabId = TabManager.getId(tab);
 | |
|     }
 | |
| 
 | |
|     pageDataMap.set(page, {tab, parentWindow});
 | |
|   }
 | |
| 
 | |
|   delegate.getSender = getSender;
 | |
| });
 | |
| 
 | |
| extensions.on("page-unload", (type, page) => {
 | |
|   pageDataMap.delete(page);
 | |
| });
 | |
| 
 | |
| extensions.on("page-shutdown", (type, page) => {
 | |
|   if (pageDataMap.has(page)) {
 | |
|     let {tab, parentWindow} = pageDataMap.get(page);
 | |
|     pageDataMap.delete(page);
 | |
| 
 | |
|     if (tab) {
 | |
|       parentWindow.gBrowser.removeTab(tab);
 | |
|     }
 | |
|   }
 | |
| });
 | |
| 
 | |
| extensions.on("fill-browser-data", (type, browser, data, result) => {
 | |
|   let tabId = TabManager.getBrowserId(browser);
 | |
|   if (tabId == -1) {
 | |
|     result.cancel = true;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   data.tabId = tabId;
 | |
| });
 | |
| 
 | |
| global.currentWindow = function(context)
 | |
| {
 | |
|   let pageData = pageDataMap.get(context);
 | |
|   if (pageData) {
 | |
|     return pageData.parentWindow;
 | |
|   }
 | |
|   return WindowManager.topWindow;
 | |
| }
 | |
| 
 | |
| // TODO: activeTab permission
 | |
| 
 | |
| extensions.registerAPI((extension, context) => {
 | |
|   let self = {
 | |
|     tabs: {
 | |
|       onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
 | |
|         let tab = event.originalTarget;
 | |
|         let tabId = TabManager.getId(tab);
 | |
|         let windowId = WindowManager.getId(tab.ownerDocument.defaultView);
 | |
|         fire({tabId, windowId});
 | |
|       }).api(),
 | |
| 
 | |
|       onCreated: new EventManager(context, "tabs.onCreated", fire => {
 | |
|         let listener = event => {
 | |
|           let tab = event.originalTarget;
 | |
|           fire({tab: TabManager.convert(extension, tab)});
 | |
|         };
 | |
| 
 | |
|         let windowListener = window => {
 | |
|           for (let tab of window.gBrowser.tabs) {
 | |
|             fire({tab: TabManager.convert(extension, tab)});
 | |
|           }
 | |
|         };
 | |
| 
 | |
|         WindowListManager.addOpenListener(windowListener, false);
 | |
|         AllWindowEvents.addListener("TabOpen", listener);
 | |
|         return () => {
 | |
|           WindowListManager.removeOpenListener(windowListener);
 | |
|           AllWindowEvents.removeListener("TabOpen", listener);
 | |
|         };
 | |
|       }).api(),
 | |
| 
 | |
|       onUpdated: new EventManager(context, "tabs.onUpdated", fire => {
 | |
|         function sanitize(extension, changeInfo) {
 | |
|           let result = {};
 | |
|           let nonempty = false;
 | |
|           for (let prop in changeInfo) {
 | |
|             if ((prop != "favIconUrl" && prop != "url") || extension.hasPermission("tabs")) {
 | |
|               nonempty = true;
 | |
|               result[prop] = changeInfo[prop];
 | |
|             }
 | |
|           }
 | |
|           return [nonempty, result];
 | |
|         }
 | |
| 
 | |
|         let listener = event => {
 | |
|           let tab = event.originalTarget;
 | |
|           let window = tab.ownerDocument.defaultView;
 | |
|           let tabId = TabManager.getId(tab);
 | |
| 
 | |
|           let changeInfo = {};
 | |
|           let needed = false;
 | |
|           if (event.type == "TabAttrModified") {
 | |
|             if (event.detail.changed.indexOf("image") != -1) {
 | |
|               changeInfo.favIconUrl = window.gBrowser.getIcon(tab);
 | |
|               needed = true;
 | |
|             }
 | |
|           } else if (event.type == "TabPinned") {
 | |
|             changeInfo.pinned = true;
 | |
|             needed = true;
 | |
|           } else if (event.type == "TabUnpinned") {
 | |
|             changeInfo.pinned = false;
 | |
|             needed = true;
 | |
|           }
 | |
| 
 | |
|           [needed, changeInfo] = sanitize(extension, changeInfo);
 | |
|           if (needed) {
 | |
|             fire(tabId, changeInfo, TabManager.convert(extension, tab));
 | |
|           }
 | |
|         };
 | |
|         let progressListener = {
 | |
|           onStateChange(browser, webProgress, request, stateFlags, statusCode) {
 | |
|             if (!webProgress.isTopLevel) {
 | |
|               return;
 | |
|             }
 | |
| 
 | |
|             let status;
 | |
|             if (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
 | |
|               if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
 | |
|                 status = "loading";
 | |
|               } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
 | |
|                 status = "complete";
 | |
|               }
 | |
|             } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
 | |
|                        statusCode == Cr.NS_BINDING_ABORTED) {
 | |
|               status = "complete";
 | |
|             }
 | |
| 
 | |
|             let gBrowser = browser.ownerDocument.defaultView.gBrowser;
 | |
|             let tab = gBrowser.getTabForBrowser(browser);
 | |
|             let tabId = TabManager.getId(tab);
 | |
|             let [needed, changeInfo] = sanitize(extension, {status});
 | |
|             fire(tabId, changeInfo, TabManager.convert(extension, tab));
 | |
|           },
 | |
| 
 | |
|           onLocationChange(browser, webProgress, request, locationURI, flags) {
 | |
|             let gBrowser = browser.ownerDocument.defaultView.gBrowser;
 | |
|             let tab = gBrowser.getTabForBrowser(browser);
 | |
|             let tabId = TabManager.getId(tab);
 | |
|             let [needed, changeInfo] = sanitize(extension, {url: locationURI.spec});
 | |
|             if (needed) {
 | |
|               fire(tabId, changeInfo, TabManager.convert(extension, tab));
 | |
|             }
 | |
|           },
 | |
|         };
 | |
| 
 | |
|         AllWindowEvents.addListener("progress", progressListener);
 | |
|         AllWindowEvents.addListener("TabAttrModified", listener);
 | |
|         AllWindowEvents.addListener("TabPinned", listener);
 | |
|         AllWindowEvents.addListener("TabUnpinned", listener);
 | |
|         return () => {
 | |
|           AllWindowEvents.removeListener("progress", progressListener);
 | |
|           AllWindowEvents.addListener("TabAttrModified", listener);
 | |
|           AllWindowEvents.addListener("TabPinned", listener);
 | |
|           AllWindowEvents.addListener("TabUnpinned", listener);
 | |
|         };
 | |
|       }).api(),
 | |
| 
 | |
|       onReplaced: ignoreEvent(),
 | |
| 
 | |
|       onRemoved: new EventManager(context, "tabs.onRemoved", fire => {
 | |
|         let tabListener = event => {
 | |
|           let tab = event.originalTarget;
 | |
|           let tabId = TabManager.getId(tab);
 | |
|           let windowId = WindowManager.getId(tab.ownerDocument.defaultView);
 | |
|           let removeInfo = {windowId, isWindowClosing: false};
 | |
|           fire(tabId, removeInfo);
 | |
|         };
 | |
| 
 | |
|         let windowListener = window => {
 | |
|           for (let tab of window.gBrowser.tabs) {
 | |
|             let tabId = TabManager.getId(tab);
 | |
|             let windowId = WindowManager.getId(window);
 | |
|             let removeInfo = {windowId, isWindowClosing: true};
 | |
|             fire(tabId, removeInfo);
 | |
|           }
 | |
|         };
 | |
| 
 | |
|         WindowListManager.addCloseListener(windowListener);
 | |
|         AllWindowEvents.addListener("TabClose", tabListener);
 | |
|         return () => {
 | |
|           WindowListManager.removeCloseListener(windowListener);
 | |
|           AllWindowEvents.removeListener("TabClose", tabListener);
 | |
|         };
 | |
|       }).api(),
 | |
| 
 | |
|       create: function(createProperties, callback) {
 | |
|         if (!createProperties) {
 | |
|           createProperties = {};
 | |
|         }
 | |
| 
 | |
|         let url = createProperties.url || aboutNewTabService.newTabURL;
 | |
|         url = extension.baseURI.resolve(url);
 | |
| 
 | |
|         function createInWindow(window) {
 | |
|           let tab = window.gBrowser.addTab(url);
 | |
| 
 | |
|           let active = true;
 | |
|           if ("active" in createProperties) {
 | |
|             active = createProperties.active;
 | |
|           } else if ("selected" in createProperties) {
 | |
|             active = createProperties.selected;
 | |
|           }
 | |
|           if (active) {
 | |
|             window.gBrowser.selectedTab = tab;
 | |
|           }
 | |
| 
 | |
|           if ("index" in createProperties) {
 | |
|             window.gBrowser.moveTabTo(tab, createProperties.index);
 | |
|           }
 | |
| 
 | |
|           if (createProperties.pinned) {
 | |
|             window.gBrowser.pinTab(tab);
 | |
|           }
 | |
| 
 | |
|           if (callback) {
 | |
|             runSafe(context, callback, TabManager.convert(extension, tab));
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         let window = "windowId" in createProperties ?
 | |
|           WindowManager.getWindow(createProperties.windowId) :
 | |
|           WindowManager.topWindow;
 | |
|         if (!window.gBrowser) {
 | |
|           let obs = (finishedWindow, topic, data) => {
 | |
|             if (finishedWindow != window) {
 | |
|               return;
 | |
|             }
 | |
|             Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
 | |
|             createInWindow(window);
 | |
|           };
 | |
|           Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
 | |
|         } else {
 | |
|           createInWindow(window);
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       remove: function(tabs, callback) {
 | |
|         if (!Array.isArray(tabs)) {
 | |
|           tabs = [tabs];
 | |
|         }
 | |
| 
 | |
|         for (let tabId of tabs) {
 | |
|           let tab = TabManager.getTab(tabId);
 | |
|           tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
 | |
|         }
 | |
| 
 | |
|         if (callback) {
 | |
|           runSafe(context, callback);
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       update: function(...args) {
 | |
|         let tabId, updateProperties, callback;
 | |
|         if (args.length == 1) {
 | |
|           updateProperties = args[0];
 | |
|         } else {
 | |
|           [tabId, updateProperties, callback] = args;
 | |
|         }
 | |
| 
 | |
|         let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
 | |
|         let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
 | |
|         if ("url" in updateProperties) {
 | |
|           tab.linkedBrowser.loadURI(updateProperties.url);
 | |
|         }
 | |
|         if ("active" in updateProperties) {
 | |
|           if (updateProperties.active) {
 | |
|             tabbrowser.selectedTab = tab;
 | |
|           } else {
 | |
|             // Not sure what to do here? Which tab should we select?
 | |
|           }
 | |
|         }
 | |
|         if ("pinned" in updateProperties) {
 | |
|           if (updateProperties.pinned) {
 | |
|             tabbrowser.pinTab(tab);
 | |
|           } else {
 | |
|             tabbrowser.unpinTab(tab);
 | |
|           }
 | |
|         }
 | |
|         // FIXME: highlighted/selected, openerTabId
 | |
| 
 | |
|         if (callback) {
 | |
|           runSafe(context, callback, TabManager.convert(extension, tab));
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       reload: function(tabId, reloadProperties, callback) {
 | |
|         let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
 | |
|         let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
 | |
|         if (reloadProperties && reloadProperties.bypassCache) {
 | |
|           flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
 | |
|         }
 | |
|         tab.linkedBrowser.reloadWithFlags(flags);
 | |
| 
 | |
|         if (callback) {
 | |
|           runSafe(context, callback);
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       get: function(tabId, callback) {
 | |
|         let tab = TabManager.getTab(tabId);
 | |
|         runSafe(context, callback, TabManager.convert(extension, tab));
 | |
|       },
 | |
| 
 | |
|       getAllInWindow: function(...args) {
 | |
|         let window, callback;
 | |
|         if (args.length == 1) {
 | |
|           callbacks = args[0];
 | |
|         } else {
 | |
|           window = WindowManager.getWindow(args[0]);
 | |
|           callback = args[1];
 | |
|         }
 | |
| 
 | |
|         if (!window) {
 | |
|           window = WindowManager.topWindow;
 | |
|         }
 | |
| 
 | |
|         return self.tabs.query({windowId: WindowManager.getId(window)}, callback);
 | |
|       },
 | |
| 
 | |
|       query: function(queryInfo, callback) {
 | |
|         if (!queryInfo) {
 | |
|           queryInfo = {};
 | |
|         }
 | |
| 
 | |
|         function matches(window, tab) {
 | |
|           let props = ["active", "pinned", "highlighted", "status", "title", "url", "index"];
 | |
|           for (let prop of props) {
 | |
|             if (prop in queryInfo && queryInfo[prop] != tab[prop]) {
 | |
|               return false;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           let lastFocused = window == WindowManager.topWindow;
 | |
|           if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) {
 | |
|             return false;
 | |
|           }
 | |
| 
 | |
|           let windowType = WindowManager.windowType(window);
 | |
|           if ("windowType" in queryInfo && queryInfo.windowType != windowType) {
 | |
|             return false;
 | |
|           }
 | |
| 
 | |
|           if ("windowId" in queryInfo) {
 | |
|             if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) {
 | |
|               if (currentWindow(context) != window) {
 | |
|                 return false;
 | |
|               }
 | |
|             } else {
 | |
|               if (queryInfo.windowId != tab.windowId) {
 | |
|                 return false;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           if ("currentWindow" in queryInfo) {
 | |
|             let eq = window == currentWindow(context);
 | |
|             if (queryInfo.currentWindow != eq) {
 | |
|               return false;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           return true;
 | |
|         }
 | |
| 
 | |
|         let result = [];
 | |
|         let e = Services.wm.getEnumerator("navigator:browser");
 | |
|         while (e.hasMoreElements()) {
 | |
|           let window = e.getNext();
 | |
|           if (window.document.readyState != "complete") {
 | |
|             continue;
 | |
|           }
 | |
|           let tabs = TabManager.getTabs(extension, window);
 | |
|           for (let tab of tabs) {
 | |
|             if (matches(window, tab)) {
 | |
|               result.push(tab);
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         runSafe(context, callback, result);
 | |
|       },
 | |
| 
 | |
|       _execute: function(tabId, details, kind, callback) {
 | |
|         let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
 | |
|         let mm = tab.linkedBrowser.messageManager;
 | |
| 
 | |
|         let options = {js: [], css: []};
 | |
|         if (details.code) {
 | |
|           options[kind + 'Code'] = details.code;
 | |
|         }
 | |
|         if (details.file) {
 | |
|           options[kind].push(extension.baseURI.resolve(details.file));
 | |
|         }
 | |
|         if (details.allFrames) {
 | |
|           options.all_frames = details.allFrames;
 | |
|         }
 | |
|         if (details.matchAboutBlank) {
 | |
|           options.match_about_blank = details.matchAboutBlank;
 | |
|         }
 | |
|         if (details.runAt) {
 | |
|           options.run_at = details.runAt;
 | |
|         }
 | |
|         mm.sendAsyncMessage("Extension:Execute",
 | |
|                             {extensionId: extension.id, options});
 | |
| 
 | |
|         // TODO: Call the callback with the result (which is what???).
 | |
|       },
 | |
| 
 | |
|       executeScript: function(...args) {
 | |
|         if (args.length == 1) {
 | |
|           self.tabs._execute(undefined, args[0], 'js', undefined);
 | |
|         } else {
 | |
|           self.tabs._execute(args[0], args[1], 'js', args[2]);
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       insertCss: function(tabId, details, callback) {
 | |
|         if (args.length == 1) {
 | |
|           self.tabs._execute(undefined, args[0], 'css', undefined);
 | |
|         } else {
 | |
|           self.tabs._execute(args[0], args[1], 'css', args[2]);
 | |
|         }
 | |
|       },
 | |
| 
 | |
|       connect: function(tabId, connectInfo) {
 | |
|         let tab = TabManager.getTab(tabId);
 | |
|         let mm = tab.linkedBrowser.messageManager;
 | |
| 
 | |
|         let name = connectInfo.name || "";
 | |
|         let recipient = {extensionId: extension.id};
 | |
|         if ("frameId" in connectInfo) {
 | |
|           recipient.frameId = connectInfo.frameId;
 | |
|         }
 | |
|         return context.messenger.connect(mm, name, recipient);
 | |
|       },
 | |
| 
 | |
|       sendMessage: function(tabId, message, options, responseCallback) {
 | |
|         let tab = TabManager.getTab(tabId);
 | |
|         if (!tab) {
 | |
|           // ignore sendMessage to non existent tab id
 | |
|           return;
 | |
|         }
 | |
|         let mm = tab.linkedBrowser.messageManager;
 | |
| 
 | |
|         let recipient = {extensionId: extension.id};
 | |
|         if (options && "frameId" in options) {
 | |
|           recipient.frameId = options.frameId;
 | |
|         }
 | |
|         return context.messenger.sendMessage(mm, message, recipient, responseCallback);
 | |
|       },
 | |
|     },
 | |
|   };
 | |
|   return self;
 | |
| });
 |