/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Joe Hewitt (original author) * Jason Barnabe * Shawn Wilsher * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /*************************************************************** * InspectorApp ------------------------------------------------- * The primary object that controls the Inspector application. ****************************************************************/ //////////// global variables ///////////////////// var inspector; //////////// global constants //////////////////// const kClipboardHelperCID = "@mozilla.org/widget/clipboardhelper;1"; const kPromptServiceCID = "@mozilla.org/embedcomp/prompt-service;1"; const kFOStreamCID = "@mozilla.org/network/file-output-stream;1"; const kEncoderCIDbase = "@mozilla.org/layout/documentEncoder;1?type="; const kSerializerCID = "@mozilla.org/xmlextras/xmlserializer;1"; const nsIWebNavigation = Components.interfaces.nsIWebNavigation; const nsIDocShellTreeItem = Components.interfaces.nsIDocShellTreeItem; const nsIDocShell = Components.interfaces.nsIDocShell; const nsIFileOutputStream = Components.interfaces.nsIFileOutputStream; const nsIDocumentEncoder = Components.interfaces.nsIDocumentEncoder; const nsIDOMSerializer = Components.interfaces.nsIDOMSerializer; ////////////////////////////////////////////////// window.addEventListener("load", InspectorApp_initialize, false); window.addEventListener("unload", InspectorApp_destroy, false); function InspectorApp_initialize() { inspector = new InspectorApp(); // window.arguments may be either a string or a node. // If passed via a command line handler, it will be a uri string. // If passed via navigator hooks, it will be a dom node to inspect. var initNode, initURI; if (window.arguments && window.arguments.length) { if (typeof window.arguments[0] == "string") { initURI = window.arguments[0]; } else if (window.arguments[0] instanceof Components.interfaces.nsIDOMNode) { initNode = window.arguments[0]; } } inspector.initialize(initNode, initURI); // Disables the Mac Specific VK_BACK for delete key for non-mac systems if (!/Mac/.test(navigator.platform)) { document.getElementById("keyEditDeleteMac") .setAttribute("disabled", "true"); } } function InspectorApp_destroy() { inspector.destroy(); } //////////////////////////////////////////////////////////////////////////// //// class InspectorApp function InspectorApp() { } InspectorApp.prototype = { //////////////////////////////////////////////////////////////////////////// //// Initialization mShowBrowser: false, mClipboardHelper: null, mPromptService: null, get document() { return this.mDocPanel.viewer.subject }, get panelset() { return this.mPanelSet; }, initialize: function(aTarget, aURI) { this.mInitTarget = aTarget; var el = document.getElementById("bxBrowser"); el.addEventListener("pageshow", BrowserPageShowListener, true); this.setBrowser(false, true); this.mClipboardHelper = XPCU.getService(kClipboardHelperCID, "nsIClipboardHelper"); this.mPromptService = XPCU.getService(kPromptServiceCID, "nsIPromptService"); this.mPanelSet = document.getElementById("bxPanelSet"); this.mPanelSet.addObserver("panelsetready", this, false); this.mPanelSet.initialize(); // check if accessibility service is available var cmd = document.getElementById("cmd:toggleAccessibleNodes"); if (cmd) { if (!("@mozilla.org/accessibleRetrieval;1" in Components.classes)) cmd.setAttribute("disabled", "true"); } if (aURI) { this.gotoURL(aURI); } }, destroy: function() { InsUtil.persistAll("bxDocPanel"); InsUtil.persistAll("bxObjectPanel"); }, //////////////////////////////////////////////////////////////////////////// //// Viewer Panels initViewerPanels: function() { this.mDocPanel = this.mPanelSet.getPanel(0); this.mDocPanel.addObserver("subjectChange", this, false); this.mObjectPanel = this.mPanelSet.getPanel(1); if (this.mInitTarget) { if (this.mInitTarget.nodeType == Node.DOCUMENT_NODE) this.setTargetDocument(this.mInitTarget); else if (this.mInitTarget.nodeType == Node.ELEMENT_NODE) { this.setTargetDocument(this.mInitTarget.ownerDocument); this.mDocPanel.params = this.mInitTarget; } this.mInitTarget = null; } }, onEvent: function(aEvent) { switch (aEvent.type) { case "panelsetready": this.initViewerPanels(); break; case "subjectChange": if (aEvent.target == this.mDocPanel.viewer && aEvent.subject && "location" in aEvent.subject) { this.locationText = aEvent.subject.location; // display document url var docTitle = aEvent.subject.title || aEvent.subject.location; if (/Mac/.test(navigator.platform)) { document.title = docTitle; } else { document.title = docTitle + " - " + document.documentElement.getAttribute("title"); } this.updateCommand("cmdSave"); } break; } }, //////////////////////////////////////////////////////////////////////////// //// UI Commands updateCommand: function inspector_updateCommand(aCommand) { var command = document.getElementById(aCommand); var disabled = false; switch (aCommand) { case "cmdSave": var doc = this.mDocPanel.subject; disabled = !((kEncoderCIDbase + doc.contentType) in Components.classes || (kSerializerCID in Components.classes)); break; } command.setAttribute("disabled", disabled); }, doViewerCommand: function(aCommand) { this.mPanelSet.execCommand(aCommand); }, showOpenURLDialog: function() { var bundle = this.mPanelSet.stringBundle; var msg = bundle.getString("inspectURL.message"); var title = bundle.getString("inspectURL.title"); var url = { value: "http://" }; var dummy = { value: false }; var go = this.mPromptService.prompt(window, title, msg, url, null, dummy); if (go) { this.gotoURL(url.value); } }, showPrefsDialog: function() { goPreferences("advancedItem", "chrome://inspector/content/prefs/pref-inspector.xul", "inspector"); }, toggleBrowser: function(aToggleSplitter) { this.setBrowser(!this.mShowBrowser, aToggleSplitter) }, setBrowser: function(aValue, aToggleSplitter) { this.mShowBrowser = aValue; if (aToggleSplitter) this.openSplitter("Browser", aValue); var cmd = document.getElementById("cmdToggleBrowser"); cmd.setAttribute("checked", aValue); }, openSplitter: function(aName, aTruth) { var splitter = document.getElementById("spl" + aName); if (aTruth) splitter.open(); else splitter.close(); }, /** * Saves the current document state in the inspector. */ save: function save() { var picker = Components.classes["@mozilla.org/filepicker;1"] .createInstance(nsIFilePicker); var title = document.getElementById("mi-save").label; picker.init(window, title, picker.modeSave) picker.appendFilters(picker.filterHTML | picker.filterXML | picker.filterXUL); if (picker.show() == picker.returnCancel) return; var fos = Components.classes[kFOStreamCID] .createInstance(nsIFileOutputStream); const flags = 0x02 | 0x08 | 0x20; // write, create, truncate var doc = this.mDocPanel.subject; if ((kEncoderCIDbase + doc.contentType) in Components.classes) { // first we try to use the document encoder for that content type. If // that fails, we move on to the xml serializer. var encoder = Components.classes[kEncoderCIDbase + doc.contentType] .createInstance(nsIDocumentEncoder); encoder.init(doc, doc.contentType, encoder.OutputRaw); encoder.setCharset(doc.characterSet); fos.init(picker.file, flags, 0666, 0); try { encoder.encodeToStream(fos); } finally { fos.close(); } } else { var serializer = Components.classes[kSerializerCID] .createInstance(nsIDOMSerializer); fos.init(picker.file, flags, 0666, 0); try { serializer.serializeToStream(doc, fos); } finally { fos.close(); } } }, exit: function() { window.close(); // Todo: remove observer service here }, //////////////////////////////////////////////////////////////////////////// //// Navigation gotoTypedURL: function() { var url = document.getElementById("tfURLBar").value; this.gotoURL(url); }, gotoURL: function(aURL, aNoSaveHistory) { this.mPendingURL = aURL; this.mPendingNoSave = aNoSaveHistory; this.browseToURL(aURL); this.setBrowser(true, true); }, browseToURL: function(aURL) { try { this.webNavigation.loadURI(aURL, nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null); } catch(ex) { // nsIWebNavigation.loadURI will spit out an appropriate user prompt, so // we don't need to do anything here. See nsDocShell::DisplayLoadError() } }, /** * Creates the submenu for Inspect Content/Chrome Document */ showInspectDocumentList: function showInspectDocumentList(aEvent, aChrome) { const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; var menu = aEvent.target; var ww = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); var windows = ww.getXULWindowEnumerator(null); var docs = []; while (windows.hasMoreElements()) { try { // Get the window's main docshell var windowDocShell = windows.getNext() .QueryInterface(Components.interfaces.nsIXULWindow) .docShell; this.appendContainedDocuments(docs, windowDocShell, aChrome ? nsIDocShellTreeItem.typeChrome : nsIDocShellTreeItem.typeContent); } catch (ex) { // We've failed with this window somehow, but we're catching the error // so the others will still work Components.utils.reportError(ex); } } // Clear out any previous menu this.emptyChildren(menu); // Now add what we found to the menu if (!docs.length) { var noneMenuItem = document.createElementNS(XULNS, "menuitem"); noneMenuItem.setAttribute("label", this.mPanelSet.stringBundle .getString("inspectWindow.noDocuments.message")); noneMenuItem.setAttribute("disabled", true); menu.appendChild(noneMenuItem); } else { for (var i = 0; i < docs.length; i++) this.addInspectDocumentMenuItem(menu, docs[i], i + 1); } }, /** * Appends to the array the documents contained in docShell (including the passed * docShell itself). * * @param array the array to append to * @param docShell the docshell to look for documents in * @param type one of the types defined in nsIDocShellTreeItem */ appendContainedDocuments: function appendContainedDocuments(array, docShell, type) { // Load all the window's content docShells var containedDocShells = docShell.getDocShellEnumerator(type, nsIDocShell.ENUMERATE_FORWARDS); while (containedDocShells.hasMoreElements()) { try { // Get the corresponding document for this docshell var childDoc = containedDocShells.getNext().QueryInterface(nsIDocShell) .contentViewer.DOMDocument; // Ignore the DOM Insector's browser docshell if it's not being used if (docShell.contentViewer.DOMDocument.location.href != document.location.href || childDoc.location.href != "about:blank") { array.push(childDoc); } } catch (ex) { // We've failed with this document somehow, but we're catching the error so // the others will still work dump(ex + "\n"); } } }, /** * Creates a menu item for Inspect Document. * * @param doc document related to this menu item * @param docNumber the position of the document */ addInspectDocumentMenuItem: function addInspectDocumentMenuItem(parent, doc, docNumber) { const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; var menuItem = document.createElementNS(XULNS, "menuitem"); menuItem.doc = doc; // Use the URL if there's no title var title = doc.title || doc.location.href; // The first ten items get numeric access keys if (docNumber < 10) { menuItem.setAttribute("label", docNumber + " " + title); menuItem.setAttribute("accesskey", docNumber); } else { menuItem.setAttribute("label", title); } parent.appendChild(menuItem); }, setTargetWindow: function(aWindow) { this.setTargetDocument(aWindow.document); }, setTargetDocument: function(aDoc) { this.mDocPanel.subject = aDoc; }, get webNavigation() { var browser = document.getElementById("ifBrowser"); return browser.webNavigation; }, //////////////////////////////////////////////////////////////////////////// //// UI Labels getters and setters get locationText() { return document.getElementById("tfURLBar").value; }, set locationText(aText) { document.getElementById("tfURLBar").value = aText; }, get statusText() { return document.getElementById("txStatus").value; }, set statusText(aText) { document.getElementById("txStatus").value = aText; }, get progress() { return document.getElementById("pmStatus").value; }, set progress(aPct) { document.getElementById("pmStatus").value = aPct; }, //////////////////////////////////////////////////////////////////////////// //// Document Loading documentLoaded: function() { this.setTargetWindow(_content); var url = this.webNavigation.currentURI.spec; // put the url into the urlbar this.locationText = url; // add url to the history, unless explicity told not to if (!this.mPendingNoSave) this.addToHistory(url); this.mPendingURL = null; this.mPendingNoSave = null; }, //////////////////////////////////////////////////////////////////////////// //// History addToHistory: function(aURL) { }, //////////////////////////////////////////////////////////////////////////// //// Uncategorized get isViewingContent() { return this.mPanelSet.getPanel(0).subject != null; }, fillInTooltip: function(tipElement) { var retVal = false; var textNode = document.getElementById("txTooltip"); if (textNode) { try { var tipText = tipElement.getAttribute("tooltiptext"); if (tipText != "") { textNode.setAttribute("value", tipText); retVal = true; } } catch (e) { } } return retVal; }, initPopup: function(aPopup) { var items = aPopup.getElementsByTagName("menuitem"); var js, fn, item; for (var i = 0; i < items.length; i++) { item = items[i]; fn = "isDisabled" in item ? item.isDisabled : null; if (!fn) { js = item.getAttribute("isDisabled"); if (js) { fn = new Function(js); item.isDisabled = fn; } else { item.isDisabled = null; // to prevent annoying "strict" warning messages } } if (fn) { if (item.isDisabled()) item.setAttribute("disabled", "true"); else item.removeAttribute("disabled"); } fn = null; } }, emptyChildren: function(aNode) { while (aNode.hasChildNodes()) { aNode.removeChild(aNode.lastChild); } }, onSplitterOpen: function(aSplitter) { if (aSplitter.id == "splBrowser") { this.setBrowser(aSplitter.isOpened, false); } }, // needed by overlayed commands from viewer to get references to a specific // viewer object by name getViewer: function(aUID) { return this.mPanelSet.registry.getViewerByUID(aUID); } }; //////////////////////////////////////////////////////////////////////////// //// event listeners function BrowserPageShowListener(aEvent) { // since we will also get pageshow events for frame documents, // make sure we respond to the top-level document load if (aEvent.target.defaultView == _content) inspector.documentLoaded(); } function UtilWindowOpenListener(aWindow) { inspector.doViewSearchItem(aWindow); }