home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 March / PCpro_2006_03.ISO / files / freeware / greasemonkey-0.6.4-fx.xpi / chrome / chromeFiles / content / browser.js < prev    next >
Encoding:
JavaScript  |  2005-11-30  |  17.1 KB  |  550 lines

  1.  
  2. // this file is the javascript backing for the UI wrangling which happens in
  3. // browser.xul. It also initializes the Greasemonkey singleton which contains
  4. // all the main injection logic, though that should probably be a proper XPCOM
  5. // service and wouldn't need to be initialized in that case.
  6.  
  7. var GM_BrowserUI = new Object();
  8.  
  9. /**
  10.  * nsISupports.QueryInterface
  11.  */
  12. GM_BrowserUI.QueryInterface = function(aIID) {
  13.   if (!aIID.equals(Components.interfaces.nsISupports) &&
  14.       !aIID.equals(Components.interfaces.gmIBrowserWindow) &&
  15.       !aIID.equals(Components.interfaces.nsISupportsWeakReference))
  16.     throw Components.results.NS_ERROR_NO_INTERFACE;
  17.  
  18.   return this;
  19. }
  20.  
  21.  
  22. /**
  23.  * Called when this file is parsed, by the last line. Set up initial objects,
  24.  * do version checking, and set up listeners for browser xul load and location
  25.  * changes.
  26.  */
  27. GM_BrowserUI.init = function() {
  28.   this.menuCommanders = [];
  29.   this.currentMenuCommander = null;
  30.  
  31.   GM_updateVersion();
  32.  
  33.   GM_listen(window, "load", GM_hitch(this, "chromeLoad"));
  34.   GM_listen(window, "unload", GM_hitch(this, "chromeUnload"));
  35. }
  36.  
  37. /**
  38.  * The browser XUL has loaded. Find the elements we need and set up our
  39.  * listeners and wrapper objects.
  40.  */
  41. GM_BrowserUI.chromeLoad = function(e) {
  42.   // get all required DOM elements
  43.   this.tabBrowser = document.getElementById("content");
  44.   this.appContent = document.getElementById("appcontent");
  45.   this.contextMenu = document.getElementById("contentAreaContextMenu"); 
  46.   this.statusImage = document.getElementById("gm-status-image");
  47.   this.statusLabel = document.getElementById("gm-status-label");
  48.   this.statusPopup = document.getElementById("gm-status-popup");
  49.   this.statusEnabledItem = document.getElementById("gm-status-enabled-item");
  50.   this.toolsMenu = document.getElementById("menu_ToolsPopup");
  51.  
  52.   // seamonkey compat
  53.   if (!this.toolsMenu) {
  54.     this.toolsMenu = document.getElementById("taskPopup");
  55.   }
  56.  
  57.   // update visual status when enabled state changes
  58.   this.enabledWatcher = GM_hitch(this, "refreshStatus");
  59.   GM_prefRoot.watch("enabled", this.enabledWatcher);
  60.  
  61.   // hook various events
  62.   GM_listen(this.appContent, "DOMContentLoaded", GM_hitch(this, "contentLoad"));
  63.   GM_listen(this.contextMenu, "popupshowing", GM_hitch(this, "contextMenuShowing"));
  64.   GM_listen(this.toolsMenu, "popupshowing", GM_hitch(this, "toolsMenuShowing"));    
  65.  
  66.   // listen for clicks on the install bar
  67.   Components.classes["@mozilla.org/observer-service;1"]
  68.             .getService(Components.interfaces.nsIObserverService)
  69.             .addObserver(this, "install-userscript", true);
  70.  
  71.   // we use this to determine if we are the active window sometimes
  72.   this.winWat = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  73.                           .getService(Components.interfaces.nsIWindowWatcher);
  74.  
  75.   // this gives us onLocationChange
  76.   document.getElementById("content").addProgressListener(this,
  77.     Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);
  78.  
  79.   // update enabled icon
  80.   this.refreshStatus();
  81.  
  82.   // register for notifications from greasemonkey-service about ui type things
  83.   this.gmSvc = Components.classes["@greasemonkey.mozdev.org/greasemonkey-service;1"]
  84.                          .getService(Components.interfaces.gmIGreasemonkeyService);
  85.  
  86.   this.gmSvc.registerBrowser(this);
  87. }
  88.  
  89. /**
  90.  * gmIBrowserWindow.registerMenuCommand
  91.  */
  92. GM_BrowserUI.registerMenuCommand = function(menuCommand) {
  93.   if (this.isMyWindow(menuCommand.window)) {
  94.     var commander = this.getCommander(menuCommand.window);
  95.  
  96.     commander.registerMenuCommand(menuCommand.name,
  97.                                   menuCommand.doCommand,
  98.                                   menuCommand.accelKey,
  99.                                   menuCommand.accelModifiers,
  100.                                   menuCommand.accessKey);
  101.   }
  102. }
  103.  
  104. /**
  105.  * gmIBrowserWindow.openInTab
  106.  */
  107. GM_BrowserUI.openInTab = function(domWindow, url) {
  108.   if (this.isMyWindow(domWindow)) {
  109.     document.getElementById("content").addTab(url);
  110.   }
  111. }
  112.  
  113. /**
  114.  * Gets called when a DOMContentLoaded event occurs somewhere in the browser.
  115.  * If that document is in in the top-level window of the focused tab, find 
  116.  * it's menu items and activate them.
  117.  */
  118. GM_BrowserUI.contentLoad = function(e) {
  119.   var unsafeWin;
  120.   var href;
  121.   var commander;
  122.  
  123.   if (GM_deepWrappersEnabled(window)) {
  124.     // when deep wrappers are enabled, e.target is already a deep xpcnw
  125.     unsafeWin = e.target.defaultView;
  126.  
  127.     // in DPa2, there was a bug that made this *not* a deep wrapper.
  128.     if (unsafeWin.wrappedJSObject) {
  129.       unsafeWin = unsafeWin.wrappedJSObject;
  130.     }
  131.  
  132.     href = e.target.location.href;
  133.   } else {
  134.     // otherwise we need to wrap it manually
  135.     unsafeWin = new XPCNativeWrapper(
  136.                   new XPCNativeWrapper(e, "target").target,
  137.                   "defaultView").defaultView;
  138.     href = new XPCNativeWrapper(
  139.               new XPCNativeWrapper(unsafeWin, "location").location,
  140.               "href").href;
  141.   }
  142.  
  143.   if (GM_getEnabled() && GM_isGreasemonkeyable(href)) {
  144.     commander = this.getCommander(unsafeWin);
  145.  
  146.     // if this content load is in the focused tab, attach the menuCommaander  
  147.     if (unsafeWin == this.tabBrowser.selectedBrowser.contentWindow) {
  148.       this.currentMenuCommander = commander;
  149.       this.currentMenuCommander.attach();
  150.     }
  151.  
  152.     this.gmSvc.domContentLoaded({ wrappedJSObject: unsafeWin });
  153.   
  154.     GM_listen(unsafeWin, "pagehide", GM_hitch(this, "contentUnload"));
  155.   }
  156.  
  157.   if (GM_getEnabled() && href.match(/\.user\.js($|\?)/i)) {
  158.     // find the browser the user script is loading in
  159.     for (var i = 0, browser; browser = this.tabBrowser.browsers[i]; i++) {
  160.       if (browser.contentWindow == unsafeWin) {
  161.         var pick = Math.round(Math.random() * (GM_BrowserUI.greetz.length - 1));
  162.         var greeting = GM_BrowserUI.greetz[pick];
  163.  
  164.         this.tabBrowser.showMessage(
  165.           browser,
  166.           "chrome://greasemonkey/content/status_on.gif",
  167.           greeting + " This is a Greasemonkey User Script. " + 
  168.                      "Click Install to start using it.",
  169.           "Install",
  170.           null /* default doc shell */,
  171.           "install-userscript",
  172.           null /* no popuup */,
  173.           "top",
  174.           true /* show close button */,
  175.           "I" /* access key */);
  176.  
  177.         break;
  178.       }
  179.     }
  180.   }
  181. }
  182.  
  183. /**
  184.  * Implements nsIObserve.observe. Right now we're only observing our own
  185.  * install-userscript, which happens when the install bar is clicked.
  186.  */
  187. GM_BrowserUI.observe = function(subject, topic, data) {
  188.   if (topic == "install-userscript") {
  189.     if (window == this.winWat.activeWindow) {
  190.       new ScriptDownloader().installFromURL(
  191.         this.tabBrowser.selectedBrowser.contentWindow.location.href);
  192.     }
  193.   } else {
  194.     throw new Error("Unexpected topic received: {" + topic + "}");
  195.   }
  196. }
  197.  
  198. /**
  199.  * The browser's location has changed. Usually, we don't care. But in the case
  200.  * of tab switching we need to change the list of commands displayed in the
  201.  * User Script Commands submenu.
  202.  */
  203. GM_BrowserUI.onLocationChange = function(a,b,c) {
  204.   if (this.currentMenuCommander != null) {
  205.     this.currentMenuCommander.detach();
  206.     this.currentMenuCommander = null;
  207.   }
  208.  
  209.   var menuCommander = this.getCommander(this.tabBrowser.selectedBrowser.
  210.                                         contentWindow);
  211.   
  212.   if (menuCommander) {
  213.     this.currentMenuCommander = menuCommander;
  214.     this.currentMenuCommander.attach();
  215.   }
  216. }
  217.  
  218. /**
  219.  * A content document has unloaded. We need to remove it's menuCommander to 
  220.  * avoid leaking it's memory. 
  221.  */
  222. GM_BrowserUI.contentUnload = function(e) {
  223.   if (e.persisted) {
  224.     return;
  225.   }
  226.  
  227.   var unsafeWin = e.target.defaultView;
  228.  
  229.   // remove the commander for this document  
  230.   var commander = null;
  231.   
  232.   // looping over commanders rather than using getCommander because we need
  233.   // the index into commanders.splice.
  234.   for (var i = 0; item = this.menuCommanders[i]; i++) {
  235.     if (item.win == unsafeWin) {
  236.  
  237.       log("* Found corresponding commander. Is currentMenuCommander: " + 
  238.           (item.commander == this.currentMenuCommander));
  239.  
  240.       if (item.commander == this.currentMenuCommander) {
  241.         this.currentMenuCommander.detach();
  242.         this.currentMenuCommander = null;
  243.       }
  244.       
  245.       this.menuCommanders.splice(i, 1);
  246.  
  247.       log("* Found and removed corresponding commander")
  248.       break;
  249.     }
  250.   }
  251. }
  252.  
  253. /**
  254.  * The browser XUL has unloaded. We need to let go of the pref watcher so
  255.  * that a non-existant window is not informed when greasemonkey enabled state
  256.  * changes. And we need to let go of the progress listener so that we don't
  257.  * leak it's memory.
  258.  */
  259. GM_BrowserUI.chromeUnload = function() {
  260.   GM_prefRoot.unwatch("enabled", this.enabledWatcher);
  261.   this.tabBrowser.removeProgressListener(this);
  262.   this.gmSvc.unregisterBrowser(this);
  263.   delete this.menuCommanders;
  264. }
  265.  
  266.  
  267. GM_BrowserUI.contextMenuShowing = function() {
  268.   var contextItem = ge("install-userscript");
  269.   var contextSep = ge("install-userscript-sep");
  270.  
  271.   var culprit = document.popupNode;
  272.  
  273.   while (culprit && culprit.tagName && culprit.tagName.toLowerCase() != "a") {
  274.      culprit = culprit.parentNode;
  275.   }
  276.  
  277.   contextItem.hidden = 
  278.     contextSep.hidden = 
  279.     !this.getUserScriptLinkUnderPointer();
  280. }
  281.  
  282.  
  283. GM_BrowserUI.getUserScriptLinkUnderPointer = function() {
  284.   var contextItem = ge("install-userscript");
  285.   var contextSep = ge("install-userscript-sep");
  286.  
  287.   var culprit = document.popupNode;
  288.  
  289.   while (culprit && culprit.tagName && culprit.tagName.toLowerCase() != "a") {
  290.      culprit = culprit.parentNode;
  291.   }
  292.  
  293.   if (culprit && culprit.href && 
  294.       culprit.href.match(/\.user\.js(\?|$)/i) != null) {
  295.     return culprit;
  296.   } else {
  297.     return null;
  298.   }
  299. }
  300.  
  301.  
  302. GM_BrowserUI.toolsMenuShowing = function() {
  303.   var installItem = ge("userscript-tools-install");
  304.   var disabled = true;
  305.   
  306.   if (window._content) {
  307.     var locationGetter = new XPCNativeWrapper(window._content, "location");
  308.  
  309.     if (locationGetter.location) {
  310.       var href = new XPCNativeWrapper(locationGetter.location, "href").href;
  311.  
  312.       if (href.match(/\.user\.js(\?|$)/i)) {
  313.         disabled = false;
  314.       }
  315.     }
  316.   }
  317.   
  318.   installItem.setAttribute("disabled", disabled.toString());
  319. }
  320.  
  321. /**
  322.  * Helper method which gets the menuCommander corresponding to a given 
  323.  * document
  324.  */
  325. GM_BrowserUI.getCommander = function(unsafeWin) {
  326.   for (var i = 0; i < this.menuCommanders.length; i++) {
  327.     if (this.menuCommanders[i].win == unsafeWin) {
  328.       return this.menuCommanders[i].commander;
  329.     }
  330.   }
  331.  
  332.   // no commander found. create one and add it.
  333.   var commander = new GM_MenuCommander(document);
  334.   this.menuCommanders.push({win:unsafeWin, commander:commander});
  335.  
  336.   return commander;
  337. }
  338.  
  339. /**
  340.  * Helper to determine if a given dom window is in this tabbrowser
  341.  */
  342. GM_BrowserUI.isMyWindow = function(domWindow) {
  343.   var tabbrowser = getBrowser();  
  344.   var browser;
  345.  
  346.   for (var i = 0; browser = tabbrowser.browsers[i]; i++) {
  347.     if (browser.contentWindow == domWindow) {
  348.       return true;
  349.     }
  350.   }
  351.  
  352.   return false;
  353. }
  354.  
  355. function GM_showPopup(aEvent) {
  356.     var config = new Config(getScriptFile("config.xml"));
  357.     config.load();
  358.     var popup = aEvent.target;
  359.     var url = getBrowser().contentWindow.document.location.href;
  360.  
  361.   // set the enabled/disabled state
  362.   GM_BrowserUI.statusEnabledItem.setAttribute("checked", GM_getEnabled());
  363.  
  364.     // remove all the scripts from the list
  365.   for (var i = popup.childNodes.length - 1; i >= 0; i--) {
  366.     if (popup.childNodes[i].hasAttribute("value")) {
  367.       popup.removeChild(popup.childNodes[i]);
  368.     }
  369.     }
  370.  
  371.   var foundScript = false;
  372.  
  373.     // build the new list of scripts
  374.     for (var i = 0, script = null; script = config.scripts[i]; i++) {
  375.     incloop: for (var j = 0; j < script.includes.length; j++) {
  376.       var pattern = convert2RegExp(script.includes[j]);
  377.       if (pattern.test(url)) {
  378.         for (var k = 0; k < script.excludes.length; k++) {
  379.           pattern = convert2RegExp(script.excludes[k]);
  380.           if (pattern.test(url)) {
  381.             break incloop;
  382.           }
  383.         }
  384.  
  385.         foundInjectedScript = true;
  386.  
  387.         var mi = document.createElement('menuitem');
  388.         mi.setAttribute('label', script.name);
  389.         mi.setAttribute('value', i);
  390.         mi.setAttribute('type', 'checkbox');
  391.         mi.setAttribute('checked', script.enabled.toString());
  392.  
  393.         popup.insertBefore(mi, document.getElementById("gm-status-no-scripts-sep"));
  394.  
  395.         break incloop;
  396.       }
  397.     }
  398.     }
  399.  
  400.   document.getElementById("gm-status-no-scripts").collapsed = foundInjectedScript;
  401. }
  402.  
  403. function GM_popupClicked(aEvent) {
  404.     var config = new Config(getScriptFile("config.xml"));
  405.     config.load();
  406.     var scriptNum=aEvent.target.value;
  407.     if (!config.scripts[scriptNum]) return;
  408.     config.scripts[scriptNum].enabled=!config.scripts[scriptNum].enabled;
  409.     config.save();
  410. }
  411.  
  412. /**
  413.  * Greasemonkey's enabled state has changed, either as a result of clicking
  414.  * the icon in this window, clicking it in another window, or even changing
  415.  * the mozilla preference that backs it directly.
  416.  */
  417. GM_BrowserUI.refreshStatus = function() {
  418.   if (GM_getEnabled()) {
  419.     this.statusImage.src = "chrome://greasemonkey/content/status_on.gif";
  420.     this.statusImage.tooltipText = "Greasemonkey is enabled";
  421.   } else {
  422.     this.statusImage.src = "chrome://greasemonkey/content/status_off.gif";
  423.     this.statusImage.tooltipText = "Greasemonkey is disabled";
  424.   }
  425. }
  426.  
  427. GM_BrowserUI.newUserScript = function() {
  428.   var tempname = "newscript.user.js";
  429.   
  430.   var source = getContentDir();
  431.   source.append("template.user.js");
  432.   
  433.   var dest = Components.classes["@mozilla.org/file/directory_service;1"]
  434.         .getService(Components.interfaces.nsIProperties)
  435.         .get("TmpD", Components.interfaces.nsILocalFile);
  436.         
  437.   var destFile = dest.clone().QueryInterface(Components.interfaces.nsILocalFile);
  438.   destFile.append(tempname);
  439.   
  440.   if (destFile.exists()) {
  441.     destFile.remove(false);
  442.   }
  443.  
  444.   source.copyTo(dest, tempname);
  445.   openInEditor(destFile);
  446. }
  447.  
  448. GM_BrowserUI.showStatus = function(message, autoHide) {
  449.   if (this.statusLabel.collapsed) {
  450.     this.statusLabel.collapsed = false;
  451.   }
  452.  
  453.   var current = parseInt(this.statusLabel.style.width);
  454.   this.statusLabel.value = message;
  455.   this.statusLabel.style.width = "";
  456.   var max = this.statusLabel.boxObject.width;
  457.  
  458.   this.statusLabel.style.width = current + "px";
  459.   
  460.   this.showAnimation = new Accelimation(this.statusLabel.style, 
  461.                                           "width", max, 300, 2, "px");
  462.   this.showAnimation.onend = GM_hitch(this, "showStatusAnimationEnd", autoHide);
  463.   this.showAnimation.start();
  464. }
  465.  
  466. GM_BrowserUI.showStatusAnimationEnd = function(autoHide) {
  467.   this.showAnimation = null;
  468.   this.setAutoHideTimer();
  469. }
  470.  
  471. GM_BrowserUI.setAutoHideTimer = function() {
  472.   if (this.autoHideTimer) {
  473.     window.clearTimeout(this.autoHideTimer);
  474.   }
  475.  
  476.   this.autoHideTimer = window.setTimeout(GM_hitch(this, "hideStatus"), 3000);
  477. }
  478.  
  479. GM_BrowserUI.hideStatus = function() {
  480.   if (!this.hideAnimation) {
  481.     this.autoHideTimer = null;
  482.     this.hideAnimation = new Accelimation(this.statusLabel.style, 
  483.                                             "width", 0, 300, 2, "px");
  484.     this.hideAnimation.onend = GM_hitch(this, "hideStatusAnimationEnd");
  485.     this.hideAnimation.start();
  486.   }
  487. }
  488.  
  489. GM_BrowserUI.hideStatusAnimationEnd = function() {
  490.   this.hideAnimation = null;
  491.   this.statusLabel.collapsed = true;
  492. }
  493.  
  494. GM_BrowserUI.greetz = [
  495.   "Huzzah!",
  496.   "Toodles...",
  497.   "Howdy!",
  498.   "Sup...",
  499.   "Greetings, fellow traveler.",
  500.   "G'Day!"
  501.   ];
  502.  
  503. // necessary for webProgressListener implementation
  504. GM_BrowserUI.onProgressChange = function(webProgress,b,c,d,e,f){}
  505. GM_BrowserUI.onStateChange = function(a,b,c,d){}
  506. GM_BrowserUI.onStatusChange = function(a,b,c,d){}
  507. GM_BrowserUI.onSecurityChange = function(a,b,c){}
  508. GM_BrowserUI.onLinkIconAvailable = function(a){}
  509.  
  510. loggify(GM_BrowserUI, "GM_BrowserUI");
  511.  
  512. log("calling init...")
  513. GM_BrowserUI.init();
  514.  
  515. // the following functions were copied wholesale from old code without 
  516. // refactoring. need to be reorganized a little.
  517.  
  518. function manageMenuItemClicked() {
  519.    window.openDialog("chrome://greasemonkey/content/manage.xul", "manager", 
  520.     "resizable,centerscreen,modal");
  521. }
  522.  
  523. function installMenuItemClicked() {
  524.   var sd = new ScriptDownloader();
  525.   var unsafeDoc = new XPCNativeWrapper(window._content, "document").document;
  526.   var unsafeLoc = new XPCNativeWrapper(window._content, "location").location;
  527.  
  528.   sd.installFromURL(new XPCNativeWrapper(unsafeLoc, "href").href);
  529. }
  530.  
  531. function installContextItemClicked() {
  532.   var sd = new ScriptDownloader();
  533.   sd.installFromURL(GM_BrowserUI.getUserScriptLinkUnderPointer().href);
  534. }
  535.  
  536. // this is a debug function to make sure that menuCommanders are being
  537. // created and destroyed at the correct time.
  538.  
  539. // window.setInterval(GM_checkState, 3000);
  540.  
  541. function GM_checkState() {
  542.   var commander;
  543.   var urls = [];
  544.   for (var i = 0; commander = GM_BrowserUI.menuCommanders[i]; i++) {
  545.     urls.push(commander.win.location.href);
  546.   }
  547.   
  548.   log(urls.length + " active commanders: " + urls.join(", "));
  549. }
  550.