block document loads for URIs tagged as malware by the url-classifier service. b=384941, r=bzbarsky, r=tony, sr=biesi, ui-r=johnath, a=bzbarsky
authordcamp@mozilla.com
Fri, 31 Aug 2007 16:18:46 -0700
changeset 5528 213572e337631479746e2df43d118b6145bf2124
parent 5527 7017ec0e1bea3cfab2bee2b3c1cce2330c2f40b5
child 5529 3a6a11c480c211028600b86a5fff0c7e2d0dd8a2
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky, tony, biesi, johnath, bzbarsky
bugs384941
milestone1.9a8pre
block document loads for URIs tagged as malware by the url-classifier service. b=384941, r=bzbarsky, r=tony, sr=biesi, ui-r=johnath, a=bzbarsky
browser/locales/en-US/chrome/overrides/appstrings.properties
docshell/base/Makefile.in
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIURIClassifier.idl
docshell/base/nsWebShell.cpp
docshell/build/nsDocShellCID.h
docshell/resources/content/netError.xhtml
dom/locales/en-US/chrome/appstrings.properties
toolkit/components/build/nsToolkitCompsModule.cpp
toolkit/components/url-classifier/src/Makefile.in
toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/src/nsUrlClassifierDBService.h
uriloader/base/nsURILoader.h
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties
+++ b/browser/locales/en-US/chrome/overrides/appstrings.properties
@@ -54,8 +54,9 @@ proxyResolveFailure=Firefox is configure
 proxyConnectFailure=Firefox is configured to use a proxy server that is refusing connections.
 contentEncodingError=The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression. Please contact the website owners to inform them of this problem.
 externalProtocolTitle=External Protocol Request
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
+malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -108,16 +108,17 @@ XPIDLSRCS	= \
 		nsIWebNavigation.idl		\
 		nsIWebNavigationInfo.idl        \
 		nsIContentViewer.idl		\
 		nsIContentViewerEdit.idl	\
 		nsIContentViewerFile.idl	\
 		nsIURIFixup.idl			\
 		nsIEditorDocShell.idl		\
 		nsIWebPageDescriptor.idl	\
+		nsIURIClassifier.idl		\
 		$(NULL)
 
 EXPORTS		= nsDocShellLoadTypes.h
 
 CPPSRCS		= \
 		nsDocShell.cpp		\
 		nsWebShell.cpp		\
 		nsDocShellLoadInfo.cpp		\
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -53,16 +53,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIDocumentViewer.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsCURILoader.h"
+#include "nsURILoader.h"
 #include "nsDocShellCID.h"
 #include "nsLayoutCID.h"
 #include "nsDOMCID.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsNetUtil.h"
 #include "nsRect.h"
 #include "prprf.h"
 #include "nsIMarkupDocumentViewer.h"
@@ -100,16 +101,17 @@
 #include "nsIAppShell.h"
 #include "nsWidgetsCID.h"
 #include "nsDOMJSUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIScrollableView.h"
 #include "nsIScriptChannel.h"
+#include "nsIURIClassifier.h"
 
 // we want to explore making the document own the load group
 // so we can associate the document URI with the load group.
 // until this point, we have an evil hack:
 #include "nsIHttpChannelInternal.h"  
 
 
 // Local Includes
@@ -2835,16 +2837,17 @@ nsDocShell::DisplayLoadError(nsresult aE
     NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
 
     nsAutoString error;
     const PRUint32 kMaxFormatStrArgs = 2;
     nsAutoString formatStrs[kMaxFormatStrArgs];
     PRUint32 formatStrCount = 0;
     nsresult rv = NS_OK;
     nsAutoString messageStr;
+    nsCAutoString cssClass;
 
     // Turn the error code into a human readable error message.
     if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
         NS_ENSURE_ARG_POINTER(aURI);
         // extract the scheme
         nsCAutoString scheme;
         aURI->GetScheme(scheme);
         CopyASCIItoUTF16(scheme, formatStrs[0]);
@@ -2975,16 +2978,25 @@ nsDocShell::DisplayLoadError(nsresult aE
         case NS_ERROR_PROXY_CONNECTION_REFUSED:
             // Proxy connection was refused.
             error.AssignLiteral("proxyConnectFailure");
             break;
         case NS_ERROR_INVALID_CONTENT_ENCODING:
             // Bad Content Encoding.
             error.AssignLiteral("contentEncodingError");
             break;
+        case NS_ERROR_MALWARE_URI:
+            nsCAutoString host;
+            aURI->GetHost(host);
+            CopyUTF8toUTF16(host, formatStrs[0]);
+            formatStrCount = 1;
+
+            error.AssignLiteral("malwareBlocked");
+            cssClass.AssignLiteral("blacklist");
+            break;
         }
     }
 
     // Test if the error should be displayed
     if (error.IsEmpty()) {
         return NS_OK;
     }
 
@@ -3016,17 +3028,17 @@ nsDocShell::DisplayLoadError(nsresult aE
 
     // Display the error as a page or an alert prompt
     NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
     // Note: For now, display an alert instead of an error page if we have no
     // URI object. Missing URI objects are handled badly by session history.
     if (mUseErrorPages && aURI && aFailedChannel) {
         // Display an error page
         LoadErrorPage(aURI, aURL, error.get(), messageStr.get(),
-                      aFailedChannel);
+                      cssClass.get(), aFailedChannel);
     } 
     else
     {
         // The prompter reqires that our private window has a document (or it
         // asserts). Satisfy that assertion now since GetDocument will force
         // creation of one if it hasn't already been created.
         nsCOMPtr<nsPIDOMWindow> pwin(do_QueryInterface(mScriptGlobal));
         if (pwin) {
@@ -3041,16 +3053,17 @@ nsDocShell::DisplayLoadError(nsresult aE
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
                           const PRUnichar *aErrorType,
                           const PRUnichar *aDescription,
+                          const char *aCSSClass,
                           nsIChannel* aFailedChannel)
 {
 #if defined(PR_LOGGING) && defined(DEBUG)
     if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
         nsCAutoString spec;
         aURI->GetSpec(spec);
 
         nsCAutoString chanName;
@@ -3105,31 +3118,37 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
     }
 
     // Create a URL to pass all the error information through to the page.
 
     char *escapedUrl = nsEscape(url.get(), url_Path);
     char *escapedCharset = nsEscape(charset.get(), url_Path);
     char *escapedError = nsEscape(NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path);
     char *escapedDescription = nsEscape(NS_ConvertUTF16toUTF8(aDescription).get(), url_Path);
+    char *escapedCSSClass = nsEscape(aCSSClass, url_Path);
 
     nsCString errorPageUrl("about:neterror?e=");
 
     errorPageUrl.AppendASCII(escapedError);
     errorPageUrl.AppendLiteral("&u=");
     errorPageUrl.AppendASCII(escapedUrl);
+    if (escapedCSSClass && escapedCSSClass[0]) {
+        errorPageUrl.AppendASCII("&s=");
+        errorPageUrl.AppendASCII(escapedCSSClass);
+    }
     errorPageUrl.AppendLiteral("&c=");
     errorPageUrl.AppendASCII(escapedCharset);
     errorPageUrl.AppendLiteral("&d=");
     errorPageUrl.AppendASCII(escapedDescription);
 
     nsMemory::Free(escapedDescription);
     nsMemory::Free(escapedError);
     nsMemory::Free(escapedUrl);
     nsMemory::Free(escapedCharset);
+    nsMemory::Free(escapedCSSClass);
 
     nsCOMPtr<nsIURI> errorPageURI;
     nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return InternalLoad(errorPageURI, nsnull, nsnull, PR_TRUE, nsnull, nsnull,
                         nsnull, nsnull, LOAD_ERROR_PAGE,
                         nsnull, PR_TRUE, nsnull, nsnull);
@@ -3217,16 +3236,21 @@ nsDocShell::Stop(PRUint32 aStopFlags)
         // Suspend any timers that were set for this loader.  We'll clear
         // them out for good in CreateContentViewer.
         if (mRefreshURIList) {
             SuspendRefreshURIs();
             mSavedRefreshURIList.swap(mRefreshURIList);
             mRefreshURIList = nsnull;
         }
 
+        if (mClassifier) {
+            mClassifier->Cancel();
+            mClassifier = nsnull;
+        }
+
         // XXXbz We could also pass |this| to nsIURILoader::Stop.  That will
         // just call Stop() on us as an nsIDocumentLoader... We need fewer
         // redundant apis!
         Stop();
     }
 
     PRInt32 n;
     PRInt32 count = mChildList.Count();
@@ -4826,16 +4850,27 @@ nsDocShell::OnRedirectStateChange(nsICha
                                   PRUint32 aRedirectFlags,
                                   PRUint32 aStateFlags)
 {
     NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
                  "Calling OnRedirectStateChange when there is no redirect");
     if (!(aStateFlags & STATE_IS_DOCUMENT))
         return; // not a toplevel document
 
+    // If this load is being checked by the URI classifier, we need to
+    // query the classifier again for the new URI.
+    if (mClassifier) {
+        mClassifier->SetChannel(aNewChannel);
+
+        // we call the nsClassifierCallback:Run() from the main loop to
+        // give the channel a chance to AsyncOpen() the channel before
+        // we suspend it.
+        NS_DispatchToCurrentThread(mClassifier);
+    }
+
     nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
     nsresult result = NS_ERROR_NOT_IMPLEMENTED;
     if (history3) {
         // notify global history of this redirect
         result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
                                                aRedirectFlags, !IsFrame());
     }
 
@@ -4875,16 +4910,20 @@ nsDocShell::EndPageLoad(nsIWebProgress *
                         nsIChannel * aChannel, nsresult aStatus)
 {
     //
     // one of many safeguards that prevent death and destruction if
     // someone is so very very rude as to bring this window down
     // during this load handler.
     //
     nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
+
+    // We're done with the URI classifier for this channel
+    mClassifier = nsnull;
+
     //
     // Notify the ContentViewer that the Document has finished loading...
     //
     // This will cause any OnLoad(...) handlers to fire, if it is a HTML
     // document...
     //
     if (!mEODForCurrentDocument && mContentViewer) {
         mIsExecutingOnLoadHandler = PR_TRUE;
@@ -7239,18 +7278,44 @@ nsresult nsDocShell::DoChannelLoad(nsICh
         break;
     }
 
     (void) aChannel->SetLoadFlags(loadFlags);
 
     rv = aURILoader->OpenURI(aChannel,
                              (mLoadType == LOAD_LINK),
                              this);
-    
-    return rv;
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = CheckClassifier(aChannel);
+    if (NS_FAILED(rv)) {
+        aChannel->Cancel(rv);
+        return rv;
+    }
+
+    return NS_OK;
+}
+
+nsresult
+nsDocShell::CheckClassifier(nsIChannel *aChannel)
+{
+    nsRefPtr<nsClassifierCallback> classifier = new nsClassifierCallback();
+    if (!classifier) return NS_ERROR_OUT_OF_MEMORY;
+
+    classifier->SetChannel(aChannel);
+    nsresult rv = classifier->Run();
+    if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) {
+        // no URI classifier, ignore this
+        return NS_OK;
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mClassifier = classifier;
+
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
                            PRUint32 aLoadType, nscoord *cx, nscoord *cy)
 {
     NS_ASSERTION(aURI, "null uri arg");
     NS_ASSERTION(aWasAnchor, "null anchor arg");
@@ -8987,9 +9052,131 @@ nsDocShell::IsAboutBlank(nsIURI* aURI)
     if (NS_FAILED(aURI->SchemeIs("about", &isAbout)) || !isAbout) {
         return PR_FALSE;
     }
     
     nsCAutoString str;
     aURI->GetSpec(str);
     return str.EqualsLiteral("about:blank");
 }
-                                     
+
+//*****************************************************************************
+// nsClassifierCallback
+//*****************************************************************************
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsClassifierCallback,
+                              nsIURIClassifierCallback,
+                              nsIRunnable)
+
+NS_IMETHODIMP
+nsClassifierCallback::Run()
+{
+    if (!mChannel) {
+        return NS_OK;
+    }
+
+    NS_ASSERTION(!mSuspendedChannel,
+                 "nsClassifierCallback::Run() called while a "
+                 "channel is still suspended.");
+
+    nsCOMPtr<nsIChannel> channel;
+    channel.swap(mChannel);
+
+    // Don't bother to run the classifier on a load that has already failed.
+    // (this might happen after a redirect)
+    PRUint32 status;
+    channel->GetStatus(&status);
+    if (NS_FAILED(status))
+        return NS_OK;
+
+    // Don't bother to run the classifier on a load that's coming from the
+    // cache and doesn't need validaton.
+    nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(channel);
+    if (cachingChannel) {
+        PRBool fromCache;
+        if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) &&
+            fromCache) {
+            return NS_OK;
+        }
+    }
+
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = channel->GetURI(getter_AddRefs(uri));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Don't bother checking certain types of URIs.
+    PRBool hasFlags;
+    rv = NS_URIChainHasFlags(uri,
+                             nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
+                             &hasFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (hasFlags) return NS_OK;
+
+    rv = NS_URIChainHasFlags(uri,
+                             nsIProtocolHandler::URI_IS_LOCAL_FILE,
+                             &hasFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (hasFlags) return NS_OK;
+
+    rv = NS_URIChainHasFlags(uri,
+                             nsIProtocolHandler::URI_IS_UI_RESOURCE,
+                             &hasFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (hasFlags) return NS_OK;
+
+    nsCOMPtr<nsIURIClassifier> uriClassifier =
+        do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    PRBool expectCallback;
+    rv = uriClassifier->Classify(uri, this, &expectCallback);
+    if (NS_FAILED(rv)) return rv;
+
+    if (expectCallback) {
+        // Suspend the channel, it will be resumed when we get the classifier
+        // callback.
+        rv = channel->Suspend();
+        NS_ENSURE_SUCCESS(rv, rv);
+        mSuspendedChannel = channel;
+
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsClassifierCallback[%p]: suspended channel %p",
+                this, mSuspendedChannel.get()));
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClassifierCallback::OnClassifyComplete(nsresult aErrorCode)
+{
+    if (mSuspendedChannel) {
+        if (NS_FAILED(aErrorCode)) {
+            PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+                   ("nsClassifierCallback[%p]: cancelling channel %p with error code: %d",
+                    this, mSuspendedChannel.get(), aErrorCode));
+            mSuspendedChannel->Cancel(aErrorCode);
+        }
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsClassifierCallback[%p]: resuming channel %p from OnClassifyComplete",
+                this, mSuspendedChannel.get()));
+        mSuspendedChannel->Resume();
+        mSuspendedChannel = nsnull;
+    }
+
+    return NS_OK;
+}
+
+void
+nsClassifierCallback::Cancel()
+{
+    if (mSuspendedChannel) {
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsClassifierCallback[%p]: resuming channel %p from Cancel()",
+                this, mSuspendedChannel.get()));
+        mSuspendedChannel->Resume();
+        mSuspendedChannel = nsnull;
+    }
+
+    if (mChannel) {
+        mChannel = nsnull;
+    }
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -100,16 +100,17 @@
 #include "nsIWebBrowserFind.h"
 #include "nsIHttpChannel.h"
 #include "nsDocShellTransferableHooks.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsISecureBrowserUI.h"
 #include "nsIObserver.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsPIDOMEventTarget.h"
+#include "nsIURIClassifier.h"
 
 class nsIScrollableView;
 
 /* load commands were moved to nsIDocShell.h */
 /* load types were moved to nsDocShellLoadTypes.h */
 
 /* internally used ViewMode types */
 enum ViewMode {
@@ -136,16 +137,36 @@ public:
     PRInt32               mDelay;
     PRPackedBool          mRepeat;
     PRPackedBool          mMetaRefresh;
     
 protected:
     virtual ~nsRefreshTimer();
 };
 
+class nsClassifierCallback : public nsIURIClassifierCallback
+                           , public nsIRunnable
+{
+public:
+    nsClassifierCallback() {}
+    ~nsClassifierCallback() {}
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIURICLASSIFIERCALLBACK
+    NS_DECL_NSIRUNNABLE
+
+    void SetChannel(nsIChannel * aChannel)
+        { mChannel = aChannel; }
+
+    void Cancel();
+private:
+    nsCOMPtr<nsIChannel> mChannel;
+    nsCOMPtr<nsIChannel> mSuspendedChannel;
+};
+
 //*****************************************************************************
 //***    nsDocShell
 //*****************************************************************************
 
 class nsDocShell : public nsDocLoader,
                    public nsIDocShell,
                    public nsIDocShellTreeItem, 
                    public nsIDocShellHistory,
@@ -255,16 +276,22 @@ protected:
                                PRBool firstParty,
                                nsIDocShell ** aDocShell,
                                nsIRequest ** aRequest,
                                PRBool aIsNewWindowTarget);
     NS_IMETHOD AddHeadersToChannel(nsIInputStream * aHeadersData, 
                                   nsIChannel * aChannel);
     virtual nsresult DoChannelLoad(nsIChannel * aChannel,
                                    nsIURILoader * aURILoader);
+
+    // Check the channel load against the URI classifier service (if it
+    // exists).  The channel will be suspended until the classification is
+    // complete.
+    nsresult CheckClassifier(nsIChannel *aChannel);
+
     NS_IMETHOD ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
                               PRUint32 aLoadType, nscoord *cx, nscoord *cy);
 
     // Returns PR_TRUE if would have called FireOnLocationChange,
     // but did not because aFireOnLocationChange was false on entry.
     // In this case it is the caller's responsibility to ensure
     // FireOnLocationChange is called.
     // In all other cases PR_FALSE is returned.
@@ -374,16 +401,17 @@ protected:
     NS_IMETHOD EnsureFind();
     nsresult   RefreshURIFromQueue();
     NS_IMETHOD DisplayLoadError(nsresult aError, nsIURI *aURI,
                                 const PRUnichar *aURL,
                                 nsIChannel* aFailedChannel = nsnull);
     NS_IMETHOD LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
                              const PRUnichar *aPage,
                              const PRUnichar *aDescription,
+                             const char *aCSSClass,
                              nsIChannel* aFailedChannel);
     PRBool IsNavigationAllowed(PRBool aDisplayPrintErrorDialog = PR_TRUE);
     PRBool IsPrintingOrPP(PRBool aDisplayErrorDialog = PR_TRUE);
 
     nsresult SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer);
 
     static  inline  PRUint32
     PRTimeToSeconds(PRTime t_usec)
@@ -607,16 +635,19 @@ protected:
     nsDocShellEditorData*      mEditorData;          // editor data, if any
 
     // Transferable hooks/callbacks
     nsCOMPtr<nsIClipboardDragDropHookList>  mTransferableHookData;
 
     // Secure browser UI object
     nsCOMPtr<nsISecureBrowserUI> mSecurityUI;
 
+    // Suspends/resumes channels based on the URI classifier.
+    nsRefPtr<nsClassifierCallback> mClassifier;
+
     // WEAK REFERENCES BELOW HERE.
     // Note these are intentionally not addrefd.  Doing so will create a cycle.
     // For that reasons don't use nsCOMPtr.
 
     nsIDocShellTreeOwner *     mTreeOwner; // Weak Reference
     nsPIDOMEventTarget *       mChromeEventHandler; //Weak Reference
 
     static nsIURIFixup *sURIFixup;
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsIURIClassifier.idl
@@ -0,0 +1,85 @@
+/* ***** 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 Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsIChannel;
+
+/**
+ * Callback function for nsIURIClassifier lookups.
+ */
+[scriptable, function, uuid(8face46e-0c96-470f-af40-0037dcd797bd)]
+interface nsIURIClassifierCallback : nsISupports
+{
+  /**
+   * Called by the URI classifier service when it is done checking a URI.
+   *
+   * Clients are responsible for associating callback objects with classify()
+   * calls.
+   *
+   * @param aErrorCode
+   *        The error code with which the channel should be cancelled, or
+   *        NS_OK if the load should continue normally.
+   */
+  void onClassifyComplete(in nsresult aErrorCode);
+};
+
+/**
+ * The URI classifier service checks a URI against lists of phishing
+ * and malware sites.
+ */
+[scriptable, uuid(2de5c563-1203-43dd-a212-f5d56d530b6f)]
+interface nsIURIClassifier : nsISupports
+{
+  /**
+   * Classify a URI.
+   *
+   * @param aURI
+   *        The URI that should be checked by the URI classifier.
+   * @param aCallback
+   *        The URI classifier will call this callback when the URI has been
+   *        classified.
+   *
+   * @return <code>false</code> if classification is not necessary.  The
+   *         callback will not be called.
+   *         <code>true</code> if classification will be performed.  The
+   *         callback will be called.
+   */
+  boolean classify(in nsIURI aURI,
+                   in nsIURIClassifierCallback aCallback);
+};
--- a/docshell/base/nsWebShell.cpp
+++ b/docshell/base/nsWebShell.cpp
@@ -80,16 +80,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsCURILoader.h"
+#include "nsURILoader.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsEscape.h"
 #include "nsIPlatformCharset.h"
 #include "nsICharsetConverterManager.h"
 #include "nsISocketTransportService.h"
 #include "nsTextFormatter.h"
 #include "nsPIDOMWindow.h"
 #include "nsPICommandUpdater.h"
@@ -119,16 +120,18 @@
 #include "nsIIDNService.h"
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsITimer.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsContentPolicyUtils.h"
 
+#include "nsIURIClassifier.h"
+
 #ifdef NS_DEBUG
 /**
  * Note: the log module is created during initialization which
  * means that you cannot perform logging before then.
  */
 static PRLogModuleInfo* gLogModule = PR_NewLogModule("webshell");
 #endif
 
@@ -1186,16 +1189,17 @@ nsresult nsWebShell::EndPageLoad(nsIWebP
       DisplayLoadError(aStatus, url, nsnull, channel);
     }
     // Errors to be shown for any frame
     else if (aStatus == NS_ERROR_NET_TIMEOUT ||
              aStatus == NS_ERROR_REDIRECT_LOOP ||
              aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
              aStatus == NS_ERROR_NET_INTERRUPT ||
              aStatus == NS_ERROR_NET_RESET ||
+             aStatus == NS_ERROR_MALWARE_URI ||
              NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
       DisplayLoadError(aStatus, url, nsnull, channel);
     }
     else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
       /* A document that was requested to be fetched *only* from
        * the cache is not in cache. May be this is one of those 
        * postdata results. Throw a  dialog to the user,
        * saying that the page has expired from cache and ask if 
--- a/docshell/build/nsDocShellCID.h
+++ b/docshell/build/nsDocShellCID.h
@@ -45,16 +45,22 @@
  * A contract that can be used to get a service that provides
  * meta-information about nsIWebNavigation objects' capabilities.
  * @implements nsIWebNavigationInfo
  */
 #define NS_WEBNAVIGATION_INFO_CONTRACTID \
     "@mozilla.org/webnavigation-info;1"
 
 /**
+ * Contract ID for a service implementing nsIURIClassifier that identifies
+ * phishing and malware sites.
+ */
+#define NS_URICLASSIFIERSERVICE_CONTRACTID "@mozilla.org/uriclassifierservice"
+
+/**
  * An observer service topic that can be listened to to catch creation
  * of content browsing areas (both toplevel ones and subframes).  The
  * subject of the notification will be the nsIWebNavigation being
  * created.  At this time the additional data wstring is not defined
  * to be anything in particular.
  */
 #define NS_WEBNAVIGATION_CREATE "webnavigation-create"
 
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.xhtml
@@ -62,17 +62,17 @@
 
     <script type="application/x-javascript"><![CDATA[
       // Error url MUST be formatted like this:
       //   moz-neterror:page?e=error&u=url&d=desc
       //
       // or optionally, to specify an alternate CSS class to allow for
       // custom styling and favicon:
       //
-      //   moz-neterror:page?c=classname&e=error&u=url&d=desc
+      //   moz-neterror:page?e=error&u=url&s=classname&d=desc
 
       // Note that this file uses document.documentURI to get
       // the URL (with the format from above). This is because
       // document.location.href gets the current URI off the docshell,
       // which is the URL displayed in the location bar, i.e.
       // the URI that the user attempted to load.
 
       function getErrorCode()
@@ -81,24 +81,23 @@
         var error = url.search(/e\=/);
         var duffUrl = url.search(/\&u\=/);
         return decodeURIComponent(url.slice(error + 2, duffUrl));
       }
 
       function getCSSClass()
       {
         var url = document.documentURI;
-        var classParam = url.search(/c\=/);
-        
-        // c is optional, if classParam == -1 just return nothing
-        if (classParam == -1)
+        var matches = url.match(/s\=([^&]+)\&/);
+        // s is optional, if no match just return nothing
+        if (!matches || matches.length < 2)
           return "";
-          
-        var rest = url.search(/\&/);
-        return decodeURIComponent(url.slice(classParam + 2, rest));
+
+        // parenthetical match is the second entry
+        return decodeURIComponent(matches[1]);
       }
 
       function getDescription()
       {
         var url = document.documentURI;
         var desc = url.search(/d\=/);
 
         // desc == -1 if not found; if so, return an empty string
--- a/dom/locales/en-US/chrome/appstrings.properties
+++ b/dom/locales/en-US/chrome/appstrings.properties
@@ -54,8 +54,9 @@ proxyResolveFailure=The proxy server you
 proxyConnectFailure=The connection was refused when attempting to contact the proxy server you have configured. Please check your proxy settings and try again.
 contentEncodingError=The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression. Please contact the website owners to inform them of this problem.
 externalProtocolTitle=External Protocol Request
 externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
 #LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
+malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -53,16 +53,17 @@
 #endif // MOZ_SUITE
 
 #include "nsTypeAheadFind.h"
 
 #ifdef MOZ_URL_CLASSIFIER
 #include "nsUrlClassifierDBService.h"
 #include "nsUrlClassifierStreamUpdater.h"
 #include "nsUrlClassifierUtils.h"
+#include "nsDocShellCID.h"
 #endif
 
 #ifdef MOZ_FEEDS
 #include "nsScriptableUnescapeHTML.h"
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 
@@ -132,16 +133,20 @@ static const nsModuleComponentInfo compo
     NS_TYPEAHEADFIND_CONTRACTID,
     nsTypeAheadFindConstructor
   },
 #ifdef MOZ_URL_CLASSIFIER
   { "Url Classifier DB Service",
     NS_URLCLASSIFIERDBSERVICE_CID,
     NS_URLCLASSIFIERDBSERVICE_CONTRACTID,
     nsUrlClassifierDBServiceConstructor },
+  { "Url Classifier DB Service",
+    NS_URLCLASSIFIERDBSERVICE_CID,
+    NS_URICLASSIFIERSERVICE_CONTRACTID,
+    nsUrlClassifierDBServiceConstructor },
   { "Url Classifier Stream Updater",
     NS_URLCLASSIFIERSTREAMUPDATER_CID,
     NS_URLCLASSIFIERSTREAMUPDATER_CONTRACTID,
     nsUrlClassifierStreamUpdaterConstructor },
   { "Url Classifier Utils",
     NS_URLCLASSIFIERUTILS_CID,
     NS_URLCLASSIFIERUTILS_CONTRACTID,
     nsUrlClassifierUtilsConstructor },
--- a/toolkit/components/url-classifier/src/Makefile.in
+++ b/toolkit/components/url-classifier/src/Makefile.in
@@ -7,19 +7,21 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = url-classifier
 LIBRARY_NAME = urlclassifier_s
 XPIDL_MODULE = url-classifier
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 
 
-REQUIRES = necko \
+REQUIRES = docshell \
+           necko \
            storage \
            string \
+           uriloader \
            xpcom \
            $(ZLIB_REQUIRES) \
            $(NULL)
 
 CPPSRCS = \
           nsUrlClassifierDBService.cpp \
           nsUrlClassifierStreamUpdater.cpp \
           nsUrlClassifierUtils.cpp \
--- a/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp
@@ -33,33 +33,38 @@
  * 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 ***** */
 
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "mozIStorageService.h"
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "mozStorageHelper.h"
 #include "mozStorageCID.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAutoLock.h"
 #include "nsCRT.h"
 #include "nsICryptoHash.h"
 #include "nsIDirectoryService.h"
 #include "nsIObserverService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefBranch2.h"
+#include "nsIPrefService.h"
 #include "nsIProperties.h"
 #include "nsIProxyObjectManager.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIUrlClassifierUtils.h"
 #include "nsUrlClassifierDBService.h"
+#include "nsURILoader.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsVoidArray.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMStrings.h"
 #include "prlog.h"
@@ -114,16 +119,20 @@ static const PRLogModuleInfo *gUrlClassi
 #define MAX_HOST_COMPONENTS 5
 #define MAX_PATH_COMPONENTS 4
 
 // Updates will fail if fed chunks larger than this
 #define MAX_CHUNK_SIZE (1024 * 1024)
 
 #define KEY_LENGTH 16
 
+// Prefs for implementing nsIURIClassifier to block page loads
+#define CHECK_MALWARE_PREF      "browser.safebrowsing.malware.enabled"
+#define CHECK_MALWARE_DEFAULT   PR_FALSE
+
 // Singleton instance.
 static nsUrlClassifierDBService* sUrlClassifierDBService;
 
 // Thread that we do the updates on.
 static nsIThread* gDbBackgroundThread = nsnull;
 
 // Once we've committed to shutting down, don't do work in the background
 // thread.
@@ -2024,20 +2033,64 @@ nsUrlClassifierDBServiceWorker::MaybeCre
     NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS moz_chunks_id"
                        " ON moz_chunks(chunk_id)"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
 
 // -------------------------------------------------------------------------
+// Helper class for nsIURIClassifier implementation, translates table names
+// to nsIURIClassifier enums.
+
+class nsUrlClassifierClassifyCallback : public nsIUrlClassifierCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIURLCLASSIFIERCALLBACK
+
+  nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c)
+    : mCallback(c)
+    {}
+
+private:
+  nsCOMPtr<nsIURIClassifierCallback> mCallback;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierClassifyCallback,
+                              nsIUrlClassifierCallback);
+
+NS_IMETHODIMP
+nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
+{
+  // XXX: we should probably have the wardens tell the service which table
+  // names match with which classification.  For now the table names give
+  // enough information.
+  nsresult response = NS_OK;
+
+  nsACString::const_iterator begin, end;
+
+  tables.BeginReading(begin);
+  tables.EndReading(end);
+  if (FindInReadable(NS_LITERAL_CSTRING("-malware-"), begin, end)) {
+    response = NS_ERROR_MALWARE_URI;
+  }
+
+  mCallback->OnClassifyComplete(response);
+
+  return NS_OK;
+}
+
+
+// -------------------------------------------------------------------------
 // Proxy class implementation
 
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierDBService,
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsUrlClassifierDBService,
                               nsIUrlClassifierDBService,
+                              nsIURIClassifier,
                               nsIObserver)
 
 /* static */ nsUrlClassifierDBService*
 nsUrlClassifierDBService::GetInstance()
 {
   if (!sUrlClassifierDBService) {
     sUrlClassifierDBService = new nsUrlClassifierDBService();
     if (!sUrlClassifierDBService)
@@ -2053,16 +2106,17 @@ nsUrlClassifierDBService::GetInstance()
     // Already exists, just add a ref
     NS_ADDREF(sUrlClassifierDBService);   // addref the return result
   }
   return sUrlClassifierDBService;
 }
 
 
 nsUrlClassifierDBService::nsUrlClassifierDBService()
+ : mCheckMalware(CHECK_MALWARE_DEFAULT)
 {
 }
 
 nsUrlClassifierDBService::~nsUrlClassifierDBService()
 {
   sUrlClassifierDBService = nsnull;
 }
 
@@ -2083,16 +2137,27 @@ nsUrlClassifierDBService::Init()
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Force PSM to be loaded on the main thread.
   nsCOMPtr<nsICryptoHash> hash =
     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Should we check document loads for malware URIs?
+  nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+  if (prefs) {
+    PRBool tmpbool;
+    rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool);
+    mCheckMalware = NS_SUCCEEDED(rv) ? tmpbool : CHECK_MALWARE_DEFAULT;
+
+    prefs->AddObserver(CHECK_MALWARE_PREF, this, PR_FALSE);
+  }
+
   // Start the background thread.
   rv = NS_NewThread(&gDbBackgroundThread);
   if (NS_FAILED(rv))
     return rv;
 
   mWorker = new nsUrlClassifierDBServiceWorker();
   if (!mWorker)
     return NS_ERROR_OUT_OF_MEMORY;
@@ -2110,38 +2175,68 @@ nsUrlClassifierDBService::Init()
     return NS_ERROR_FAILURE;
 
   observerService->AddObserver(this, "profile-before-change", PR_FALSE);
   observerService->AddObserver(this, "xpcom-shutdown-threads", PR_FALSE);
 
   return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
+nsUrlClassifierDBService::Classify(nsIURI *uri,
+                                   nsIURIClassifierCallback* c,
+                                   PRBool* result)
+{
+  NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+  if (!mCheckMalware) {
+    *result = PR_FALSE;
+    return NS_OK;
+  }
+
+  nsRefPtr<nsUrlClassifierClassifyCallback> callback =
+    new nsUrlClassifierClassifyCallback(c);
+  if (!callback) return NS_ERROR_OUT_OF_MEMORY;
+
+  *result = PR_TRUE;
+  return LookupURI(uri, callback, PR_TRUE);
+}
+
+NS_IMETHODIMP
 nsUrlClassifierDBService::Lookup(const nsACString& spec,
                                  nsIUrlClassifierCallback* c,
                                  PRBool needsProxy)
 {
   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsIURI> uri;
 
   nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uri = NS_GetInnermostURI(uri);
   if (!uri) {
     return NS_ERROR_FAILURE;
   }
 
+  return LookupURI(uri, c, needsProxy);
+}
+
+nsresult
+nsUrlClassifierDBService::LookupURI(nsIURI* uri,
+                                    nsIUrlClassifierCallback* c,
+                                    PRBool needsProxy)
+{
+  NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
   nsCAutoString key;
   // Canonicalize the url
   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
-  rv = utilsService->GetKeyForURI(uri, key);
+  nsresult rv = utilsService->GetKeyForURI(uri, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
   if (needsProxy) {
     // The proxy callback uses the current thread.
     rv = NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
                               NS_GET_IID(nsIUrlClassifierCallback),
                               c,
@@ -2273,34 +2368,49 @@ nsUrlClassifierDBService::CancelStream()
 
   return proxy->CancelStream();
 }
 
 NS_IMETHODIMP
 nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
                                   const PRUnichar *aData)
 {
-  NS_ASSERTION(strcmp(aTopic, "profile-before-change") == 0 ||
-               strcmp(aTopic, "xpcom-shutdown-threads") == 0,
-               "Unexpected observer topic");
-
-  Shutdown();
+  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+    nsresult rv;
+    nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_LITERAL_STRING(CHECK_MALWARE_PREF).Equals(aData)) {
+      PRBool tmpbool;
+      rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool);
+      mCheckMalware = NS_SUCCEEDED(rv) ? tmpbool : CHECK_MALWARE_DEFAULT;
+    }
+  } else if (!strcmp(aTopic, "profile-before-change") ||
+             !strcmp(aTopic, "xpcom-shutdown-threads")) {
+    Shutdown();
+  } else {
+    return NS_ERROR_UNEXPECTED;
+  }
 
   return NS_OK;
 }
 
 // Join the background thread if it exists.
 nsresult
 nsUrlClassifierDBService::Shutdown()
 {
   LOG(("shutting down db service\n"));
 
   if (!gDbBackgroundThread)
     return NS_OK;
 
+  nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (prefs) {
+    prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
+  }
+
   nsresult rv;
   // First close the db connection.
   if (mWorker) {
     nsCOMPtr<nsIUrlClassifierDBServiceWorker> proxy;
     rv = NS_GetProxyForObject(gDbBackgroundThread,
                               NS_GET_IID(nsIUrlClassifierDBServiceWorker),
                               mWorker,
                               NS_PROXY_ASYNC,
--- a/toolkit/components/url-classifier/src/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/src/nsUrlClassifierDBService.h
@@ -40,22 +40,24 @@
 #ifndef nsUrlClassifierDBService_h_
 #define nsUrlClassifierDBService_h_
 
 #include <nsISupportsUtils.h>
 
 #include "nsID.h"
 #include "nsIObserver.h"
 #include "nsIUrlClassifierDBService.h"
+#include "nsIURIClassifier.h"
 
 class nsUrlClassifierDBServiceWorker;
 
 // This is a proxy class that just creates a background thread and delagates
 // calls to the background thread.
 class nsUrlClassifierDBService : public nsIUrlClassifierDBService,
+                                 public nsIURIClassifier,
                                  public nsIObserver
 {
 public:
   // This is thread safe. It throws an exception if the thread is busy.
   nsUrlClassifierDBService();
 
   nsresult Init();
 
@@ -64,29 +66,38 @@ public:
 #ifdef MOZILLA_1_8_BRANCH
   NS_DEFINE_STATIC_IID_ACCESSOR(NS_URLCLASSIFIERDBSERVICE_CID)
 #else
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_URLCLASSIFIERDBSERVICE_CID)
 #endif
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERDBSERVICE
+  NS_DECL_NSIURICLASSIFIER
   NS_DECL_NSIOBSERVER
 
 private:
   // No subclassing
   ~nsUrlClassifierDBService();
 
+  nsresult LookupURI(nsIURI* uri,
+                     nsIUrlClassifierCallback* c,
+                     PRBool needsProxy);
+
   // Disallow copy constructor
   nsUrlClassifierDBService(nsUrlClassifierDBService&);
 
   // Make sure the event queue is intialized before we use it.
   void EnsureThreadStarted();
   
   // Close db connection and join the background thread if it exists. 
   nsresult Shutdown();
   
   nsCOMPtr<nsUrlClassifierDBServiceWorker> mWorker;
+
+  // TRUE if the nsURIClassifier implementation should check for malware
+  // uris on document loads.
+  PRBool mCheckMalware;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
 
 #endif // nsUrlClassifierDBService_h_
--- a/uriloader/base/nsURILoader.h
+++ b/uriloader/base/nsURILoader.h
@@ -87,9 +87,15 @@ protected:
    * NSPR logging.  The module is called "URILoader"
    */
   static PRLogModuleInfo* mLog;
 #endif
   
   friend class nsDocumentOpenInfo;
 };
 
+/**
+ * The load has been cancelled because it was found on a malware blacklist.
+ * XXX: this belongs in an nsDocShellErrors.h file of some sort.
+ */
+#define NS_ERROR_MALWARE_URI   NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_URILOADER, 30)
+
 #endif /* nsURILoader_h__ */