Fix 59707: extract navigator's context menu and allow it to be used elsewhere in the app (and to help in embedding). r=jag a=alecf

This commit is contained in:
blakeross%telocity.com 2006-07-29 05:33:12 +00:00
Родитель 2c4258b8a3
Коммит c577348464
5 изменённых файлов: 1222 добавлений и 120 удалений

Просмотреть файл

@ -0,0 +1,103 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributors:
*
* Alec Flett <alecf@netscape.com>
* Ben Goodger <ben@netscape.com>
* Mike Pinkerton <pinkerton@netscape.com>
* Blake Ross <blakeross@telocity.com>
*/
var pref = null;
pref = Components.classes["@mozilla.org/preferences;1"];
pref = pref.getService();
pref = pref.QueryInterface(Components.interfaces.nsIPref);
// Called whenever the user clicks in the content area,
// except when left-clicking on links (special case)
// should always return true for click to go through
function contentAreaClick(event)
{
var target = event.originalTarget;
var linkNode;
switch (target.localName.toLowerCase()) {
case "a":
linkNode = event.target;
break;
case "area":
if (event.target.href)
linkNode = event.target;
break;
default:
linkNode = findParentNode(target, "a");
break;
}
if (linkNode) {
handleLinkClick(event, linkNode.href);
return true;
}
if (pref && event.button == 2 &&
!findParentNode(target, "scrollbar") &&
pref.GetBoolPref("middlemouse.paste")) {
middleMousePaste(event);
}
return true;
}
function handleLinkClick(event, href)
{
switch (event.button) {
case 1: // if left button clicked
if (event.metaKey || event.ctrlKey) { // and meta or ctrl are down
openNewWindowWith(href); // open link in new window
event.preventBubble();
return true;
}
if (event.shiftKey) { // if shift is down
savePage(href); // save the link
return true;
}
if (event.altKey) // if alt is down
return true; // do nothing
return false;
case 2: // if middle button clicked
if (pref && pref.GetBoolPref("middlemouse.openNewWindow")) { // and the pref is on
openNewWindowWith(href); // open link in new window
event.preventBubble();
return true;
}
break;
}
return false;
}
function middleMousePaste(event)
{
var url = readFromClipboard();
//dump ("Loading URL on clipboard: '" + url + "'; length = " + url.length + "\n");
if (url) {
var urlBar = document.getElementById("urlbar");
urlBar.value = url;
BrowserLoadURL();
event.preventBubble();
return true;
}
return false;
}

Просмотреть файл

@ -0,0 +1,182 @@
<?xml version="1.0"?>
<!-- The contents of this file are subject to the Netscape 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/NPL/
-
- 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 Communicator client code, released
- March 31, 1998.
-
- The Initial Developer of the Original Code is Netscape
- Communications Corporation. Portions created by Netscape are
- Copyright (C) 1998-1999 Netscape Communications Corporation. All
- Rights Reserved.
-
- Contributor(s):
- Blake Ross <blakeross@telocity.com>
-->
<!DOCTYPE window SYSTEM "chrome://communicator/locale/contentAreaCommands.dtd" >
<overlay id="contentAreaContextOverlay"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- Context menu -->
<script language="javascript">
// Global variable that holds the nsContextMenu instance.
var contextMenu = null;
</script>
<script type="text/javascript" src="chrome://communicator/content/nsContextMenu.js"/>
<script type="text/javascript" src="chrome://communicator/content/contentAreaUtils.js"/>
<popupset id="contentAreaContextSet">
<!-- This is the context menu for the content-area of the navigator
window. It contains each and every possible menu choice. The
appropriate items are hidden/shown upon display, based on what
the user has clicked-on.
-->
<popup id="contentAreaContextMenu"
oncreate="contextMenu = new nsContextMenu( this ); return contextMenu.shouldDisplay;"
ondestroy="contextMenu.onDestroy(); contextMenu = null;">
<!-- Open ==================================== -->
<menuitem id="context-openlink"
value="&openLinkCmd.label;"
accesskey=""
oncommand="contextMenu.openLink();"/>
<!-- XXX - DEPENDENT ON PRESENCE OF EDITOR!! - XXX -->
<menuitem id="context-editlink"
value="&editLinkCmd.label;"
accesskey=""
oncommand="contextMenu.editLink();"/>
<menuitem id="context-openframe"
value="&openFrameCmd.label;"
accesskey=""
oncommand="contextMenu.openFrame();"/>
<menuitem id="context-showonlythisframe"
value="&showOnlyThisFrameCmd.label;"
accesskey="&showOnlyThisFrameCmd.accesskey;"
oncommand="contextMenu.showOnlyThisFrame();"/>
<menuseparator id="context-sep-open"/>
<!-- Navigation ============================== -->
<menuitem id="context-back"
value="&goBackCmd.label;"
accesskey="&goBackCmd.accesskey;"
oncommand="BrowserBack()"/>
<menuitem id="context-forward"
value="&goForwardCmd.label;"
accesskey="&goForwardCmd.accesskey;"
oncommand="BrowserForward()"/>
<menuitem id="context-reload"
value="&reloadCmd.label;"
accesskey="&reloadCmd.accesskey;"
oncommand="BrowserReallyReload(event);"/>
<menuitem id="context-reload-frame"
value="&reloadFrameCmd.label;"
accesskey="&reloadFrameCmd.accesskey;"
oncommand="contextMenu.reloadFrame();"/>
<menuitem id="context-stop"
value="&stopCmd.label;"
accesskey=""
oncommand="BrowserStop();"/>
<menuseparator/>
<!-- View ==================================== -->
<menuitem id="context-viewsource"
value="&viewPageSourceCmd.label;"
accesskey=""
oncommand="BrowserViewSource();"/>
<menuitem id="context-viewframesource"
value="&viewFrameSourceCmd.label;"
accesskey=""
oncommand="contextMenu.viewFrameSource();"/>
<menuitem id="context-viewinfo"
value="&viewPageInfoCmd.label;"
accesskey=""
oncommand="contextMenu.viewInfo();"/>
<menuitem id="context-viewframeinfo"
value="&viewFrameInfoCmd.label;"
accesskey=""
oncommand="contextMenu.viewFrameInfo();"/>
<menuitem id="context-viewimage"
value="&viewImageCmd.label;"
accesskey=""
oncommand="contextMenu.viewImage();"/>
<menuitem id="context-blockimage"
value="&blockImageCmd.label;"
accesskey=""
oncommand="contextMenu.blockImage();"/>
<menuseparator id="context-sep-view"/>
<!-- Misc ==================================== -->
<menuitem id="context-bookmarkpage"
value="&bookmarkPageCmd.label;"
accesskey="&bookmarkPageCmd.accesskey;"
oncommand="BrowserAddBookmark( window._content.location.href,
window._content.document.title )"/>
<menuitem id="context-bookmarklink"
value="&bookmarkLinkCmd.label;"
accesskey="&bookmarkLinkCmd.accesskey;"
oncommand="BrowserAddBookmark( contextMenu.linkURL(),
contextMenu.linkText() )"/>
<menuseparator/>
<!-- Save ==================================== -->
<menuitem id="context-savepage"
value="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey;"
oncommand="contextMenu.savePage();"/>
<menuitem id="context-saveframe"
value="&saveFrameCmd.label;"
accesskey="&saveFrameCmd.accesskey;"
oncommand="contextMenu.saveFrame();"/>
<menuitem id="context-savelink"
value="&saveLinkCmd.label;"
accesskey="&saveLinkCmd.accesskey;"
oncommand="contextMenu.saveLink();"/>
<menuitem id="context-saveimage"
value="&saveImageCmd.label;"
accesskey="&saveImageCmd.accesskey;"
oncommand="contextMenu.saveImage();"/>
<menuitem id="context-savebgimage"
value="&saveBGImageCmd.label;"
accesskey="&saveBGImageCmd.accesskey;"
oncommand="contextMenu.saveBGImage();"/>
<menuseparator id="context-sep-save"/>
<!-- Clipboard =============================== -->
<menuitem id="context-selectall"
value="&selectAllCmd.label;"
accesskey="&selectAllCmd.accesskey;"
observes="cmd_selectAll"/>
<menuitem id="context-cut"
value="&cutCmd.label;"
accesskey="&cutCmd.accesskey;"
observes="cmd_cut"/>
<menuitem id="context-copy"
value="&copyCmd.label;"
accesskey="&copyCmd.accesskey;"
observes="cmd_copy"/>
<menuitem id="context-paste"
value="&pasteCmd.label;"
accesskey="&pasteCmd.accesskey;"
observes="cmd_paste"/>
<menuitem id="context-capture"
value="&captureCmd.label;"
accesskey=""
oncommand="contextMenu.capture();"/>
<menuitem id="context-prefill"
value="&prefillCmd.label;"
accesskey=""
oncommand="contextMenu.prefill();"/>
<menuitem id="context-copylink"
value="&copyLinkCmd.label;"
accesskey="&copyLinkCmd.accesskey;"
oncommand="contextMenu.copyLink();"/>
<menuitem id="context-copyimage"
value="&copyImageCmd.label;"
accesskey="&copyImageCmd.accesskey;"
oncommand="contextMenu.copyImage();"/>
</popup>
</popupset>
</overlay>

Просмотреть файл

@ -19,130 +19,31 @@
*
* Contributors:
*
* Alec Flett <alecf@netscape.com>
* Ben Goodger <ben@netscape.com>
* Mike Pinkerton <pinkerton@netscape.com>
* Blake Ross <blakeross@telocity.com>
*/
var pref = null;
pref = Components.classes["@mozilla.org/preferences;1"];
pref = pref.getService();
pref = pref.QueryInterface(Components.interfaces.nsIPref);
function findParentNode(node, parentNode)
{
while (node) {
var nodeName = node.localName;
if (!nodeName)
return null;
nodeName = nodeName.toLowerCase();
if (nodeName == "body" || nodeName == "html" ||
nodeName == "#document") {
return null;
}
if (nodeName == parentNode)
return node;
node = node.parentNode;
}
return null;
}
// Called whenever the user clicks in the content area,
// except when left-clicking on links (special case)
// should always return true for click to go through
function contentAreaClick(event)
{
var target = event.originalTarget;
var linkNode;
switch (target.localName.toLowerCase()) {
case "a":
linkNode = event.target;
break;
case "area":
if (event.target.href)
linkNode = event.target;
break;
default:
linkNode = findParentNode(target, "a");
break;
}
if (linkNode) {
handleLinkClick(event, linkNode.href);
return true;
}
if (pref && event.button == 2 &&
!findParentNode(target, "scrollbar") &&
pref.GetBoolPref("middlemouse.paste")) {
middleMousePaste(event);
}
return true;
}
function handleLinkClick(event, href)
{
switch (event.button) {
case 1: // if left button clicked
if (event.metaKey || event.ctrlKey) { // and meta or ctrl are down
openNewWindowWith(href); // open link in new window
event.preventBubble();
return true;
}
if (event.shiftKey) { // if shift is down
savePage(href); // save the link
return true;
}
if (event.altKey) // if alt is down
return true; // do nothing
return false;
case 2: // if middle button clicked
if (pref && pref.GetBoolPref("middlemouse.openNewWindow")) { // and the pref is on
openNewWindowWith(href); // open link in new window
event.preventBubble();
return true;
}
break;
}
return false;
}
function middleMousePaste(event)
{
var url = readFromClipboard();
//dump ("Loading URL on clipboard: '" + url + "'; length = " + url.length + "\n");
if (url) {
var urlBar = document.getElementById("urlbar");
urlBar.value = url;
BrowserLoadURL();
event.preventBubble();
return true;
}
return false;
}
function openNewWindowWith( url ) {
var newWin;
var wintype = document.firstChild.getAttribute('windowtype');
function openNewWindowWith( url ) {
var newWin;
var wintype = document.firstChild.getAttribute('windowtype');
// if and only if the current window is a browser window and it has a document with a character
// set, then extract the current charset menu setting from the current document and use it to
// initialize the new browser window...
if (window && (wintype == "navigator:browser") &&
window._content && window._content.document) {
var DocCharset = window._content.document.characterSet;
charsetArg = "charset="+DocCharset;
dump("*** Current document charset: " + DocCharset + "\n");
// if and only if the current window is a browser window and it has a document with a character
// set, then extract the current charset menu setting from the current document and use it to
// initialize the new browser window...
if (window && (wintype == "navigator:browser") &&
window._content && window._content.document) {
var DocCharset = window._content.document.characterSet;
charsetArg = "charset="+DocCharset;
dump("*** Current document charset: " + DocCharset + "\n");
//we should "inherit" the charset menu setting in a new window
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url, charsetArg );
}
else { // forget about the charset information.
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url );
}
//we should "inherit" the charset menu setting in a new window
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url, charsetArg );
}
else { // forget about the charset information.
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url );
}
// Fix new window.
newWin.saveFileAndPos = true;
}
// Fix new window.
newWin.saveFileAndPos = true;
}
function savePage(url)
{
@ -164,4 +65,27 @@
}
return true;
}
}
function editPage(url)
{
window.openDialog( "chrome://editor/content", "_blank", "chrome,all,dialog=no", url );
}
function findParentNode(node, parentNode)
{
while (node) {
var nodeName = node.localName;
if (!nodeName)
return null;
nodeName = nodeName.toLowerCase();
if (nodeName == "body" || nodeName == "html" ||
nodeName == "#document") {
return null;
}
if (nodeName == parentNode)
return node;
node = node.parentNode;
}
return null;
}

Просмотреть файл

@ -0,0 +1,832 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Communicator client code,
* released March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* William A. ("PowerGUI") Law <law@netscape.com>
* Blake Ross <blakeross@telocity.com>
*/
/*------------------------------ nsContextMenu ---------------------------------
| This JavaScript "class" is used to implement the browser's content-area |
| context menu. |
| |
| For usage, see references to this class in navigator.xul. |
| |
| Currently, this code is relatively useless for any other purpose. In the |
| longer term, this code will be restructured to make it more reusable. |
------------------------------------------------------------------------------*/
function nsContextMenu( xulMenu ) {
this.target = null;
this.menu = null;
this.onTextInput = false;
this.onImage = false;
this.onLink = false;
this.onSaveableLink = false;
this.link = false;
this.inFrame = false;
this.hasBGImage = false;
this.inDirList = false;
this.shouldDisplay = true;
this.disableCapture = false;
// Initialize new menu.
this.initMenu( xulMenu );
}
// Prototype for nsContextMenu "class."
nsContextMenu.prototype = {
// onDestroy is a no-op at this point.
onDestroy : function () {
},
// Initialize context menu.
initMenu : function ( popup, event ) {
// Save menu.
this.menu = popup;
// Get contextual info.
this.setTarget( document.popupNode );
// Initialize (disable/remove) menu items.
this.initItems();
},
initItems : function () {
this.initOpenItems();
this.initNavigationItems();
this.initViewItems();
this.initMiscItems();
this.initSaveItems();
this.initClipboardItems();
},
initOpenItems : function () {
// Remove open/edit link if not applicable.
this.showItem( "context-openlink", this.onSaveableLink || ( this.inDirList && this.onLink ) );
this.showItem( "context-editlink", this.onSaveableLink && !this.inDirList );
// Remove open frame if not applicable.
this.showItem( "context-openframe", this.inFrame );
this.showItem( "context-showonlythisframe", this.inFrame );
// Remove separator after open items if neither link nor frame.
this.showItem( "context-sep-open", this.onSaveableLink || ( this.inDirList && this.onLink ) || this.inFrame );
},
initNavigationItems : function () {
// Back determined by canGoBack broadcaster.
this.setItemAttrFromNode( "context-back", "disabled", "canGoBack" );
// Forward determined by canGoForward broadcaster.
this.setItemAttrFromNode( "context-forward", "disabled", "canGoForward" );
// Reload is OK if not on a frame; vice-versa for reload-frame.
this.showItem( "context-reload", !this.inFrame );
this.showItem( "context-reload-frame", this.inFrame );
// XXX: Stop is determined in navigator.js; the canStop broadcaster is broken
//this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
},
initSaveItems : function () {
// Save page is always OK, unless in directory listing.
this.showItem( "context-savepage", !this.inDirList );
// Save frame as depends on whether we're in a frame.
this.showItem( "context-saveframe", this.inFrame );
// Save link depends on whether we're in a link.
this.showItem( "context-savelink", this.onSaveableLink );
// Save background image depends on whether there is one.
this.showItem( "context-savebgimage", this.hasBGImage );
// Save image depends on whether there is one.
this.showItem( "context-saveimage", this.onImage );
// Remove separator if none of these were shown.
var showSep = !this.inDirList || this.inFrame || this.onSaveableLink || this.hasBGImage || this.onImage;
this.showItem( "context-sep-save", showSep );
},
initViewItems : function () {
// View source is always OK, unless in directory listing.
this.showItem( "context-viewsource", !this.inDirList );
// View frame source depends on whether we're in a frame.
this.showItem( "context-viewframesource", this.inFrame );
// View Info don't work no way no how.
this.showItem( "context-viewinfo", false );
// View Frame Info isn't working, either.
this.showItem( "context-viewframeinfo", false );
// View Image depends on whether an image was clicked on.
this.showItem( "context-viewimage", this.onImage );
// Block image depends on whether an image was clicked on, and,
// whether the user pref is enabled.
this.showItem( "context-blockimage", this.onImage && this.isBlockingImages() );
// Capture depends on whether a form is being displayed.
this.showItem( "context-capture", this.okToCapture() );
this.setItemAttr( "context-capture", "disabled", this.disableCapture);
// Prefill depends on whether a form is being displayed.
this.showItem( "context-prefill", this.okToPrefill() );
// Remove separator if all items are removed.
this.showItem( "context-sep-view", !this.inDirList || this.inFrame || this.onImage );
},
initMiscItems : function () {
// Use "Bookmark This Link" if on a link.
this.showItem( "context-bookmarkpage", !this.onLink );
this.showItem( "context-bookmarklink", this.onLink );
// Send Page not working yet.
this.showItem( "context-sendpage", false );
},
initClipboardItems : function () {
// Select All is always OK, unless in directory listing.
this.showItem( "context-selectall", !this.inDirList );
// Copy depends on whether there is selected text.
// Enabling this context menu item is now done through the global
// command updating system
// this.setItemAttr( "context-copy", "disabled", this.isNoTextSelected() );
goUpdateGlobalEditMenuItems();
// Items for text areas
this.showItem( "context-cut", this.onTextInput );
this.showItem( "context-paste", this.onTextInput );
// Copy link location depends on whether we're on a link.
this.showItem( "context-copylink", this.onLink );
// Copy image location depends on whether we're on an image.
this.showItem( "context-copyimage", this.onImage );
},
// Set various context menu attributes based on the state of the world.
setTarget : function ( node ) {
// Initialize contextual info.
this.onImage = false;
this.onTextInput = false;
this.imageURL = "";
this.onLink = false;
this.inFrame = false;
this.hasBGImage = false;
// Remember the node that was clicked.
this.target = node;
// See if the user clicked on an image.
if ( this.target.nodeType == 1 ) {
if ( this.target.tagName.toUpperCase() == "IMG" ) {
this.onImage = true;
this.imageURL = this.target.src;
// Look for image map.
var mapName = this.target.getAttribute( "usemap" );
if ( mapName ) {
// Find map.
var map = this.target.ownerDocument.getElementById( mapName.substr(1) );
if ( map ) {
// Search child <area>s for a match.
var areas = map.childNodes;
//XXX Client side image maps are too hard for now!
dump( "Client side image maps not supported yet, sorry!\n" );
areas.length = 0;
for ( var i = 0; i < areas.length && !this.onLink; i++ ) {
var area = areas[i];
if ( area.nodeType == 1
&&
area.tagName.toUpperCase() == "AREA" ) {
// Get type (rect/circle/polygon/default).
var type = area.getAttribute( "type" );
var coords = this.parseCoords( area );
switch ( type.toUpperCase() ) {
case "RECT":
case "RECTANGLE":
break;
case "CIRC":
case "CIRCLE":
break;
case "POLY":
case "POLYGON":
break;
case "DEFAULT":
// Default matches entire image.
this.onLink = true;
this.link = area;
this.onSaveableLink = this.isLinkSaveable( this.link );
break;
}
}
}
}
}
} else if ( this.target.tagName.toUpperCase() == "OBJECT"
&&
// See if object tag is for an image.
this.objectIsImage( this.target ) ) {
// This is an image.
this.onImage = true;
// URL must be constructed.
this.imageURL = this.objectImageURL( this.target );
} else if ( this.target.tagName.toUpperCase() == "INPUT") {
if(this.target.getAttribute( "type" ).toUpperCase() == "IMAGE") {
this.onImage = true;
// Convert src attribute to absolute URL.
this.imageURL = this.makeURLAbsolute( this.target.ownerDocument,
this.target.src );
} else /* if (this.target.getAttribute( "type" ).toUpperCase() == "TEXT") */ {
this.onTextInput = this.isTargetATextField(this.target);
}
} else if ( this.target.tagName.toUpperCase() == "TEXTAREA" ) {
this.onTextInput = true;
} else if ( this.target.getAttribute( "background" ) ) {
this.onImage = true;
// Convert background attribute to absolute URL.
this.imageURL = this.makeURLAbsolute( this.target.ownerDocument,
this.target.getAttribute( "background" ) );
} else if ( window._content.HTTPIndex == "[xpconnect wrapped nsIHTTPIndex]"
&&
typeof window._content.HTTPIndex == "object"
&&
!window._content.HTTPIndex.constructor ) {
// The above test is a roundabout way of determining whether
// the content area contains chrome://global/content/directory/directory.xul.
this.inDirList = true;
// Bubble outward till we get to an element with id= attribute
// (which should be the href).
var root = this.target;
while ( root && !this.link ) {
if ( root.getAttribute( "id" ) ) {
if ( root.tagName == "tree" ) {
// Hit root of tree; must have clicked in empty space;
// thus, no link.
break;
}
// Build pseudo link object so link-related functions work.
this.onLink = true;
this.link = { href : root.getAttribute( "id" ) };
// If element is a directory, then you can't save it.
if ( root.getAttribute( "container" ) == "true" ) {
this.onSaveableLink = false;
} else {
this.onSaveableLink = true;
}
} else {
root = root.parentNode;
}
}
} else if ( this.target.parentNode.tagName == "scrollbar"
||
this.target.parentNode.tagName == "thumb"
||
this.target.parentNode.tagName == "xul:slider") {
this.shouldDisplay = false;
} else {
try {
var cssAttr = this.target.style.getPropertyValue( "list-style-image" ) ||
this.target.style.getPropertyValue( "list-style" ) ||
this.target.style.getPropertyValue( "background-image" ) ||
this.target.style.getPropertyValue( "background" );
if ( cssAttr ) {
this.onImage = true;
var url = cssAttr.toLowerCase().replace(/url\("*(.+)"*\)/, "$1");
// Convert attribute to absolute URL.
this.imageURL = this.makeURLAbsolute( this.target.ownerDocument, url );
}
} catch ( exception ) {
}
}
}
// See if the user clicked in a frame.
if ( this.target.ownerDocument != window._content.document ) {
this.inFrame = true;
}
// Bubble up looking for an input or textarea
var elem = this.target;
while ( elem && !this.onTextInput ) {
// Test for element types of interest.
if ( elem.nodeType == 1 ) {
// Clicked on a link.
this.onTextInput = this.isTargetATextField(elem);
}
elem = elem.parentNode;
}
// Bubble out, looking for link.
elem = this.target;
while ( elem && !this.onLink ) {
// Test for element types of interest.
if ( elem.nodeType == 1 &&
( elem.tagName.toUpperCase() == "A"
||
elem.tagName.toUpperCase() == "AREA"
||
elem.getAttributeNS("http://www.w3.org/1999/xlink","type") == "simple")) {
// Clicked on a link.
this.onLink = true;
// Remember corresponding element.
this.link = elem;
// Remember if it is saveable.
this.onSaveableLink = this.isLinkSaveable( this.link );
}
elem = elem.parentNode;
}
},
// Returns true iff clicked on link is saveable.
isLinkSaveable : function ( link ) {
// Test for missing protocol property.
if ( !link.protocol ) {
// We must resort to testing the URL string :-(.
var protocol;
if (link.href) {
protocol = link.href.substr( 0, 11 );
} else {
protocol = link.getAttributeNS("http://www.w3.org/1999/xlink","href");
if (protocol) {
protocol = protocol.substr( 0, 11 );
}
}
return protocol.toLowerCase() != "javascript:";
} else {
// Presume all but javascript: urls are saveable.
return link.protocol.toLowerCase() != "javascript:";
}
},
// Open linked-to URL in a new window.
openLink : function () {
// Determine linked-to URL.
openNewWindowWith( this.linkURL() );
},
// Edit linked-to URL in a new window.
editLink : function () {
editPage( this.linkURL() );
},
// Reload clicked-in frame.
reloadFrame : function () {
this.target.ownerDocument.location.reload();
},
// Open clicked-in frame in its own window.
openFrame : function () {
openNewWindowWith( this.target.ownerDocument.location.href );
},
// Open clicked-in frame in the same window
showOnlyThisFrame : function () {
window._content.location.href = this.target.ownerDocument.location.href;
},
// Open new "view source" window with the frame's URL.
viewFrameSource : function () {
window.openDialog( "chrome://navigator/content/viewSource.xul",
"_blank",
"scrollbars,resizable,chrome,dialog=no",
this.target.ownerDocument.location.href);
},
viewInfo : function () {
dump( "nsContextMenu.viewInfo not implemented yet\n" );
},
viewFrameInfo : function () {
dump( "nsContextMenu.viewFrameInfo not implemented yet\n" );
},
// Open new window with the URL of the image.
viewImage : function () {
openNewWindowWith( this.imageURL );
},
// Save URL of clicked-on frame.
saveFrame : function () {
this.savePage( this.target.ownerDocument.location.href, true );
},
// Save URL of clicked-on link.
saveLink : function () {
this.savePage( this.linkURL(), false );
},
// Save URL of clicked-on image.
saveImage : function () {
this.savePage( this.imageURL, true );
},
// Save URL of background image.
saveBGImage : function () {
this.savePage( this.bgImageURL(), true );
},
// Generate link URL and put it on clibboard.
copyLink : function () {
this.copyToClipboard( this.linkURL() );
},
// Generate image URL and put it on the clipboard.
copyImage : function () {
this.copyToClipboard( this.imageURL );
},
// Capture the values that are filled in on the form being displayed.
capture : function () {
var walletService = Components.classes["@mozilla.org/wallet/wallet-service;1"].getService(Components.interfaces.nsIWalletService);
walletService.WALLET_RequestToCapture(window._content);
},
// Prefill the form being displayed.
prefill : function () {
walletPreview(window._content);
},
// Determine if "Save Form Data" is to appear in the menu.
okToCapture: function () {
this.disableCapture = false;
var rv = false;
if (!window._content.document) {
return false;
}
// test for a form with at least one text element that has a value
var formsArray = this.target.ownerDocument.forms;
if (!formsArray) {
return false;
}
var form;
for (form=0; form<formsArray.length; form++) {
var elementsArray = formsArray[form].elements;
var element;
for (element=0; element<elementsArray.length; element++) {
var type = elementsArray[element].type;
if (type=="" || type=="text") {
// we have a form with at least one text element
var value = elementsArray[element].value;
rv = true;
if (value != "") {
// at least one text element has a value, thus capture is to appear in menu
return rv;
}
}
}
}
// if we got here, then there was no text element with a value
if (rv) {
// if we got here, then we had a form with at least one text element
// in that case capture is to appear in menu but will be disabled
this.disableCapture = true;
}
return rv;
},
// Determine if "Prefill Form" is to appear in the menu.
okToPrefill: function () {
if (!window._content.document) {
return false;
}
var formsArray = this.target.ownerDocument.forms;
if (!formsArray) {
return false;
}
var form;
for (form=0; form<formsArray.length; form++) {
var elementsArray = formsArray[form].elements;
var element;
for (element=0; element<elementsArray.length; element++) {
var type = elementsArray[element].type;
var value = elementsArray[element].value;
if (type=="" || type=="text" || type=="select-one") {
return true;
}
}
}
return false;
},
// Determine if "Block Image" is to appear in the menu.
// Return true if "imageBlocker.enabled" pref is set and image is not already blocked.
isBlockingImages: function () {
/* determine if "imageBlocker.enabled" pref is set */
var pref = this.getService( '@mozilla.org/preferences;1', 'nsIPref' );
var result = false;
try {
result = pref.GetBoolPref( "imageblocker.enabled" );
} catch(e) {
}
if (!result) {
/* pref is not set so return false */
return false;
}
/* determine if image is already being blocked */
var cookieViewer = this.createInstance
("@mozilla.org/cookieviewer/cookieviewer-world;1", "nsICookieViewer");
var list = cookieViewer.GetPermissionValue(1);
var permissionList = list.split(list[0]);
for(var i = 1; i < permissionList.length; i+=2) {
permStr = permissionList[i+1];
var type = permStr.substring(0,1);
if (type == "-") {
/* some host is being blocked, need to find out if it's our image's host */
var host = permStr.substring(1,permStr.length);
if (this.imageURL.search(host) != -1) {
/* it's our image's host that's being blocked */
return false;
}
}
}
/* image is not already being blocked, so "Block Image" can appear on the menu */
return true;
},
// Block image from loading in the future.
blockImage : function () {
var cookieViewer = this.createInstance( "@mozilla.org/cookieviewer/cookieviewer-world;1",
"nsICookieViewer" );
cookieViewer.BlockImage(this.imageURL);
},
///////////////
// Utilities //
///////////////
// Create instance of component given contractId and iid (as string).
createInstance : function ( contractId, iidName ) {
var iid = Components.interfaces[ iidName ];
return Components.classes[ contractId ].createInstance( iid );
},
// Get service given contractId and iid (as string).
getService : function ( contractId, iidName ) {
var iid = Components.interfaces[ iidName ];
return Components.classes[ contractId ].getService( iid );
},
// Show/hide one item (specified via name or the item element itself).
showItem : function ( itemOrId, show ) {
var item = null;
if ( itemOrId.constructor == String ) {
// Argument specifies item id.
item = document.getElementById( itemOrId );
} else {
// Argument is the item itself.
item = itemOrId;
}
if ( item ) {
var styleIn = item.getAttribute( "style" );
var styleOut = styleIn;
if ( show ) {
// Remove style="display:none;".
styleOut = styleOut.replace( "display:none;", "" );
} else {
// Set style="display:none;".
if ( styleOut.indexOf( "display:none;" ) == -1 ) {
// Add style the first time we need to.
styleOut += "display:none;";
}
}
// Only set style if it's different.
if ( styleIn != styleOut ) {
item.setAttribute( "style", styleOut );
}
}
},
// Set given attribute of specified context-menu item. If the
// value is null, then it removes the attribute (which works
// nicely for the disabled attribute).
setItemAttr : function ( id, attr, val ) {
var elem = document.getElementById( id );
if ( elem ) {
if ( val == null ) {
// null indicates attr should be removed.
elem.removeAttribute( attr );
} else {
// Set attr=val.
elem.setAttribute( attr, val );
}
}
},
// Set context menu attribute according to like attribute of another node
// (such as a broadcaster).
setItemAttrFromNode : function ( item_id, attr, other_id ) {
var elem = document.getElementById( other_id );
if ( elem && elem.getAttribute( attr ) == "true" ) {
this.setItemAttr( item_id, attr, "true" );
} else {
this.setItemAttr( item_id, attr, null );
}
},
// Temporary workaround for DOM api not yet implemented by XUL nodes.
cloneNode : function ( item ) {
// Create another element like the one we're cloning.
var node = document.createElement( item.tagName );
// Copy attributes from argument item to the new one.
var attrs = item.attributes;
for ( var i = 0; i < attrs.length; i++ ) {
var attr = attrs.item( i );
node.setAttribute( attr.nodeName, attr.nodeValue );
}
// Voila!
return node;
},
// Generate fully-qualified URL for clicked-on link.
linkURL : function () {
if (this.link.href) {
return this.link.href;
}
// XXX TODO Relative URLs, XML Base
var href = this.link.getAttributeNS("http://www.w3.org/1999/xlink","href");
if (href == "") {
throw "Empty href"; // Without this we try to save as the current doc, for example, HTML case also throws if empty
}
return href;
},
// Get text of link (if possible).
linkText : function () {
var text = this.gatherTextUnder( this.link );
return text;
},
// Gather all descendent text under given document node.
gatherTextUnder : function ( root ) {
var text = "";
var node = root.firstChild;
var depth = 1;
while ( node && depth > 0 ) {
// See if this node is text.
if ( node.nodeName == "#text" ) {
// Add this text to our collection.
text += " " + node.data;
} else if ( node.tagName == "IMG" ) {
// If it has an alt= attribute, use that.
altText = node.getAttribute( "alt" );
if ( altText && altText != "" ) {
text = altText;
break;
}
}
// Find next node to test.
// First, see if this node has children.
if ( node.hasChildNodes() ) {
// Go to first child.
node = node.firstChild;
depth++;
} else {
// No children, try next sibling.
if ( node.nextSibling ) {
node = node.nextSibling;
} else {
// Last resort is our next oldest uncle/aunt.
node = node.parentNode.nextSibling;
depth--;
}
}
}
// Strip leading whitespace.
text = text.replace( /^\s+/, "" );
// Strip trailing whitespace.
text = text.replace( /\s+$/, "" );
// Compress remaining whitespace.
text = text.replace( /\s+/g, " " );
return text;
},
// Returns "true" if there's no text selected, null otherwise.
isNoTextSelected : function ( event ) {
// Not implemented so all text-selected-based options are disabled.
return "true";
},
// Copy link/image url to clipboard.
copyToClipboard : function ( text ) {
// Get clipboard.
var clipboard = this.getService( "@mozilla.org/widget/clipboard;1",
"nsIClipboard" );
// Create tranferable that will transfer the text.
var transferable = this.createInstance( "@mozilla.org/widget/transferable;1",
"nsITransferable" );
if ( clipboard && transferable ) {
transferable.addDataFlavor( "text/unicode" );
// Create wrapper for text.
var data = this.createInstance( "@mozilla.org/supports-wstring;1",
"nsISupportsWString" );
if ( data ) {
data.data = text;
transferable.setTransferData( "text/unicode", data, text.length * 2 );
// Put on clipboard.
clipboard.setData( transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard );
}
}
// Create a second transferable to copy selection. Unix needs this,
// other OS's will probably map to a no-op.
var transferableForSelection = this.createInstance( "@mozilla.org/widget/transferable;1",
"nsITransferable" );
if ( clipboard && transferableForSelection ) {
transferableForSelection.addDataFlavor( "text/unicode" );
// Create wrapper for text.
var selectionData = this.createInstance( "@mozilla.org/supports-wstring;1",
"nsISupportsWString" );
if ( selectionData ) {
selectionData.data = text;
transferableForSelection.setTransferData( "text/unicode", selectionData, text.length * 2 );
// Put on clipboard.
clipboard.setData( transferableForSelection, null,
Components.interfaces.nsIClipboard.kSelectionClipboard );
}
}
},
// Determine if target <object> is an image.
objectIsImage : function ( objElem ) {
var result = false;
// Get type and data attributes.
var type = objElem.getAttribute( "type" );
var data = objElem.getAttribute( "data" );
// Presume any mime type of the form "image/..." is an image.
// There must be a data= attribute with an URL, also.
if ( type.substring( 0, 6 ) == "image/" && data && data != "" ) {
result = true;
}
return result;
},
// Extract image URL from <object> tag.
objectImageURL : function ( objElem ) {
// Extract url from data= attribute.
var data = objElem.getAttribute( "data" );
// Make it absolute.
return this.makeURLAbsolute( objElem.ownerDocument, data );
},
// Convert relative URL to absolute, using document's <base>.
makeURLAbsolute : function ( doc, url ) {
// Construct nsIURL.
var baseURL = this.createInstance( "@mozilla.org/network/standard-url;1", "nsIURL" );
// Initialize from document url.
baseURL.spec = doc.location.href;
// Look for <base> tag.
var baseTags = doc.getElementsByTagName( "BASE" );
if ( baseTags && baseTags.length ) {
// Reset base URL using href attribute of <base> tag.
var href = baseTags[ baseTags.length - 1 ].getAttribute( "href" );
baseURL.spec = baseURL.resolve( href );
}
// Finally, convert argument url using base.
var result = baseURL.resolve( url );
return result;
},
// Save specified URL in user-selected file.
savePage : function ( url, doNotValidate ) {
var postData = null; // No post data, usually.
// Default is to save current page.
if ( !url ) {
url = window._content.location.href;
// Post data comes from appcore.
if ( window.appCore ) {
postData = window.appCore.postData;
}
}
// Use stream xfer component to prompt for destination and save.
var xfer = this.getService( "@mozilla.org/appshell/component/xfer;1",
"nsIStreamTransfer" );
try {
xfer.SelectFileAndTransferLocationSpec( url, window, "", "", doNotValidate, postData );
} catch( exception ) {
// Failed (or cancelled), give them another chance.
dump( "SelectFileAndTransferLocationSpec failed, rv=" + exception + "\n" );
}
return;
},
// Parse coords= attribute and return array.
parseCoords : function ( area ) {
return [];
},
toString : function () {
return "contextMenu.target = " + this.target + "\n" +
"contextMenu.onImage = " + this.onImage + "\n" +
"contextMenu.onLink = " + this.onLink + "\n" +
"contextMenu.link = " + this.link + "\n" +
"contextMenu.inFrame = " + this.inFrame + "\n" +
"contextMenu.hasBGImage = " + this.hasBGImage + "\n";
},
isTargetATextField : function ( node )
{
if (node.tagName.toUpperCase() == "INPUT") {
var attrib = node.getAttribute("type").toUpperCase();
return( (attrib != "IMAGE") &&
(attrib != "PASSWORD") &&
(attrib != "CHECKBOX") &&
(attrib != "RADIO") &&
(attrib != "SUBMIT") &&
(attrib != "RESET") &&
(attrib != "FILE") &&
(attrib != "HIDDEN") &&
(attrib != "RESET") &&
(attrib != "BUTTON") );
} else {
return(node.tagName.toUpperCase() == "TEXTAREA");
}
}
};

Просмотреть файл

@ -0,0 +1,61 @@
<!-- Context Menu -->
<!ENTITY blockImageCmd.label "Block Image from Loading">
<!ENTITY blockImageCmd.accesskey "k">
<!ENTITY openLinkCmd.label "Open Link in New Window">
<!ENTITY openLinkCmd.accesskey "o">
<!ENTITY editLinkCmd.label "Edit Link in Composer">
<!ENTITY editLinkCmd.accesskey "e">
<!ENTITY openFrameCmd.label "Open Frame in New Window">
<!ENTITY openFrameCmd.accesskey "o">
<!ENTITY showOnlyThisFrameCmd.label "Show Only This Frame">
<!ENTITY showOnlyThisFrameCmd.accesskey "w">
<!ENTITY goBackCmd.label "Back">
<!ENTITY goBackCmd.accesskey "b">
<!ENTITY goForwardCmd.label "Forward">
<!ENTITY goForwardCmd.accesskey "f">
<!ENTITY reloadCmd.label "Reload">
<!ENTITY reloadCmd.accesskey "r">
<!ENTITY reloadCmd.commandkey "r">
<!ENTITY stopCmd.label "Stop">
<!ENTITY stopCmd.accesskey "s">
<!ENTITY reloadFrameCmd.label "Reload Frame">
<!ENTITY reloadFrameCmd.accesskey "r">
<!ENTITY viewPageSourceCmd.label "View Page Source">
<!ENTITY viewPageSourceCmd.accesskey "v">
<!ENTITY viewFrameSourceCmd.label "View Frame Source">
<!ENTITY viewFrameSourceCmd.accesskey "v">
<!ENTITY viewPageInfoCmd.label "View Page Info">
<!ENTITY viewPageInfoCmd.accesskey "i">
<!ENTITY viewFrameInfoCmd.label "View Frame Info">
<!ENTITY viewFrameInfoCmd.accesskey "i">
<!ENTITY viewImageCmd.label "View Image">
<!ENTITY viewImageCmd.accesskey "w">
<!ENTITY bookmarkPageCmd.label "Bookmark this Page">
<!ENTITY bookmarkPageCmd.accesskey "b">
<!ENTITY bookmarkLinkCmd.label "Bookmark this Link">
<!ENTITY bookmarkLinkCmd.accesskey "b">
<!ENTITY savePageCmd.label "Save As...">
<!ENTITY savePageCmd.accesskey "s">
<!ENTITY savePageCmd.commandkey "s">
<!ENTITY saveFrameCmd.label "Save Frame As...">
<!ENTITY saveFrameCmd.accesskey "f">
<!ENTITY saveLinkCmd.label "Save Link As...">
<!ENTITY saveLinkCmd.accesskey "a">
<!ENTITY saveImageCmd.label "Save Image As...">
<!ENTITY saveImageCmd.accesskey "a">
<!ENTITY saveBGImageCmd.label "Save Background Image As...">
<!ENTITY saveBGImageCmd.accesskey "a">
<!ENTITY copyCmd.label "Copy">
<!ENTITY copyCmd.accesskey "c">
<!ENTITY selectAllCmd.label "Select All">
<!ENTITY selectAllCmd.accesskey "l">
<!ENTITY copyLinkCmd.label "Copy Link Location">
<!ENTITY copyLinkCmd.accesskey "n">
<!ENTITY copyImageCmd.label "Copy Image Location">
<!ENTITY copyImageCmd.accesskey "m">
<!ENTITY pasteCmd.label "Paste">
<!ENTITY pasteCmd.accesskey "p">
<!ENTITY captureCmd.label "Save Form Data">
<!ENTITY prefillCmd.label "Prefill Form">
<!ENTITY cutCmd.label "Cut">
<!ENTITY cutCmd.accesskey "u">