Change JavaScript polices for MailNews: disallowed entirely in messages; allowed elsewhere (bug 374577), r=bienvenu, sr=bzbarsky

This commit is contained in:
Dan Mosedale 2009-02-17 18:59:52 -08:00
Родитель 0c0324a438
Коммит 9723311313
7 изменённых файлов: 443 добавлений и 12 удалений

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

@ -267,7 +267,7 @@ let gFolderTreeView = {
* that the folder is actually being displayed (that is, that none of its
* ancestors are collapsed.
*
* @param aFolderUri the nsIMsgFolder to select
* @param aFolder the nsIMsgFolder to select
*/
selectFolder: function ftv_selectFolder(aFolder) {
// "this" inside the nested function refers to the function...

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

@ -0,0 +1,216 @@
/**
* Test whether javascript in a local message works.
*
* @note This assumes an existing local account, and will cause the Trash
* folder of that account to be emptied multiple times.
*/
//
/* ***** 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
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dan Mosedale <dmose@mozilla.org>
* Joey Minta <jminta@gmail.com>
*
* 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 ***** */
// XXXdmose MozMill doesn't have much asynchronicity support in it yet. Once
// that changes, we should be able to more thoroughly test that there aren't
// any races here. As it stands, we use controller.sleep() a lot to wait for
// things to finish loading before proceeding. Some of those calls could
// probably be replaced with waitForEval/waitForPageLoad or a hypothetical
// waitForEvent.
var controller = {};
Components.utils.import('resource://mozmill/modules/controller.js', controller);
var jum = {};
Components.utils.import('resource://mozmill/modules/jum.js', jum);
Components.utils.import("resource://gre/modules/iteratorUtils.jsm");
var mainWindow = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("mail:3pane");
var MC = new controller.MozMillController(mainWindow);
function addToFolder(aSubject, aBody, aFolder) {
let msgId = Components.classes["@mozilla.org/uuid-generator;1"]
.getService(Components.interfaces.nsIUUIDGenerator)
.generateUUID() +"@mozillamessaging.com";
let source = "From - Sat Nov 1 12:39:54 2008\n" +
"X-Mozilla-Status: 0001\n" +
"X-Mozilla-Status2: 00000000\n" +
"Message-ID: <" + msgId + ">\n" +
"Date: Wed, 11 Jun 2008 20:32:02 -0400\n" +
"From: Tester <tests@mozillamessaging.com>\n" +
"User-Agent: Thunderbird 3.0a2pre (Macintosh/2008052122)\n" +
"MIME-Version: 1.0\n" +
"To: recipient@mozillamessaging.com\n" +
"Subject: " + aSubject + "\n" +
"Content-Type: text/html; charset=ISO-8859-1\n" +
"Content-Transfer-Encoding: 7bit\n" +
"\n" + aBody + "\n";
aFolder.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder);
aFolder.gettingNewMessages = true;
// XXX this causes an identity NS_WARNING for unknown reasons
aFolder.addMessage(source);
aFolder.gettingNewMessages = false;
aFolder.updateFolder(mainWindow.msgWindow);
return aFolder.msgDatabase.getMsgHdrForMessageID(msgId);
}
const jsMsgBody = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n' +
'<html>\n' +
'<head>\n' +
'\n' +
'<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">\n' +
'</head>\n' +
'<body bgcolor="#ffffff" text="#000000">\n' +
'this is a test<big><big><big> stuff\n' +
'<br><br>\n' +
'</big></big></big>\n' +
'<script language="javascript"/>\n'+
'var jsIsTurnedOn = true;\n' +
'</script>\n' +
'\n' +
'</body>\n' +
'</html>\n';
const Cc = Components.classes;
const Ci = Components.interfaces;
const kTestFolderName = "testFolder";
let am = Cc["@mozilla.org/messenger/account-manager;1"].
getService(Ci.nsIMsgAccountManager);
let localRootFolder = am.localFoldersServer.rootFolder;
function ensureFreshTestFolder() {
//delete any existing test folder
try {
// get trash folder
let trashFolder = localRootFolder.getChildNamed("Trash");
// empty it without prompting
trashFolder.emptyTrash(mainWindow.msgWindow, null);
// try and get any existing test folder
let oldFolder = localRootFolder.getChildNamed(kTestFolderName);
// blow it away
let array = toXPCOMArray([oldFolder], Ci.nsIMutableArray);
oldFolder.parent.deleteSubFolders(array, null);
} catch (ex) {
dump("ignoring old folder deletion exception" + ex + "\n");
}
try {
localRootFolder.createSubfolder(kTestFolderName, mainWindow.msgWindow);
} catch (ex) {
dump("ignoring createSubfolder exception\n");
}
return localRootFolder.findSubFolder(kTestFolderName);
}
let gMsgNo = 0;
function checkJsInMail(aLocalTestFolder) {
let msgDbHdr = addToFolder("JS test message " + gMsgNo, jsMsgBody,
aLocalTestFolder);
// select the newly created message
mainWindow.GetThreadTree().view.selection.select(gMsgNo);
MC.sleep(10000);
jum.assertUndefined(mainWindow.content.wrappedJSObject.jsIsTurnedOn);
++gMsgNo;
return;
}
function checkJsInNonMessageContent() {
// get rid of the header pane to make the display less confusing
// to developers debugging this test.
// XXX should perhaps clear the threadpane selection too for the same reason
mainWindow.HideMessageHeaderPane();
// load something non-message-like in the message pane
mainWindow.GetMessagePaneFrame().location.href =
"data:text/html;charset=utf-8,<script>jsIsTurnedOn%3Dtrue%3B<%2Fscript>bar";
MC.sleep(10000);
jum.assertTrue(mainWindow.content.wrappedJSObject.jsIsTurnedOn);
return;
}
// run each test twice to ensure that there aren't any weird side effects,
// given that these loads all happen in the same docshell
function test_jsContentPolicy() {
dump("test_jsContentPolicy() starting\n");
// start from a known state.
mainWindow.ClearMessagePane();
MC.sleep(10000);
// blow away any existing test folder and create a fresh one
let localTestFolder = ensureFreshTestFolder();
// XXXdmose icky workaround: if we don't select some other folder first,
// selecting the test folder doesn't always cause the test folder to load,
// perhaps because something thinks it has already loaded. Perhaps this is
// related to commandglue.js:FolderPaneSelectionChanged() goofiness.
let inbox = localRootFolder.getChildNamed("Inbox");
mainWindow.gFolderTreeView.selectFolder(inbox);
// this starts the folder loading
mainWindow.gFolderTreeView.selectFolder(localTestFolder);
MC.sleep(10000);
checkJsInMail(localTestFolder);
checkJsInNonMessageContent();
checkJsInMail(localTestFolder);
checkJsInNonMessageContent();
dump("finished test_jsContentPolicy()\n");
}

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

@ -140,7 +140,15 @@ interface nsIMsgMessageUrl : nsISupports {
attribute boolean AddDummyEnvelope;
attribute boolean canonicalLineEnding;
attribute string originalSpec;
// a message db header for that message.
/**
* A message db header for that message.
*
* @note This attribute is not guaranteed to be set, so callers that
* actually require an nsIMsgDBHdr will need to use the uri attribute
* on this interface to get the appropriate nsIMsgMessageService and
* then get the header from there.
*/
readonly attribute nsIMsgDBHdr messageHeader;
};

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

@ -60,7 +60,15 @@ interface nsIMsgWindow : nsISupports {
attribute nsIMsgHeaderSink msgHeaderSink;
attribute nsITransactionManager transactionManager;
attribute nsIMsgFolder openFolder;
/**
* @note Setting this attribute has various side effects, including
* wiring up this object as the parent nsIURIContentListener for the
* passed-in docshell as well as setting the message content policy service
* to listen for OnLocationChange notifications.
*/
attribute nsIDocShell rootDocShell;
/**
* These are currently used to set notification callbacks on
* protocol channels to handle things like bad cert exceptions.

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

@ -21,6 +21,7 @@
*
* Contributor(s):
* Scott MacGregor <scott@scott-macgregor.org>
* Dan Mosedale <dmose@mozillamessaging.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -75,6 +76,8 @@
#include "nsContentPolicyUtils.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsILoadContext.h"
#include "nsIFrameLoader.h"
#include "nsIWebProgress.h"
static const char kBlockRemoteImages[] = "mailnews.message_display.disable_remote_image";
static const char kAllowPlugins[] = "mailnews.message_display.allow.plugins";
@ -88,8 +91,9 @@ static const char kTrustedDomains[] = "mail.trusteddomains";
#define kBlockRemoteContent 1
#define kAllowRemoteContent 2
NS_IMPL_ISUPPORTS3(nsMsgContentPolicy,
NS_IMPL_ISUPPORTS4(nsMsgContentPolicy,
nsIContentPolicy,
nsIWebProgressListener,
nsIObserver,
nsISupportsWeakReference)
@ -217,11 +221,6 @@ nsMsgContentPolicy::ShouldLoad(PRUint32 aContentType,
NS_ENSURE_ARG_POINTER(aContentLocation);
// NOTE: Not using NS_ENSURE_ARG_POINTER because this is a legitimate case
// that can happen.
if (!aRequestingLocation)
return NS_ERROR_INVALID_POINTER;
#ifndef MOZ_THUNDERBIRD
// Go find out if we are dealing with mailnews. Anything else
// isn't our concern and we accept content.
@ -236,13 +235,52 @@ nsMsgContentPolicy::ShouldLoad(PRUint32 aContentType,
return NS_OK;
#endif
if (aContentType == nsIContentPolicy::TYPE_OBJECT)
{
#ifdef DEBUG_MsgContentPolicy
nsCString spec;
#endif
switch(aContentType) {
case nsIContentPolicy::TYPE_OBJECT:
// only allow the plugin to load if the allow plugins pref has been set
if (!mAllowPlugins)
*aDecision = nsIContentPolicy::REJECT_TYPE;
return NS_OK;
case nsIContentPolicy::TYPE_DOCUMENT:
// At this point, we have no intention of supporting a different JS
// setting on a subdocument, so we don't worry about TYPE_SUBDOCUMENT here.
#ifdef DEBUG_MsgContentPolicy
(void)aContentLocation->GetSpec(spec);
fprintf(stderr, "aContentLocation = %s\n", spec.get());
#endif
// If the timing were right, we'd enable JavaScript on the docshell
// for non mailnews URIs here. However, at this point, the
// old document may still be around, so we can't do any enabling just yet.
// Instead, we apply the policy in nsIWebProgressListener::OnLocationChange.
// For now, we explicitly disable JavaScript in order to be safe rather than
// sorry, because OnLocationChange isn't guaranteed to necessarily be called
// soon enough to disable it in time (though bz says it _should_ be called
// soon enough "in all sane cases").
rv = DisableJSOnMailNewsUrlDocshells(aContentLocation, aRequestingContext);
// if something went wrong during the tweaking, reject this content
if (NS_FAILED(rv)) {
*aDecision = nsIContentPolicy::REJECT_TYPE;
return NS_OK;
}
break;
default:
break;
}
// NOTE: Not using NS_ENSURE_ARG_POINTER because this is a legitimate case
// that can happen. Also keep in mind that the default policy used for a
// failure code is ACCEPT.
if (!aRequestingLocation)
return NS_ERROR_INVALID_POINTER;
// if aRequestingLocation is chrome, resource about or file, allow
// aContentLocation to load
@ -533,6 +571,60 @@ nsresult nsMsgContentPolicy::ComposeShouldLoad(nsIDocShell * aRootDocShell, nsIS
return NS_OK;
}
nsresult nsMsgContentPolicy::DisableJSOnMailNewsUrlDocshells(
nsIURI *aContentLocation, nsISupports *aRequestingContext)
{
// XXX if this class changes so that this method can be called from
// ShouldProcess, and if it's possible for this to be null when called from
// ShouldLoad, but not in the corresponding ShouldProcess call,
// we need to re-think the assumptions underlying this code.
// If there's no docshell to get to, there's nowhere for the JavaScript to
// run, so we're already safe and don't need to disable anything.
if (!aRequestingContext) {
return NS_OK;
}
// the policy we're trying to enforce is around the settings for
// message URLs, so if this isn't one of those, bail out
nsresult rv;
nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(aContentLocation, &rv);
if (NS_FAILED(rv)) {
return NS_OK;
}
// since NS_CP_GetDocShellFromContext returns the containing docshell rather
// than the contained one we need, we can't use that here, so...
nsCOMPtr<nsIFrameLoaderOwner> flOwner = do_QueryInterface(aRequestingContext,
&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFrameLoader> frameLoader;
rv = flOwner->GetFrameLoader(getter_AddRefs(frameLoader));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(frameLoader, NS_ERROR_INVALID_POINTER);
nsCOMPtr<nsIDocShell> shell;
rv = frameLoader->GetDocShell(getter_AddRefs(shell));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocShellTreeItem> docshellTreeItem(do_QueryInterface(shell, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// what sort of docshell is this?
PRInt32 itemType;
rv = docshellTreeItem->GetItemType(&itemType);
NS_ENSURE_SUCCESS(rv, rv);
// we're only worried about policy settings in content docshells
if (itemType != nsIDocShellTreeItem::typeContent) {
return NS_OK;
}
return shell->SetAllowJavascript(PR_FALSE);
}
/**
* helper routine to get the root docshell for the window requesting the load
*/
@ -586,6 +678,11 @@ nsMsgContentPolicy::ShouldProcess(PRUint32 aContentType,
nsISupports *aExtra,
PRInt16 *aDecision)
{
// XXX Returning ACCEPT is presumably only a reasonable thing to do if we
// think that ShouldLoad is going to catch all possible cases (i.e. that
// everything we use to make decisions is going to be available at
// ShouldLoad time, and not only become available in time for ShouldProcess).
// Do we think that's actually the case?
*aDecision = nsIContentPolicy::ACCEPT;
return NS_OK;
}
@ -608,6 +705,74 @@ NS_IMETHODIMP nsMsgContentPolicy::Observe(nsISupports *aSubject, const char *aTo
return NS_OK;
}
/**
* We implement the nsIWebProgressListener interface in order to enforce
* settings at onLocationChange time.
*/
NS_IMETHODIMP
nsMsgContentPolicy::OnStateChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRUint32 aStateFlags,
nsresult aStatus)
{
return NS_OK;
}
NS_IMETHODIMP
nsMsgContentPolicy::OnProgressChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest,
PRInt32 aCurSelfProgress,
PRInt32 aMaxSelfProgress,
PRInt32 aCurTotalProgress,
PRInt32 aMaxTotalProgress)
{
return NS_OK;
}
NS_IMETHODIMP
nsMsgContentPolicy::OnLocationChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, nsIURI *aLocation)
{
nsresult rv;
// If anything goes wrong and/or there's no docshell associated with this
// request, just give up. The behavior ends up being "don't consider
// re-enabling JS on the docshell", which is the safe thing to do (and if
// the problem was that there's no docshell, that means that there was
// nowhere for any JavaScript to run, so we're already safe
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aWebProgress, &rv);
if (NS_FAILED(rv)) {
return NS_OK;
}
#ifdef DEBUG
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest, &rv);
nsCOMPtr<nsIDocShell> docShell2;
NS_QueryNotificationCallbacks(channel, docShell2);
NS_ASSERTION(docShell == docShell2, "aWebProgress and channel callbacks"
" do not point to the same docshell");
#endif
// If this is a mailnews url, turn off JavaScript, otherwise turn it on
nsCOMPtr<nsIMsgMessageUrl> messageUrl = do_QueryInterface(aLocation, &rv);
return docShell->SetAllowJavascript(NS_FAILED(rv));
}
NS_IMETHODIMP
nsMsgContentPolicy::OnStatusChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, nsresult aStatus,
const PRUnichar *aMessage)
{
return NS_OK;
}
NS_IMETHODIMP
nsMsgContentPolicy::OnSecurityChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRUint32 aState)
{
return NS_OK;
}
#ifdef MOZ_THUNDERBIRD
NS_IMPL_ISUPPORTS1(nsMsgCookiePolicy, nsICookiePermission)

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

@ -21,6 +21,7 @@
*
* Contributor(s):
* Scott MacGregor <scott@scott-macgregor.org>
* Dan Mosedale <dmose@mozillamessaging.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -52,6 +53,7 @@
#include "nsString.h"
#include "nsICookiePermission.h"
#include "nsIWebProgressListener.h"
/* DBFCFDF0-4489-4faa-8122-190FD1EFA16C */
#define NS_MSGCONTENTPOLICY_CID \
@ -64,6 +66,7 @@ class nsIDocShell;
class nsMsgContentPolicy : public nsIContentPolicy,
public nsIObserver,
public nsIWebProgressListener,
public nsSupportsWeakReference
{
public:
@ -75,7 +78,8 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPOLICY
NS_DECL_NSIOBSERVER
NS_DECL_NSIWEBPROGRESSLISTENER
protected:
PRBool mBlockRemoteImages;
PRBool mAllowPlugins;
@ -90,6 +94,8 @@ protected:
nsresult GetRootDocShellForContext(nsISupports * aRequestingContext, nsIDocShell ** aDocShell);
nsresult GetMessagePaneURI(nsIDocShell * aRootDocShell, nsIURI ** aURI);
nsresult DisableJSOnMailNewsUrlDocshells(nsIURI *aContentLocation,
nsISupports *aRequestingContext);
};
#ifdef MOZ_THUNDERBIRD

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

@ -63,6 +63,7 @@
#include "nsMsgI18N.h"
#include "nsIWebNavigation.h"
#include "nsISupportsObsolete.h"
#include "nsMsgContentPolicy.h"
// used to dispatch urls to default protocol handlers
#include "nsCExternalHandlerService.h"
@ -145,7 +146,7 @@ NS_IMETHODIMP nsMsgWindow::CloseWindow()
nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(rootShell));
if (listener)
listener->SetParentContentListener(nsnull);
mRootDocShellWeak = nsnull;
SetRootDocShell(nsnull);
mMessageWindowDocShellWeak = nsnull;
}
@ -240,6 +241,23 @@ NS_IMETHODIMP nsMsgWindow::GetRootDocShell(nsIDocShell * *aDocShell)
NS_IMETHODIMP nsMsgWindow::SetRootDocShell(nsIDocShell * aDocShell)
{
nsresult rv;
nsCOMPtr<nsIWebProgressListener> contentPolicyListener =
do_GetService(NS_MSGCONTENTPOLICY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// remove the content policy webProgressListener from the root doc shell
// we're currently holding, so we don't keep listening for loads that
// we don't care about
if (mRootDocShellWeak) {
nsCOMPtr<nsIWebProgress> oldWebProgress =
do_QueryReferent(mRootDocShellWeak, &rv);
if (NS_SUCCEEDED(rv)) {
rv = oldWebProgress->RemoveProgressListener(contentPolicyListener);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to remove old progress listener");
}
}
// Query for the doc shell and release it
mRootDocShellWeak = nsnull;
if (aDocShell)
@ -248,6 +266,16 @@ NS_IMETHODIMP nsMsgWindow::SetRootDocShell(nsIDocShell * aDocShell)
nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aDocShell));
if (listener)
listener->SetParentContentListener(this);
// set the contentPolicy webProgressListener on the root docshell for this
// window so that it can allow JavaScript for non-message content
nsCOMPtr<nsIWebProgress> docShellProgress =
do_QueryInterface(aDocShell, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = docShellProgress->AddProgressListener(contentPolicyListener,
nsIWebProgress::NOTIFY_LOCATION);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}