diff --git a/mozilla/browser/base/content/browser-doctype.inc b/mozilla/browser/base/content/browser-doctype.inc
index bd29b3aa160..05daaf8a80a 100644
--- a/mozilla/browser/base/content/browser-doctype.inc
+++ b/mozilla/browser/base/content/browser-doctype.inc
@@ -15,5 +15,9 @@
%placesDTD;
#endif
+#ifdef MOZ_SAFE_BROWSING
+
+%safebrowsingDTD;
+#endif
]>
diff --git a/mozilla/browser/base/content/browser-sets.inc b/mozilla/browser/base/content/browser-sets.inc
index cada7eb1fd3..bb4fc9a58c4 100644
--- a/mozilla/browser/base/content/browser-sets.inc
+++ b/mozilla/browser/base/content/browser-sets.inc
@@ -178,6 +178,9 @@
oncommand="PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_HISTORY);"/>
+#endif
+#ifdef MOZ_SAFE_BROWSING
+#include ../../components/safebrowsing/content/commands.inc
#endif
diff --git a/mozilla/browser/base/content/browser.xul b/mozilla/browser/base/content/browser.xul
index f4caff49b88..de09333ddf2 100644
--- a/mozilla/browser/base/content/browser.xul
+++ b/mozilla/browser/base/content/browser.xul
@@ -52,6 +52,10 @@
#endif
+#ifdef MOZ_SAFE_BROWSING
+
+#endif
+
@@ -63,6 +67,9 @@
+#ifdef MOZ_SAFE_BROWSING
+
+#endif
@@ -486,6 +499,11 @@
/>
+
+#ifdef MOZ_SAFE_BROWSING
+#include ../../components/safebrowsing/content/safebrowsing.inc
+#endif
+
#include ../../../toolkit/components/typeaheadfind/content/findBar.inc
diff --git a/mozilla/browser/base/content/global-scripts.inc b/mozilla/browser/base/content/global-scripts.inc
index 2ee2e8b604e..30e74765698 100644
--- a/mozilla/browser/base/content/global-scripts.inc
+++ b/mozilla/browser/base/content/global-scripts.inc
@@ -52,3 +52,6 @@
+#ifdef MOZ_SAFE_BROWSING
+
+#endif
diff --git a/mozilla/browser/components/Makefile.in b/mozilla/browser/components/Makefile.in
index c6b5b92aca2..efaf9cd625b 100644
--- a/mozilla/browser/components/Makefile.in
+++ b/mozilla/browser/components/Makefile.in
@@ -74,6 +74,10 @@ ifdef MOZ_FEEDS
DIRS += feeds
endif
+ifdef MOZ_SAFE_BROWSING
+DIRS += safebrowsing
+endif
+
DIRS += build
include $(topsrcdir)/config/rules.mk
diff --git a/mozilla/browser/components/places/src/Makefile.in b/mozilla/browser/components/places/src/Makefile.in
index 994eff339f7..546e4e3848a 100644
--- a/mozilla/browser/components/places/src/Makefile.in
+++ b/mozilla/browser/components/places/src/Makefile.in
@@ -73,6 +73,7 @@ REQUIRES = xpcom \
js \
nkcache \
widget \
+ url-classifier \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/../../build
diff --git a/mozilla/browser/components/safebrowsing/Makefile.in b/mozilla/browser/components/safebrowsing/Makefile.in
new file mode 100644
index 00000000000..aacc545ff14
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/Makefile.in
@@ -0,0 +1,52 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# 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 *****
+
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = src
+
+ifdef ENABLE_TESTS
+DIRS += tests
+endif
+
+include $(topsrcdir)/config/rules.mk
diff --git a/mozilla/browser/components/safebrowsing/content/application.js b/mozilla/browser/components/safebrowsing/content/application.js
new file mode 100644
index 00000000000..254453f4b3f
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/application.js
@@ -0,0 +1,93 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// An instance of our application is a PROT_Application object. It
+// basically just populates a few globals and instantiates wardens and
+// the listmanager.
+
+/**
+ * An instance of our application. There should be exactly one of these.
+ *
+ * Note: This object should instantiated only at profile-after-change
+ * or later because the listmanager and the cryptokeymanager need to
+ * read and write data files. Additionally, NSS isn't loaded until
+ * some time around then (Moz bug #321024).
+ *
+ * @constructor
+ */
+function PROT_Application() {
+ this.debugZone= "application";
+
+ // TODO This is truly lame; we definitely want something better
+ function runUnittests() {
+ if (false) {
+
+ G_DebugL("UNITTESTS", "STARTING UNITTESTS");
+ TEST_G_Protocol4Parser();
+ TEST_G_Base64();
+ TEST_G_CryptoHasher();
+ TEST_PROT_EnchashDecrypter();
+ TEST_PROT_TRTable();
+ TEST_PROT_ListManager();
+ TEST_PROT_PhishingWarden();
+ TEST_PROT_TRFetcher();
+ TEST_G_ObjectSafeMap();
+ TEST_PROT_URLCanonicalizer();
+ TEST_G_Preferences();
+ TEST_G_Observer();
+ TEST_G_File();
+ TEST_PROT_WireFormat();
+ // UrlCrypto's test should come before the key manager's
+ TEST_PROT_UrlCrypto();
+ TEST_PROT_UrlCryptoKeyManager();
+ TEST_G_MozVersionNumber();
+ TEST_G_ThisFirefoxVersion();
+ G_DebugL("UNITTESTS", "END UNITTESTS");
+ }
+ };
+
+ runUnittests();
+
+ // expose some classes
+ this.G_TabbedBrowserWatcher = G_TabbedBrowserWatcher;
+ this.PROT_Controller = PROT_Controller;
+ this.PROT_GlobalStore = PROT_GlobalStore;
+ this.PROT_PhishingWarden = PROT_PhishingWarden;
+
+ // expose the object
+ this.wrappedJSObject = this;
+}
diff --git a/mozilla/browser/components/safebrowsing/content/browser-view.js b/mozilla/browser/components/safebrowsing/content/browser-view.js
new file mode 100644
index 00000000000..d2adfa9f1eb
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/browser-view.js
@@ -0,0 +1,615 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+// There is one BrowserView per browser window, and each BrowserView
+// is responsible for keeping track of problems (phishy documents)
+// within that window. The BrowserView is also responsible for
+// figuring out what to do about such problems, for example, whether
+// the tab with a phishy page is currently showing and therefore if we
+// should be showing a warning.
+//
+// The BrowserView receives information from three places:
+//
+// - from the phishing warden. When the phishing warden notices a
+// problem, it queries all browser views to see which one (if any)
+// has the Document that is problematic. It then hands the problem
+// off to the appropriate BrowserView.
+//
+// - from the controller. The controller responds to explicit user
+// actions (tab switches, requests to hide the warning message,
+// etc.) and let's the BrowserView know about any user action
+// having to do with the problems it is tracking.
+//
+// - from the TabbedBrowserWatcher. When the BrowserView is keeping
+// track of a problematic document it listens for interesting
+// events affecting it, for example pagehide (at which point
+// we presumably hide the warning if we're showing it).
+//
+// The BrowserView associates at most one "problem" with each Document
+// in the browser window. It keeps state about which Documents are
+// problematic by storing a "problem queue" on each browser (tab).
+// At most one problematic document per browser (tab) is active
+// at any time. That is, we show the warning for at most one phishy
+// document at any one time. If another phishy doc loads in that tab,
+// it goes onto the end of the queue to be activated only when the
+// currently active document goes away.
+//
+// If we had multiple types of warnings (one for after the page had
+// loaded, one for when the user clicked a link, etc) here's where
+// we'd select the appropate one to use. As it stands, we only have
+// one displayer (an "afterload" displayer). A displayer knows _how_
+// to display a warning, whereas as the BrowserView knows _what_ and
+// _when_.
+//
+// To keep things (relatively) easy to reason about and efficient (the
+// phishwarden could be querying us inside a progresslistener
+// notification, or the controller inside an event handler), we have
+// the following rules:
+//
+// - at most one of a displayer's start() or stop() methods is called
+// in any iteration (if calling two is required, the second is run in
+// the next event loop)
+// - displayers should run their operations synchronously so we don't have
+// to look two places (here and in the displayer) to see what is happening
+// when
+// - displayer actions are run after cleaning up the browser view state
+// in case they have consequences
+//
+// TODO: this could use some redesign, but I don't have time.
+// TODO: the queue needs to be abstracted, but we want another release fast,
+// so I'm not going to touch it for the time being
+// TODO: IDN issues and canonical URLs?
+// TODO: Perhaps we should blur the page before showing a warning in order
+// to prevent stray keystrokes?
+
+/**
+ * The BrowerView is responsible for keeping track of and figuring out
+ * what to do with problems within a single browser window.
+ *
+ * TODO
+ * Unify all browser-related state here. Currently it's split
+ * between two objects, this object and the controller. We could have
+ * this object be solely responsible for UI hide/show decisions, which
+ * would probably make it easier to reason about what's going on.
+ *
+ * TODO
+ * Investigate an alternative model. For example, we could factor out
+ * the problem signaling stuff from the tab/UI logic into a
+ * ProblemRegistry. Attach listeners to new docs/requests as they go
+ * by and have these listeners periodically check in with a
+ * ProblemRegistry to see if they're watching a problematic
+ * doc/request. If so, then have them flag the browser view to be
+ * aware of the problem.
+ *
+ * @constructor
+ * @param tabWatcher Reference to the TabbedBrowserWatcher we'll use to query
+ * for information about active tabs/browsers.
+ * @param doc Reference to the XUL Document (browser window) in which the
+ * tabwatcher is watching
+ */
+function PROT_BrowserView(tabWatcher, doc) {
+ this.debugZone = "browserview";
+ this.tabWatcher_ = tabWatcher;
+ this.doc_ = doc;
+}
+
+/**
+ * See if we have any Documents with a given (problematic) URL that
+ * haven't yet been marked as problems. Called as a subroutine by
+ * tryToHandleProblemRequest().
+ *
+ * @param url String containing the URL to look for
+ *
+ * @returns Reference to an unhandled Document with the problem URL or null
+ */
+PROT_BrowserView.prototype.getFirstUnhandledDocWithURL_ = function(url) {
+ var docs = this.tabWatcher_.getDocumentsFromURL(url);
+ if (!docs.length)
+ return null;
+
+ for (var i = 0; i < docs.length; i++) {
+ var browser = this.tabWatcher_.getBrowserFromDocument(docs[i]);
+ G_Assert(this, !!browser, "Found doc but can't find browser???");
+ var alreadyHandled = this.getProblem_(docs[i], browser);
+
+ if (!alreadyHandled)
+ return docs[i];
+ }
+ return null;
+}
+
+/**
+ * Invoked by the warden to give us the opportunity to handle a
+ * problem. A problem is signaled once per request for a problem
+ * Document and is handled at most once, so there's no issue with us
+ * "losing" a problem due to multiple concurrently loading Documents
+ * with the same URL.
+ *
+ * @param warden Reference to the warden signalling the problem. We'll
+ * need him to instantiate one of his warning displayers
+ *
+ * @param request The nsIRequest that is problematic
+ *
+ * @returns Boolean indicating whether we handled problem
+ */
+PROT_BrowserView.prototype.tryToHandleProblemRequest = function(warden,
+ request) {
+
+ var doc = this.getFirstUnhandledDocWithURL_(request.name);
+ if (doc) {
+ var browser = this.tabWatcher_.getBrowserFromDocument(doc);
+ G_Assert(this, !!browser, "Couldn't get browser from problem doc???");
+ G_Assert(this, !this.getProblem_(doc, browser),
+ "Doc is supposedly unhandled, but has state?");
+
+ this.isProblemDocument_(browser, doc, warden);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * We're sure a particular Document is problematic, so let's instantiate
+ * a dispalyer for it and add it to the problem queue for the browser.
+ *
+ * @param browser Reference to the browser in which the problem doc resides
+ *
+ * @param doc Reference to the problematic document
+ *
+ * @param warden Reference to the warden signalling the problem.
+ */
+PROT_BrowserView.prototype.isProblemDocument_ = function(browser,
+ doc,
+ warden) {
+
+ G_Debug(this, "Document is problem: " + doc.location.href);
+
+ var url = doc.location.href;
+
+ // We only have one type of displayer right now
+ var displayer = new warden.displayers_["afterload"]("Phishing afterload",
+ browser,
+ this.doc_,
+ url);
+
+ // We listen for the problematic document being navigated away from
+ // so we can remove it from the problem queue
+
+ var hideHandler = BindToObject(this.onNavAwayFromProblem_,
+ this,
+ doc,
+ browser);
+ doc.defaultView.addEventListener("pagehide", hideHandler, true);
+
+ // More info than we technically need, but it comes in handy for debugging
+ var problem = {
+ "browser_": browser,
+ "doc_": doc,
+ "displayer_": displayer,
+ "url_": url,
+ "hideHandler_": hideHandler,
+ };
+ var numInQueue = this.queueProblem_(browser, problem);
+
+ // If the queue was empty, schedule us to take something out
+ if (numInQueue == 1)
+ new G_Alarm(BindToObject(this.unqueueNextProblem_, this, browser), 0);
+}
+
+/**
+ * Invoked when a problematic document is navigated away from.
+ *
+ * @param doc Reference to the problematic Document navigated away from
+
+ * @param browser Reference to the browser in which the problem document
+ * unloaded
+ */
+PROT_BrowserView.prototype.onNavAwayFromProblem_ = function(doc, browser) {
+
+ G_Debug(this, "User nav'd away from problem.");
+ var problem = this.getProblem_(doc, browser);
+ (new PROT_Reporter).report("phishnavaway", problem.url_);
+
+ G_Assert(this, doc === problem.doc_, "State doc not equal to nav away doc?");
+ G_Assert(this, browser === problem.browser_,
+ "State browser not equal to nav away browser?");
+
+ this.problemResolved(browser, doc);
+}
+
+/**
+ * @param browser Reference to a browser we'd like to know about
+ *
+ * @returns Boolean indicating if the browser in question has
+ * problematic content
+ */
+PROT_BrowserView.prototype.hasProblem = function(browser) {
+ return this.hasNonemptyProblemQueue_(browser);
+}
+
+/**
+ * @param browser Reference to a browser we'd like to know about
+ *
+ * @returns Boolean indicating if the browser in question has a
+ * problem (i.e., it has a non-empty problem queue)
+ */
+PROT_BrowserView.prototype.hasNonemptyProblemQueue_ = function(browser) {
+ try {
+ return !!browser.PROT_problemState__ &&
+ !!browser.PROT_problemState__.length;
+ } catch(e) {
+ // We could be checking a browser that has just been closed, in
+ // which case its properties will not be valid, causing the above
+ // statement to throw an error. Since this case handled elsewhere,
+ // just return false.
+ return false;
+ }
+}
+
+/**
+ * Invoked to indicate that the problem for a particular problematic
+ * document in a browser has been resolved (e.g., by being navigated
+ * away from).
+ *
+ * @param browser Reference to the browser in which resolution is happening
+ *
+ * @param opt_doc Reference to the problematic doc whose problem was resolved
+ * (if absent, assumes the doc assocaited with the currently
+ * active displayer)
+ */
+PROT_BrowserView.prototype.problemResolved = function(browser, opt_doc) {
+ var problem;
+ var doc;
+ if (!!opt_doc) {
+ doc = opt_doc;
+ problem = this.getProblem_(doc, browser);
+ } else {
+ problem = this.getCurrentProblem_(browser);
+ doc = problem.doc_;
+ }
+
+ problem.displayer_.done();
+ var wasHead = this.deleteProblemFromQueue_(doc, browser);
+
+ // Peek at the next problem (if any) in the queue for this browser
+ var queueNotEmpty = this.getCurrentProblem_(browser);
+
+ if (wasHead && queueNotEmpty) {
+ G_Debug(this, "More problems pending. Scheduling unqueue.");
+ new G_Alarm(BindToObject(this.unqueueNextProblem_, this, browser), 0);
+ }
+}
+
+/**
+ * Peek at the top of the problem queue and if there's something there,
+ * make it active.
+ *
+ * @param browser Reference to the browser we should activate a problem
+ * displayer in if one is available
+ */
+PROT_BrowserView.prototype.unqueueNextProblem_ = function(browser) {
+ var problem = this.getCurrentProblem_(browser);
+ if (!problem) {
+ G_Debug(this, "No problem in queue; doc nav'd away from? (shrug)");
+ return;
+ }
+
+ // Two problem docs that load in rapid succession could both schedule
+ // themselves to be unqueued before this method is called. So ensure
+ // that the problem at the head of the queue is not, in fact, active.
+ if (!problem.displayer_.isActive()) {
+
+ // It could be the case that the server is really slow to respond,
+ // so there might not yet be anything in the problem Document. If
+ // we show the warning when that's the case, the user will see a
+ // blank document greyed out, and if they cancel the dialog
+ // they'll see the page they're navigating away from because it
+ // hasn't been painted over yet (b/c there's no content for the
+ // problem page). So here we ensure that we have content for the
+ // problem page before showing the dialog.
+ var haveContent = false;
+ try {
+ // This will throw if there's no content yet
+ var h = problem.doc_.defaultView.getComputedStyle(problem.doc_.body, "")
+ .getPropertyValue("height");
+ G_Debug(this, "body height: " + h);
+
+ if (Number(h.substring(0, h.length - 2)))
+ haveContent = true;
+
+ } catch (e) {
+ G_Debug(this, "Masked in unqueuenextproblem: " + e);
+ }
+
+ if (!haveContent) {
+
+ G_Debug(this, "Didn't get computed style. Re-queueing.");
+
+ // One stuck problem document in a page shouldn't prevent us
+ // warning on other problem frames that might be loading or
+ // loaded. So stick the Document that doesn't have content
+ // back at the end of the queue.
+ var p = this.removeProblemFromQueue_(problem.doc_, browser);
+ G_Assert(this, p === problem, "Unqueued wrong problem?");
+ this.queueProblem_(browser, problem);
+
+ // Try again in a bit. This opens us up to a potential
+ // vulnerability (put tons of hanging frames in a page
+ // ahead of your real phishy frame), but the risk at the
+ // moment is really low (plus it is outside our threat
+ // model).
+ new G_Alarm(BindToObject(this.unqueueNextProblem_,
+ this,
+ browser),
+ 200 /*ms*/);
+ return;
+ }
+
+ problem.displayer_.start();
+
+ // OK, we have content, but there there is an additional
+ // issue. Due to a bfcache bug, if we show the warning during
+ // paint suppression, the collapsing of the content pane affects
+ // the doc we're naving from :( The symptom is a page with grey
+ // screen on navigation to or from a phishing page (the
+ // contentDocument will have width zero).
+ //
+ // Paint supression lasts at most 250ms from when the parser sees
+ // the body, and the parser sees the body well before it has a
+ // height. We err on the side of caution.
+ //
+ // Thanks to bryner for helping to track the bfcache bug down.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=319646
+
+ if (this.tabWatcher_.getCurrentBrowser() === browser)
+ new G_Alarm(BindToObject(this.problemBrowserMaybeSelected,
+ this,
+ browser),
+ 350 /*ms*/);
+ }
+}
+
+/**
+ * Helper function that adds a new problem to the queue of problems pending
+ * on this browser.
+ *
+ * @param browser Browser to which we should add state
+ *
+ * @param problem Object (structure, really) encapsulating the problem
+ *
+ * @returns Number indicating the number of items in the queue (and from
+ * which you can infer whether the recently added item was
+ * placed at the head, and hence should be active.
+ */
+PROT_BrowserView.prototype.queueProblem_ = function(browser, problem) {
+ G_Debug(this, "Adding problem state for " + problem.url_);
+
+ if (this.hasNonemptyProblemQueue_(browser))
+ G_Debug(this, "Already has problem state. Queueing this problem...");
+
+ // First problem ever signaled on this browser? Make a new queue!
+ if (browser.PROT_problemState__ == undefined)
+ browser.PROT_problemState__ = [];
+
+ browser.PROT_problemState__.push(problem);
+ return browser.PROT_problemState__.length;
+}
+
+/**
+ * Helper function that removes a problem from the queue and deactivates
+ * it.
+ *
+ * @param doc Reference to the doc for which we should remove state
+ *
+ * @param browser Reference to the browser from which we should remove
+ * state
+ *
+ * @returns Boolean indicating if the remove problem was currently active
+ * (that is, if it was at the head of the queue)
+ */
+PROT_BrowserView.prototype.deleteProblemFromQueue_ = function(doc, browser) {
+ G_Debug(this, "Deleting problem state for " + browser);
+ G_Assert(this, !!this.hasNonemptyProblemQueue_(browser),
+ "Browser has no problem state");
+
+ var problem = this.getProblem_(doc, browser);
+ G_Assert(this, !!problem, "Couldnt find state in removeproblemstate???");
+
+ var wasHead = browser.PROT_problemState__[0] === problem;
+ this.removeProblemFromQueue_(doc, browser);
+
+ var hideHandler = problem.hideHandler_;
+ G_Assert(this, !!hideHandler, "No hidehandler in state?");
+ problem.doc_.defaultView.removeEventListener("pagehide",
+ hideHandler,
+ true);
+ return wasHead;
+}
+
+/**
+ * Helper function that removes a problem from the queue but does
+ * NOT deactivate it.
+ *
+ * @param doc Reference to the doc for which we should remove state
+ *
+ * @param browser Reference to the browser from which we should remove
+ * state
+ *
+ * @returns Boolean indicating if the remove problem was currently active
+ * (that is, if it was at the head of the queue)
+ */
+PROT_BrowserView.prototype.removeProblemFromQueue_ = function(doc, browser) {
+ G_Debug(this, "Removing problem state for " + browser);
+ G_Assert(this, !!this.hasNonemptyProblemQueue_(browser),
+ "Browser has no problem state");
+
+ var problem = null;
+ // TODO Blech. Let's please have an abstraction here instead.
+ for (var i = 0; i < browser.PROT_problemState__.length; i++)
+ if (browser.PROT_problemState__[i].doc_ === doc) {
+ problem = browser.PROT_problemState__.splice(i, 1)[0];
+ break;
+ }
+ return problem;
+}
+
+/**
+ * Retrieve (but do not remove) the problem state for a particular
+ * problematic Document in this browser
+ *
+ * @param doc Reference to the problematic doc to get state for
+ *
+ * @param browser Reference to the browser from which to get state
+ *
+ * @returns Object encapsulating the state we stored, or null if none
+ */
+PROT_BrowserView.prototype.getProblem_ = function(doc, browser) {
+ if (!this.hasNonemptyProblemQueue_(browser))
+ return null;
+
+ // TODO Blech. Let's please have an abstraction here instead.
+ for (var i = 0; i < browser.PROT_problemState__.length; i++)
+ if (browser.PROT_problemState__[i].doc_ === doc)
+ return browser.PROT_problemState__[i];
+ return null;
+}
+
+/**
+ * Retrieve the problem state for the currently active problem Document
+ * in this browser
+ *
+ * @param browser Reference to the browser from which to get state
+ *
+ * @returns Object encapsulating the state we stored, or null if none
+ */
+PROT_BrowserView.prototype.getCurrentProblem_ = function(browser) {
+ return browser.PROT_problemState__[0];
+}
+
+/**
+ * Invoked by the controller when the user switches tabs away from a problem
+ * tab.
+ *
+ * @param browser Reference to the tab that was switched from
+ */
+PROT_BrowserView.prototype.problemBrowserUnselected = function(browser) {
+ var problem = this.getCurrentProblem_(browser);
+ G_Assert(this, !!problem, "Couldn't get state from browser");
+ problem.displayer_.browserUnselected();
+}
+
+/**
+ * Checks to see if the problem browser is selected, and if so,
+ * tell it it to show its warning.
+ *
+ * @param browser Reference to the browser we wish to check
+ */
+PROT_BrowserView.prototype.problemBrowserMaybeSelected = function(browser) {
+ var problem = this.getCurrentProblem_(browser);
+
+ if (this.tabWatcher_.getCurrentBrowser() === browser &&
+ problem &&
+ problem.displayer_.isActive())
+ this.problemBrowserSelected(browser);
+}
+
+/**
+ * Invoked by the controller when the user switches tabs to a problem tab
+ *
+ * @param browser Reference to the tab that was switched to
+ */
+PROT_BrowserView.prototype.problemBrowserSelected = function(browser) {
+ G_Debug(this, "Problem browser selected");
+ var problem = this.getCurrentProblem_(browser);
+ G_Assert(this, !!problem, "No state? But we're selected!");
+ problem.displayer_.browserSelected();
+}
+
+/**
+ * Invoked by the controller when the user accepts our warning. Passes
+ * the accept through to the message displayer, which knows what to do
+ * (it will be displayer-specific).
+ *
+ * @param browser Reference to the browser for which the user accepted
+ * our warning
+ */
+PROT_BrowserView.prototype.acceptAction = function(browser) {
+ var problem = this.getCurrentProblem_(browser);
+
+ // We run the action only after we're completely through processing
+ // this event. We do this because the action could cause state to be
+ // cleared (e.g., by navigating the problem document) that we need
+ // to finish processing the event.
+
+ new G_Alarm(BindToObject(problem.displayer_.acceptAction,
+ problem.displayer_),
+ 0);
+}
+
+/**
+ * Invoked by the controller when the user declines our
+ * warning. Passes the decline through to the message displayer, which
+ * knows what to do (it will be displayer-specific).
+ *
+ * @param browser Reference to the browser for which the user declined
+ * our warning
+ */
+PROT_BrowserView.prototype.declineAction = function(browser) {
+ var problem = this.getCurrentProblem_(browser);
+ G_Assert(this, !!problem, "User declined but no state???");
+
+ // We run the action only after we're completely through processing
+ // this event. We do this because the action could cause state to be
+ // cleared (e.g., by navigating the problem document) that we need
+ // to finish processing the event.
+
+ new G_Alarm(BindToObject(problem.displayer_.declineAction,
+ problem.displayer_),
+ 0);
+}
+
+/**
+ * The user wants to see the warning message. So let em! At some point when
+ * we have multiple types of warnings, we'll have to mediate them here.
+ *
+ * @param browser Reference to the browser that has the warning the user
+ * wants to see.
+ */
+PROT_BrowserView.prototype.explicitShow = function(browser) {
+ var problem = this.getCurrentProblem_(browser);
+ G_Assert(this, !!problem, "Explicit show on browser w/o problem state???");
+ problem.displayer_.explicitShow();
+}
diff --git a/mozilla/browser/components/safebrowsing/content/commands.inc b/mozilla/browser/components/safebrowsing/content/commands.inc
new file mode 100644
index 00000000000..332825b1913
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/commands.inc
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mozilla/browser/components/safebrowsing/content/controller.js b/mozilla/browser/components/safebrowsing/content/controller.js
new file mode 100644
index 00000000000..48592892dc4
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/controller.js
@@ -0,0 +1,390 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// This is our controller -- the thingy that listens to what the user
+// is doing. There is one controller per browser window, and each has
+// a BrowserView that manages information about problems within the
+// window. The controller figures out when the browser might want to
+// know about something, but the browser view figures out what exactly
+// to do (and the BrowserView's displayer figures out how to do it).
+//
+// For example, the controller might notice that the user has switched
+// to a tab that has something problematic in it. It would tell its
+// BrowserView this, and the BrowserView would figure out whether it
+// is appropriate to show a warning (e.g., perhaps the user previously
+// dismissed the warning for that problem). If so, the BrowserView tells
+// the displayer to show the warning. Anyhoo...
+//
+// TODO Could move all browser-related hide/show logic into the browser
+// view. Need to think about this more.
+
+/**
+ * Handles user actions, translating them into messages to the view
+ *
+ * @constructor
+ * @param win Reference to the Window (browser window context) we should
+ * attach to
+ * @param tabWatcher Reference to the TabbedBrowserWatcher object
+ * the controller should use to receive events about tabs.
+ * @param phishingWarden Reference to the PhishingWarden we should register
+ * our browserview with
+ */
+function PROT_Controller(win, tabWatcher, phishingWarden) {
+ this.debugZone = "controller";
+
+ this.win_ = win;
+ this.phishingWarden_ = phishingWarden;
+
+ // Use this to query preferences
+ this.prefs_ = new G_Preferences();
+
+ // Read state: are we in advanced mode?
+ this.checkRemotePrefName_ = PROT_GlobalStore.getServerCheckEnabledPrefName();
+ this.checkRemote_ = this.prefs_.getPref(this.checkRemotePrefName_, null);
+
+ // Get notifications when the advanced mode preference changes
+ this.checkRemotePrefObserver = BindToObject(this.onCheckRemotePrefChanged,
+ this);
+ this.prefs_.addObserver(this.checkRemotePrefName_,
+ this.checkRemotePrefObserver);
+
+
+ // Global preference to enable the phishing warden
+ this.phishWardenPrefName_ = PROT_GlobalStore.getPhishWardenEnabledPrefName();
+ this.phishWardenEnabled_ = this.prefs_.getPref(this.phishWardenPrefName_,
+ null);
+
+ // Get notifications when the phishing warden enabled pref changes
+ this.phishWardenPrefObserver =
+ BindToObject(this.onPhishWardenEnabledPrefChanged, this);
+ this.prefs_.addObserver(this.phishWardenPrefName_,
+ this.phishWardenPrefObserver);
+
+ // Set us up to receive the events we want.
+ this.tabWatcher_ = tabWatcher;
+ this.onShutdown_ = BindToObject(this.shutdown, this);
+ this.win_.addEventListener("unload", this.onShutdown_, false);
+ this.onTabSwitchCallback_ = BindToObject(this.onTabSwitch, this);
+ this.tabWatcher_.registerListener("tabswitch",
+ this.onTabSwitchCallback_);
+
+
+ // Install our command controllers. These commands are issued from
+ // various places in our UI, including our preferences dialog, the
+ // warning dialog, etc.
+ var commandHandlers = {
+ "safebrowsing-show-warning" :
+ BindToObject(this.onUserShowWarning, this),
+ "safebrowsing-accept-warning" :
+ BindToObject(this.onUserAcceptWarning, this),
+ "safebrowsing-decline-warning" :
+ BindToObject(this.onUserDeclineWarning, this),
+ "safebrowsing-submit-blacklist" :
+ BindToObject(this.onUserSubmitToBlacklist, this),
+ "safebrowsing-submit-generic-phishing" :
+ BindToObject(this.onUserSubmitToGenericPhish, this),
+ "safebrowsing-preferences" :
+ BindToObject(this.onUserPreferences, this),
+ "safebrowsing-test-link" :
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getTestURLs()[0]),
+ "safebrowsing-preferences-home-link":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getHomePageURL()),
+ "safebrowsing-preferences-policy-link":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getPolicyURL()),
+ "safebrowsing-preferences-home-link-nochrome":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getHomePageURL(),
+ true /* chromeless */),
+ "safebrowsing-preferences-policy-link-nochrome":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getPolicyURL(),
+ true /* chromeless */),
+ };
+
+ this.commandController_ = new PROT_CommandController(commandHandlers);
+ this.win_.controllers.appendController(this.commandController_);
+
+ // This guy embodies the logic of when to display warnings
+ // (displayers embody the how).
+ this.browserView_ = new PROT_BrowserView(this.tabWatcher_,
+ this.win_.document);
+
+ // We need to let the phishing warden know about this browser view so it
+ // can be given the opportunity to handle problem documents. We also need
+ // to let the warden know when this window and hence this browser view
+ // is going away.
+ this.phishingWarden_.addBrowserView(this.browserView_);
+
+ this.windowWatcher_ =
+ Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+ .getService(Components.interfaces.nsIWindowWatcher);
+
+ G_Debug(this, "Controller initialized.");
+}
+
+/**
+ * Invoked when the browser window is closing. Do some cleanup.
+ */
+PROT_Controller.prototype.shutdown = function(e) {
+ G_Debug(this, "Browser window closing. Shutting controller down.");
+ if (this.browserView_) {
+ this.phishingWarden_.removeBrowserView(this.browserView_);
+ }
+
+ if (this.commandController_) {
+ this.win_.controllers.removeController(this.commandController_);
+ this.commandController_ = null;
+ }
+
+
+ // No need to drain the browser view's problem queue explicitly; it will
+ // receive pagehides for all the browsers in its queues as they're torn
+ // down, and it will remove them.
+ this.browserView_ = null;
+
+ this.prefs_.removeObserver(this.checkRemotePrefName_,
+ this.checkRemotePrefObserver);
+ this.prefs_.removeObserver(this.phishWardenPrefName_,
+ this.phishWardenPrefObserver);
+ if (this.tabWatcher_) {
+ this.tabWatcher_.removeListener("tabswitch",
+ this.onTabSwitchCallback_);
+ this.tabWatcher_.shutdown();
+ }
+
+ this.win_.removeEventListener("unload", this.onShutdown_, false);
+ this.prefs_ = null;
+
+ this.windowWatcher_ = null;
+
+ G_Debug(this, "Controller shut down.");
+}
+
+/**
+ * Deal with a user changing the pref that says whether we're in advanced
+ * mode (and thus should check the remote server)
+ *
+ * @param prefName Name of the pref holding the value indicating whether
+ * we should check remote server
+ */
+PROT_Controller.prototype.onCheckRemotePrefChanged = function(prefName) {
+ this.checkRemote_ = this.prefs_.getBoolPrefOrDefault(prefName,
+ this.checkRemote_);
+}
+
+/**
+ * Deal with a user changing the pref that says whether we should
+ * enable the phishing warden
+ *
+ * @param prefName Name of the pref holding the value indicating whether
+ * we should enable the phishing warden
+ */
+PROT_Controller.prototype.onPhishWardenEnabledPrefChanged = function(
+ prefName) {
+ this.phishWardenEnabled_ =
+ this.prefs_.getBoolPrefOrDefault(prefName, this.phishWardenEnabled_);
+}
+
+/**
+ * The user clicked the urlbar icon; they want to see the warning message
+ * again.
+ */
+PROT_Controller.prototype.onUserShowWarning = function() {
+ var browser = this.tabWatcher_.getCurrentBrowser();
+ this.browserView_.explicitShow(browser);
+}
+
+/**
+ * Deal with a user wanting preferences
+ */
+PROT_Controller.prototype.onUserPreferences = function() {
+ G_Debug(this, "User wants preferences.");
+ var instantApply = this.prefs_.getPref("browser.preferences.instantApply",
+ false);
+ var features = "chrome,titlebar,toolbar,centerscreen" +
+ (instantApply ? ",dialog=no" : ",modal");
+ var target = this.windowWatcher_.openWindow(
+ this.win_,
+ "chrome://safe-browsing/content/safebrowsing-preferences.xul",
+ "safebrowsingprefsdialog",
+ features,
+ null /* args */);
+
+ return true;
+}
+
+/**
+ * The user clicked on one of the links in the preferences text.
+ * Display the corresponding page in a new window with all the chrome
+ * enabled.
+ *
+ * @param url The URL to display in a new window
+ * @param opt_chromeless Boolean indicating whether to open chromeless
+ */
+PROT_Controller.prototype.showURL_ = function(url, opt_chromeless) {
+ var features = opt_chromeless ? "status,scrollbars=yes,resizable=yes" : null;
+ this.windowWatcher_.openWindow(this.win_,
+ url,
+ "_blank",
+ features,
+ null);
+}
+
+/**
+ * User wants to report a phishing page.
+ *
+ * TODO: pass url as query param. This is ugly.
+ */
+PROT_Controller.prototype.onUserSubmitToBlacklist = function() {
+ var current_window = this.tabWatcher_.getCurrentWindow();
+ G_Debug(this, "User wants to submit to blacklist: " +
+ current_window.location.href);
+
+ var target = this.windowWatcher_.openWindow(
+ this.windowWatcher_.activeWindow /* parent */,
+ PROT_GlobalStore.getSubmitUrl(),
+ "_blank",
+ "height=400em,width=800,scrollbars=yes,resizable=yes," +
+ "menubar,toolbar,location,directories,personalbar,status",
+ null /* args */);
+
+ this.maybeFillInURL_(current_window, target);
+ return true;
+}
+
+/**
+ * User wants to report something phishy, but we don't know if it's a
+ * false positive or negative.
+ *
+ * TODO: pass url as query param. This is ugly.
+ */
+PROT_Controller.prototype.onUserSubmitToGenericPhish = function() {
+ var current_window = this.tabWatcher_.getCurrentWindow();
+ G_Debug(this, "User wants to submit something about: " +
+ current_window.location.href);
+
+ var target = this.windowWatcher_.openWindow(
+ this.windowWatcher_.activeWindow /* parent */,
+ PROT_GlobalStore.getGenericPhishSubmitURL(),
+ "_blank",
+ "height=400em,width=800,scrollbars=yes,resizable=yes," +
+ "menubar,toolbar,location,directories,personalbar,status",
+ null /* args */);
+
+ this.maybeFillInURL_(current_window, target);
+ return true;
+}
+
+/**
+ * A really lame method used by the submission report commands to fill
+ * the current URL into the appropriate form field of submission page.
+ *
+ * TODO: this really needs an overhaul.
+ */
+PROT_Controller.prototype.maybeFillInURL_ = function(current_window, target) {
+ // TODO: merge in patch from perforce
+ return true;
+}
+
+/**
+ * Deal with a user accepting our warning.
+ *
+ * TODO the warning hide/display instructions here can probably be moved
+ * into the browserview in the future, given its knowledge of when the
+ * problem doc hides/shows.
+ */
+PROT_Controller.prototype.onUserAcceptWarning = function() {
+ G_Debug(this, "User accepted warning.");
+ var browser = this.tabWatcher_.getCurrentBrowser();
+ G_Assert(this, !!browser, "Couldn't get current browser?!?");
+ G_Assert(this, this.browserView_.hasProblem(browser),
+ "User accept fired, but browser doesn't have warning showing?!?");
+
+ this.browserView_.acceptAction(browser);
+ this.browserView_.problemResolved(browser);
+}
+
+/**
+ * Deal with a user declining our warning.
+ *
+ * TODO the warning hide/display instructions here can probably be moved
+ * into the browserview in the future, given its knowledge of when the
+ * problem doc hides/shows.
+ */
+PROT_Controller.prototype.onUserDeclineWarning = function() {
+ G_Debug(this, "User declined warning.");
+ var browser = this.tabWatcher_.getCurrentBrowser();
+ G_Assert(this, this.browserView_.hasProblem(browser),
+ "User decline fired, but browser doesn't have warning showing?!?");
+ this.browserView_.declineAction(browser);
+ // We don't call problemResolved() here because all declining does it
+ // hide the message; we still have the urlbar icon showing, giving
+ // the user the ability to bring the warning message back up if they
+ // so desire.
+}
+
+/**
+ * Notice tab switches, and display or hide warnings as appropriate.
+ *
+ * TODO this logic can probably move into the browser view at some
+ * point. But one thing at a time.
+ */
+PROT_Controller.prototype.onTabSwitch = function(e) {
+ if (this.browserView_.hasProblem(e.fromBrowser))
+ this.browserView_.problemBrowserUnselected(e.fromBrowser);
+
+ if (this.browserView_.hasProblem(e.toBrowser))
+ this.browserView_.problemBrowserSelected(e.toBrowser);
+}
+
+/**
+ * Load a URI in the browser
+ *
+ * @param browser Browser in which to load the URI
+ * @param url URL to load
+ */
+PROT_Controller.prototype.loadURI = function(browser, url) {
+ browser.loadURI(url, null, null);
+}
+
+/**
+ * Reload the current page in the given browser
+ *
+ * @param browser Browser which to reload
+ */
+PROT_Controller.prototype.reloadPage = function(browser) {
+ var normalReload = browser.webNavigation.LOAD_FLAGS_NORMAL;
+ browser.reload(normalReload);
+}
diff --git a/mozilla/browser/components/safebrowsing/content/firefox-commands.js b/mozilla/browser/components/safebrowsing/content/firefox-commands.js
new file mode 100644
index 00000000000..876df4dc331
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/firefox-commands.js
@@ -0,0 +1,83 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// Some misc command-related plumbing used by the controller.
+
+
+/**
+ * A tiny wrapper class for super-simple command handlers.
+ *
+ * @param commandHandlerMap An object containing name/value pairs where
+ * the name is command name (string) and value
+ * is the function to execute for that command
+ */
+function PROT_CommandController(commandHandlerMap) {
+ this.debugZone = "commandhandler";
+ this.cmds_ = commandHandlerMap;
+}
+
+/**
+ * @param cmd Command to query support for
+ * @returns Boolean indicating if this controller supports cmd
+ */
+PROT_CommandController.prototype.supportsCommand = function(cmd) {
+ return (cmd in this.cmds_);
+}
+
+/**
+ * Trivial implementation
+ *
+ * @param cmd Command to query status of
+ */
+PROT_CommandController.prototype.isCommandEnabled = function(cmd) {
+ return true;
+}
+
+/**
+ * Execute a command
+ *
+ * @param cmd Command to execute
+ */
+PROT_CommandController.prototype.doCommand = function(cmd) {
+ return this.cmds_[cmd]();
+}
+
+/**
+ * Trivial implementation
+ */
+PROT_CommandController.prototype.onEvent = function(cmd) { }
+
diff --git a/mozilla/toolkit/components/url-classifier/content/globalstore.js b/mozilla/browser/components/safebrowsing/content/globalstore.js
similarity index 93%
rename from mozilla/toolkit/components/url-classifier/content/globalstore.js
rename to mozilla/browser/components/safebrowsing/content/globalstore.js
index e18afd14887..fdb838fee7c 100644
--- a/mozilla/toolkit/components/url-classifier/content/globalstore.js
+++ b/mozilla/browser/components/safebrowsing/content/globalstore.js
@@ -45,6 +45,7 @@
// TODO: These values are actually specific to SafeBrowsing, not url
// classifier, so this file should be moved into
// browser/components/safebrowsing
+// TODO: many of these values should just be moved directly into code.
// TODO: The code needs to fail more gracefully if these values aren't set
// E.g., createInstance should fail for listmanager without these.
@@ -180,8 +181,9 @@ PROT_GlobalStore.getPhishingFaqURL = function() {
* @returns String containing the URL to nav to when the user wants to
* see the test page
*/
-PROT_GlobalStore.getTestURL = function() {
- return PROT_GlobalStore.getPref_("safebrowsing.provider.0.testURL");
+PROT_GlobalStore.getTestURLs = function() {
+ // TODO: return all test urls
+ return [PROT_GlobalStore.getPref_("safebrowsing.provider.0.testURL")];
}
/**
@@ -212,22 +214,3 @@ PROT_GlobalStore.getActionReportURL = function() {
PROT_GlobalStore.getGetKeyURL = function() {
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.keyURL");
}
-
-/**
- * @returns String giving filename to use to store keys
- */
-PROT_GlobalStore.getKeyFilename = function() {
- return "kf.txt";
-}
-
-/**
- * For running unittests.
- * TODO: make this automatic based on build rules
- */
-PROT_GlobalStore.isTesting = function() {
- isTesting = false;
- try {
- isTesting = PROT_GlobalStore.getPref_("safebrowsing.testing")
- } catch (e) {}
- return isTesting;
-}
diff --git a/mozilla/browser/components/safebrowsing/content/js/eventregistrar.js b/mozilla/browser/components/safebrowsing/content/js/eventregistrar.js
new file mode 100644
index 00000000000..2c3ca934417
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/js/eventregistrar.js
@@ -0,0 +1,172 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// This file implements an event registrar, an object with which you
+// can register handlers for arbitrary programmer-defined
+// events. Events are arbitrary strings and listeners are functions
+// taking an object (stuffed with arguments) as a parameter. When you
+// fire an event through the registrar, all listeners are invoked in
+// an unspecified order. The firing function takes an object to be
+// passed into each handler (it is _not_ copied, so be careful). We
+// chose this calling convention so we don't have to change handler
+// signatures when adding new information.
+//
+// Why not just use notifier/observers? Because passing data around
+// with them requires either serialization or a new xpcom interface,
+// both of which are a pain in the ass.
+//
+// Example:
+//
+// // Set up a listener
+// this.handleTabload = function(e) {
+// foo(e.url);
+// bar(e.browser);
+// };
+//
+// // Set up the registrar
+// var eventTypes = ["tabload", "tabunload", "tabswitch"];
+// var registrar = new EventRegistrar(eventTypes);
+// var handler = BindToObject(this.handleTabload, this);
+//
+// // Register a listener
+// registrar.registerListener("tabload", handler);
+//
+// // Fire an event and remove the listener
+// var event = { "url": "http://www", "browser": browser };
+// registrar.fire("tabload", event);
+// registrar.removeListener("tabload", handler);
+//
+// TODO: could add ability to cancel further handlers by having listeners
+// return a boolean
+
+/**
+ * EventRegistrars are used to manage user-defined events.
+ *
+ * @constructor
+ * @param eventTypes {Array or Object} Array holding names of events or
+ * Object holding properties the values of which are
+ * names (strings) for which listeners can register
+ */
+function EventRegistrar(eventTypes) {
+ this.eventTypes = [];
+ this.listeners_ = {}; // Listener sets, index by event type
+
+ if (eventTypes instanceof Array) {
+ var events = eventTypes;
+ } else if (typeof eventTypes == "object") {
+ var events = [];
+ for (var e in eventTypes)
+ events.push(eventTypes[e]);
+ } else {
+ throw new Error("Unrecognized init parameter to EventRegistrar");
+ }
+
+ for (var i = 0; i < events.length; i++) {
+ this.eventTypes.push(events[i]); // Copy in case caller mutates
+ this.listeners_[events[i]] =
+ new ListDictionary(events[i] + "Listeners");
+ }
+}
+
+/**
+ * Indicates whether the given event is one the registrar can handle.
+ *
+ * @param eventType {String} The name of the event to look up
+ * @returns {Boolean} false if the event type is not known or the
+ * event type string itself if it is
+ */
+EventRegistrar.prototype.isKnownEventType = function(eventType) {
+ for (var i=0; i < this.eventTypes.length; i++)
+ if (eventType == this.eventTypes[i])
+ return eventType;
+ return false;
+}
+
+/**
+ * Add an event type to listen for.
+ * @param eventType {String} The name of the event to add
+ */
+EventRegistrar.prototype.addEventType = function(eventType) {
+ if (this.isKnownEventType(eventType))
+ throw new Error("Event type already known: " + eventType);
+
+ this.eventTypes.push(eventType);
+ this.listeners_[eventType] = new ListDictionary(eventType + "Listeners");
+}
+
+/**
+ * Register to receive events of the type passed in.
+ *
+ * @param eventType {String} indicating the event type (one of this.eventTypes)
+ * @param listener {Function} to invoke when the event occurs.
+ */
+EventRegistrar.prototype.registerListener = function(eventType, listener) {
+ if (this.isKnownEventType(eventType) === false)
+ throw new Error("Unknown event type: " + eventType);
+
+ this.listeners_[eventType].addMember(listener);
+}
+
+/**
+ * Unregister a listener.
+ *
+ * @param eventType {String} One of EventRegistrar.eventTypes' members
+ * @param listener {Function} Function to remove as listener
+ */
+EventRegistrar.prototype.removeListener = function(eventType, listener) {
+ if (this.isKnownEventType(eventType) === false)
+ throw new Error("Unknown event type: " + eventType);
+
+ this.listeners_[eventType].removeMember(listener);
+}
+
+/**
+ * Invoke the handlers for the given eventType.
+ *
+ * @param eventType {String} The event to fire
+ * @param e {Object} Object containing the parameters of the event
+ */
+EventRegistrar.prototype.fire = function(eventType, e) {
+ if (this.isKnownEventType(eventType) === false)
+ throw new Error("Unknown event type: " + eventType);
+
+ var invoke = function(listener) {
+ listener(e);
+ };
+
+ this.listeners_[eventType].forEach(invoke);
+}
diff --git a/mozilla/browser/components/safebrowsing/content/js/listdictionary.js b/mozilla/browser/components/safebrowsing/content/js/listdictionary.js
new file mode 100644
index 00000000000..8169c93722c
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/js/listdictionary.js
@@ -0,0 +1,117 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// This file implements a Dictionary data structure using a list
+// (array). We could instead use an object, but using a list enables
+// us to have ordering guarantees for iterators. The interface it exposes
+// is:
+//
+// addMember(item)
+// removeMember(item)
+// isMember(item)
+// forEach(func)
+//
+// TODO: this class isn't really a Dictionary, it's more like a
+// membership set (i.e., a set without union and whatnot). We
+// should probably change the name to avoid confusion.
+
+/**
+ * Create a new Dictionary data structure.
+ *
+ * @constructor
+ * @param name A string used to name the dictionary
+ */
+function ListDictionary(name) {
+ this.name_ = name;
+ this.members_ = [];
+}
+
+/**
+ * Look an item up.
+ *
+ * @param item An item to look up in the dictionary
+ * @returns Boolean indicating if the parameter is a member of the dictionary
+ */
+ListDictionary.prototype.isMember = function(item) {
+ for (var i=0; i < this.members_.length; i++)
+ if (this.members_[i] == item)
+ return true;
+ return false;
+}
+
+/**
+ * Add an item
+ *
+ * @param item An item to add (does not check for dups)
+ */
+ListDictionary.prototype.addMember = function(item) {
+ this.members_.push(item);
+}
+
+/**
+ * Remove an item
+ *
+ * @param item The item to remove (doesn't check for dups)
+ * @returns Boolean indicating if the item was removed
+ */
+ListDictionary.prototype.removeMember = function(item) {
+ for (var i=0; i < this.members_.length; i++) {
+ if (this.members_[i] == item) {
+ for (var j=i; j < this.members_.length; j++)
+ this.members_[j] = this.members_[j+1];
+
+ this.members_.length--;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Apply a function to each of the members. Does NOT replace the members
+ * in the dictionary with results -- it just calls the function on each one.
+ *
+ * @param func Function to apply to the dictionary's members
+ */
+ListDictionary.prototype.forEach = function(func) {
+ if (typeof func != "function")
+ throw new Error("argument to forEach is not a function, it's a(n) " +
+ typeof func);
+
+ for (var i=0; i < this.members_.length; i++)
+ func(this.members_[i]);
+}
diff --git a/mozilla/browser/components/safebrowsing/content/list-warden.js b/mozilla/browser/components/safebrowsing/content/list-warden.js
new file mode 100644
index 00000000000..453c7f1ece4
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/list-warden.js
@@ -0,0 +1,239 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Niels Provos (original author)d
+
+ * Fritz Schneider
+ *
+ * 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 ***** */
+
+// A warden that knows how to register lists with a listmanager and keep them
+// updated if necessary. The ListWarden also provides a simple interface to
+// check if a URL is evil or not. Specialized wardens like the PhishingWarden
+// inherit from it.
+//
+// Classes that inherit from ListWarden are responsible for calling
+// enableTableUpdates or disableTableUpdates. This usually entails
+// registering prefObservers and calling enable or disable in the base
+// class as appropriate.
+//
+
+/**
+ * Abtracts the checking of user/browser actions for signs of
+ * phishing.
+ *
+ * @constructor
+ */
+function PROT_ListWarden() {
+ this.debugZone = "listwarden";
+ var listManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
+ .getService(Ci.nsIUrlListManager);
+ this.listManager_ = listManager;
+
+ // Once we register tables, their respective names will be listed here.
+ this.blackTables_ = [];
+ this.whiteTables_ = [];
+}
+
+/**
+ * Tell the ListManger to keep all of our tables updated
+ */
+
+PROT_ListWarden.prototype.enableBlacklistTableUpdates = function() {
+ for (var i = 0; i < this.blackTables_.length; ++i) {
+ this.listManager_.enableUpdate(this.blackTables_[i]);
+ }
+}
+
+/**
+ * Tell the ListManager to stop updating our tables
+ */
+
+PROT_ListWarden.prototype.disableBlacklistTableUpdates = function() {
+ for (var i = 0; i < this.blackTables_.length; ++i) {
+ this.listManager_.disableUpdate(this.blackTables_[i]);
+ }
+}
+
+/**
+ * Tell the ListManager to update whitelist tables. They may be enabled even
+ * when other updates aren't, for performance reasons.
+ */
+PROT_ListWarden.prototype.enableWhitelistTableUpdates = function() {
+ for (var i = 0; i < this.whiteTables_.length; ++i) {
+ this.listManager_.enableUpdate(this.whiteTables_[i]);
+ }
+}
+
+/**
+ * Tell the ListManager to stop updating whitelist tables.
+ */
+PROT_ListWarden.prototype.disableWhitelistTableUpdates = function() {
+ for (var i = 0; i < this.whiteTables_.length; ++i) {
+ this.listManager_.disableUpdate(this.whiteTables_[i]);
+ }
+}
+
+/**
+ * Register a new black list table with the list manager
+ * @param tableName - name of the table to register
+ * @returns true if the table could be registered, false otherwise
+ */
+
+PROT_ListWarden.prototype.registerBlackTable = function(tableName) {
+ var result = this.listManager_.registerTable(tableName, false);
+ if (result) {
+ this.blackTables_.push(tableName);
+ }
+ return result;
+}
+
+/**
+ * Register a new white list table with the list manager
+ * @param tableName - name of the table to register
+ * @returns true if the table could be registered, false otherwise
+ */
+
+PROT_ListWarden.prototype.registerWhiteTable = function(tableName) {
+ var result = this.listManager_.registerTable(tableName, false);
+ if (result) {
+ this.whiteTables_.push(tableName);
+ }
+ return result;
+}
+
+/**
+ * Internal method that looks up a url in a set of tables
+ *
+ * @param tables array of table names
+ * @param url URL to look up
+ * @returns Boolean indicating if the url is in one of the tables
+ */
+PROT_ListWarden.prototype.isURLInTables_ = function(tables, url) {
+ for (var i = 0; i < tables.length; ++i) {
+ if (this.listManager_.safeExists(tables[i], url))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Internal method that looks up a url in the white list.
+ *
+ * @param url URL to look up
+ * @returns Boolean indicating if the url is on our whitelist
+ */
+PROT_ListWarden.prototype.isWhiteURL_ = function(url) {
+ if (!this.listManager_)
+ return false;
+
+ var whitelisted = this.isURLInTables_(this.whiteTables_, url);
+
+ if (whitelisted)
+ G_Debug(this, "Whitelist hit: " + url);
+
+ return whitelisted;
+}
+
+/**
+ * External method that looks up a url on the whitelist, used by the
+ * content-analyzer.
+ *
+ * @param url The URL to check
+ * @returns Boolean indicating if the url is on the whitelist
+ */
+PROT_ListWarden.prototype.isWhiteURL = function(url) {
+ return this.isWhiteURL_(url);
+}
+
+/**
+ * Internal method that looks up a url in the black lists. Skips the lookup if
+ * the URL is on our whitelist(s).
+ *
+ * @param url URL to look up
+ * @returns Boolean indicating if the url is on our blacklist(s)
+ */
+PROT_ListWarden.prototype.isBlackURL_ = function(url) {
+ if (!this.listManager_)
+ return false;
+
+ var blacklisted = this.isURLInTables_(this.blackTables_, url);
+
+ if (blacklisted)
+ G_Debug(this, "Blacklist hit: " + url);
+
+ return blacklisted;
+}
+
+/**
+ * Internal method that looks up a url in both the white and black lists.
+ *
+ * If there is conflict, the white list has precedence over the black list.
+ *
+ * @param url URL to look up
+ * @returns Boolean indicating if the url is phishy.
+ */
+PROT_ListWarden.prototype.isEvilURL_ = function(url) {
+ return !this.isWhiteURL_(url) && this.isBlackURL_(url);
+}
+
+// Some unittests
+// TODO something more appropriate
+
+function TEST_PROT_ListWarden() {
+ if (G_GDEBUG) {
+ var z = "listwarden UNITTEST";
+ G_debugService.enableZone(z);
+ G_Debug(z, "Starting");
+
+ var threadQueue = new TH_ThreadQueue();
+ var listManager = new PROT_ListManager(threadQueue, true /* testing */);
+
+ var warden = new PROT_ListWarden(listManager);
+
+ // Just some really simple test
+ G_Assert(z, warden.registerWhiteTable("test-white-domain"),
+ "Failed to register test-white-domain table");
+ G_Assert(z, warden.registerWhiteTable("test-black-url"),
+ "Failed to register test-black-url table");
+ listManager.safeInsert("test-white-domain", "http://foo.com/good", "1");
+ listManager.safeInsert("test-black-url", "http://foo.com/good/1", "1");
+ listManager.safeInsert("test-black-url", "http://foo.com/bad/1", "1");
+
+ G_Assert(z, !warden.isEvilURL_("http://foo.com/good/1"),
+ "White listing is not working.");
+ G_Assert(z, warden.isEvilURL_("http://foo.com/bad/1"),
+ "Black listing is not working.");
+
+ G_Debug(z, "PASSED");
+ }
+}
diff --git a/mozilla/browser/components/safebrowsing/content/moz/navwatcher.js b/mozilla/browser/components/safebrowsing/content/moz/navwatcher.js
new file mode 100644
index 00000000000..36851c51b69
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/moz/navwatcher.js
@@ -0,0 +1,272 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// A NavWatcher abstracts away the mechanics of listening for progresslistener-
+// based notifications. You register on it to hear these notifications instead
+// of on a tabbedbrowser or the docloader service.
+//
+// Since it hooks the docloader service, you only need one NavWatcher
+// per application (as opposed to a TabbedBrowserWatcher, of which you need
+// one per tabbedbrowser, meaning one per browser window).
+//
+// The notifications you can register to hear are:
+//
+// EVENT DESCRIPTION
+// ----- -----------
+//
+// docnavstart Fires when the request for a Document has begun. This is
+// as early a notification as you can get (roughly
+// equivalent to STATE_START). As a result, there is no
+// guarantee that when the notification fires the request
+// has been associated with a Document accessible in a
+// browser somewhere; the only guarantee is that there
+// _will_ be, baring some major error. You can handle
+// such error cases gracefully by examining the nsIRequest
+// isPending flag. See phishing-warden.js for an example.
+//
+// For docnavstart the event object you'll receive will have the following
+// properties:
+//
+// request -- reference to the nsIRequest of the new request
+// url -- String containing the URL the request is for (from
+// reuest.name, currently, but might be something better
+// in the future)
+//
+// Example:
+//
+// function handler(e /*event object*/) {
+// foo(e.request);
+// };
+// var watcher = new G_NavWatcher();
+// watcher.registerListener("docnavstart", handler);
+// watcher.removeListener("docnavstart", handler);
+//
+// TODO: should probably make both NavWatcher and TabbedBrowserWatcher
+// subclasses of EventRegistrar
+//
+// TODO: might make a stateful NavWatcher, one that takes care of mapping
+// requests and urls to Documents.
+
+/**
+ * The NavWatcher abstracts listening for progresslistener-based
+ * notifications.
+ *
+ * @param opt_filterSpurious Boolean indicating whether to filter events
+ * for navigations to docs about which you
+ * probably don't care, such as about:blank,
+ * chrome://, and file:// URLs.
+ *
+ * @constructor
+ */
+function G_NavWatcher(opt_filterSpurious) {
+ this.debugZone = "navwatcher";
+ this.filterSpurious_ = !!opt_filterSpurious;
+ this.events = G_NavWatcher.events; // Convenience pointer
+
+ this.registrar_ = new EventRegistrar(this.events);
+
+ var wp = Ci.nsIWebProgress;
+ var wpService = Cc["@mozilla.org/docloaderservice;1"].getService(wp);
+ wpService.addProgressListener(this, wp.NOTIFY_STATE_ALL);
+}
+
+// Events for which listeners can register. Future additions could include
+// things such as docnavstop or navstart (for any navigation).
+G_NavWatcher.events = {
+ DOCNAVSTART: "docnavstart",
+};
+
+/**
+ * We implement nsIWebProgressListener
+ */
+G_NavWatcher.prototype.QueryInterface = function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIWebProgressListener) ||
+ iid.equals(Ci.nsISupportsWeakReference))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+}
+
+/**
+ * Register to receive events of a particular type
+ *
+ * @param eventType String indicating the event (see
+ * G_NavWatcher.events)
+ *
+ * @param listener Function to invoke when the event occurs. See top-
+ * level comments for parameters.
+ */
+G_NavWatcher.prototype.registerListener = function(eventType,
+ listener) {
+ this.registrar_.registerListener(eventType, listener);
+}
+
+/**
+ * Unregister a listener.
+ *
+ * @param eventType String one of G_NavWatcher.events' members
+ *
+ * @param listener Function to remove as listener
+ */
+G_NavWatcher.prototype.removeListener = function(eventType,
+ listener) {
+ this.registrar_.removeListener(eventType, listener);
+}
+
+/**
+ * Send an event to all listeners for that type.
+ *
+ * @param eventType String indicating the event to trigger
+ *
+ * @param e Object to pass to each listener (NOT copied -- be careful)
+ */
+G_NavWatcher.prototype.fire = function(eventType, e) {
+ this.registrar_.fire(eventType, e);
+}
+
+/**
+ * Helper function to determine whether a given URL is "spurious" for some
+ * definition of "spurious".
+ *
+ * @param url String containing the URL to check
+ *
+ * @returns Boolean indicating whether Fritz thinks it's too boring to notice
+ */
+G_NavWatcher.prototype.isSpurious_ = function(url) {
+ return (url == "about:blank" ||
+ url == "about:config" ||
+ url.indexOf("chrome://") == 0 ||
+ url.indexOf("file://") == 0 ||
+ url.indexOf("jar:") == 0);
+}
+
+/**
+ * We do our dirtywork on state changes.
+ */
+G_NavWatcher.prototype.onStateChange = function(webProgress,
+ request,
+ stateFlags,
+ status) {
+
+ var wp = Ci.nsIWebProgressListener;
+
+ // Debugging stuff
+ // function D(msg) {
+ // dump(msg + "\n");
+ // };
+ //
+ // function w(s) {
+ // if (stateFlags & wp[s])
+ // D(s);
+ // };
+ //
+ // D("\nState change:");
+ // try {
+ // D("URL: " + request.name);
+ // } catch (e) {};
+ // w("STATE_IS_REQUEST");
+ // w("STATE_IS_DOCUMENT");
+ // w("STATE_IS_NETWORK");
+ // w("STATE_IS_WINDOW");
+ // w("STATE_STOP");
+ // w("STATE_START");
+ // w("STATE_REDIRECTING");
+ // w("STATE_TRANSFERRING");
+ // w("STATE_NEGOTIATING");
+
+ // Thanks Darin for helping with this
+ if (stateFlags & wp.STATE_START &&
+ stateFlags & wp.STATE_IS_REQUEST &&
+ request.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI) {
+
+ var url;
+ try {
+ url = request.name;
+ } catch(e) { return; }
+
+ if (!this.filterSpurious_ || !this.isSpurious_(url)) {
+
+ G_Debug(this, "firing docnavstart for " + url);
+ var eventObj = {
+ "request": request,
+ "url": url,
+ };
+ this.fire(this.events.DOCNAVSTART, eventObj);
+ }
+ }
+}
+
+// We don't care about the other kinds of updates, but we need to
+// implement the interface anyway.
+
+/**
+ * NOP
+ */
+G_NavWatcher.prototype.onLocationChange = function(webProgress,
+ request,
+ location) { }
+
+/**
+ * NOP
+ */
+G_NavWatcher.prototype.onProgressChange = function(webProgress,
+ request,
+ curSelfProgress,
+ maxSelfProgress,
+ curTotalProgress,
+ maxTotalProgress) { }
+
+/**
+ * NOP
+ */
+G_NavWatcher.prototype.onSecurityChange = function(webProgress,
+ request,
+ state) { }
+
+/**
+ * NOP
+ */
+G_NavWatcher.prototype.onStatusChange = function(webProgress,
+ request,
+ status,
+ message) { }
+
+/**
+ * NOP
+ */
+G_NavWatcher.prototype.onLinkIconAvailable = function(browser, aHref) { }
+
diff --git a/mozilla/browser/components/safebrowsing/content/moz/tabbedbrowserwatcher.js b/mozilla/browser/components/safebrowsing/content/moz/tabbedbrowserwatcher.js
new file mode 100644
index 00000000000..4c6181fd64f
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/moz/tabbedbrowserwatcher.js
@@ -0,0 +1,810 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// This file implements a G_TabbedBrowserWatcher, an object
+// encapsulating and abstracting the mechanics of working with tabs
+// and the documents within them. The watcher provides notification of
+// various DOM-related events (a document loaded, a document unloaded,
+// tab was created/destroyed, user switched tabs, etc.) as well as
+// commonly required methods (get me the current tab, find the tab to
+// which this document belongs, etc.).
+//
+// This class does not do progresslistener-based notifications; for that,
+// use the NavWatcher.
+//
+// Note: I use "browser" and "tab" interchangeably.
+//
+// This class adds a level of indirection to event registration. You
+// initialize it with a tabbedbrowser, and then register to hear
+// events from it instead of from the tabbedbrowser or browser itself.
+// Your handlers are invoked with a custom object as an argument (see
+// below). This object contains useful information such as a reference
+// to the browser in which the event is happening and whether the
+// event is occurring on the top-level document in that browser.
+//
+// The events you can register to hear are:
+//
+// EVENT DESCRIPTION
+// ----- -----------
+// load Fires when a page is shown in the browser window and
+// this page wasn't in the bfcache
+//
+// unload Fires when a page is nav'd away from in the browser window
+// and the page isn't going into the bfcache
+//
+// pageshow Fires when a page is shown in the browser window, whether
+// it came from bfcache or not. (There is a "persisted"
+// property we can get from the event object if we'd like.
+// It indicates whether the page was loaded from bfcache.
+// If false then we known load recently fired).
+//
+// pagehide Fires when a page is nav'd away from in the browser,
+// whether its going into the bfcache or not. (There is
+// a persisted property here as well that we're not
+// propagating -- when its true the page is going into
+// the bfcache, else it's not, and unload will shortly
+// fire).
+//
+// domcontentloaded BROKEN BROKEN BROKEN BROKEN BROKEN BROKEN BROKEN BROKEN
+// Fires when a doc's DOM is ready, but its externally linked
+// content hasn't necessarily loaded. This event is
+// currently broken: it doesn't fire when using the
+// forward/back buttons in conjunction with the bfcache.
+// Bryner is working on a fix.
+//
+// tabload Fires when a new tab has been created (but doesn't
+// necessarily have content loaded in it)
+//
+// tabunload Fires when a tab is being destroyed (and might have had
+// the content it contains destroyed)
+//
+// tabswitch Fires when the user switches tabs
+//
+// tabmove Fires when a user drags a tab to a different position
+//
+//
+// For pageshow, pagehide, load, unload, and domcontentloaded, the event
+// object you'll receive has the following properties:
+//
+// doc -- reference to Document on which the event fired
+// browser -- the browser in which the document resides
+// isTop -- boolean indicating if it is the top-level document
+// inSelected -- boolean indicating if it is in the currently selected tab
+//
+// For tabload and unload it has:
+//
+// browser -- reference to the browser that is loading or closing
+//
+// For tabswitch it has:
+//
+// fromBrowser -- reference to the browser user is switching from
+// toBrowser -- reference to the browser user is switching to
+//
+// For tabmove it has:
+//
+// tab -- the tab that was moved (Note that this is the actual
+// tab widget that holds the document title, not the
+// browser object. We use this because it's the target
+// of the DOMNodeInserted event.)
+// fromIndex -- the tab index before the move
+// toIndex -- the tab index after the move
+//
+//
+// The order of events is: tabload
+// domcontentloaded
+// load
+// pageshow
+// -- --
+// pagehide
+// unload
+// tabunload
+//
+// Example:
+//
+// function handler(e /*event object*/) {
+// foo(e.browser);
+// };
+// var watcher = new G_TabbedBrowserWatcher(document.getElementById(gBrowser));
+// watcher.registerListener("load", handler); // register for events
+// watcher.removeListener("load", handler); // unregister
+//
+//
+// TODO, BUGS, ISSUES, COMPLICATIONS:
+//
+// The only major problem is in closing tabs:
+//
+// + When you close a tab, the reference to the Document you get in the
+// unload event is undefined. We pass this along. This could easily
+// be fixed by not listening for unload at all, but instead inferring
+// it from the information in pagehide, and then firing our own "fake"
+// unload after firing pagehide.
+//
+// + There's no docshell during the pagehide event, so we can't determine
+// if the document is top-level. We pass undefined in this case.
+//
+// + Though the browser reference during tabunload will be valid, its
+// members most likely have already been torn down. Use it in an
+// objectsafemap to keep state if you need its members.
+//
+// + The event listener DOMNodeInserted has the potential to cause
+// performance problems if there are too many events fired. It
+// should be ok, since we inserted it as far as possible into
+// the xul tree.
+//
+//
+// TODO: need better enforcement of unique names. Two tabbedbrowserwatchers
+// with the same name will clobber each other because they use that
+// name to mark browsers they've seen.
+//
+// TODO: the functions that iterate of windows and documents badly need
+// to be made cleaner. Right now we have multiple implementations
+// that essentially do the same thing :(
+//
+// But good enough for government work.
+
+/**
+ * Encapsulates tab-related information. You can use the
+ * G_TabbedBrowserWatcher to watch for events on tabs as well as to
+ * retrieve tab-related data (such as what tab is currently showing).
+ * It receives many event notifications from G_BrowserWatchers it
+ * attaches to newly opening tabs.
+ *
+ * @param tabBrowser A reference to the tabbed browser you wish to watch.
+ *
+ * @param name String containing a probabilistically unique name. Used to
+ * ensure that each tabbedbrowserwatcher can uniquely mark
+ * browser it has "seen."
+ *
+ * @param opt_filterAboutBlank Boolean indicating whether to filter events
+ * for about:blank. These events are often
+ * spurious since about:blank is the default
+ * page for an empty browser.
+ *
+ * @constructor
+ */
+
+function G_TabbedBrowserWatcher(tabBrowser, name, opt_filterAboutBlank) {
+ this.debugZone = "tabbedbrowserwatcher";
+ this.registrar_ = new EventRegistrar(G_TabbedBrowserWatcher.events);
+ this.tabBrowser_ = tabBrowser;
+ this.filterAboutBlank_ = !!opt_filterAboutBlank;
+ this.events = G_TabbedBrowserWatcher.events; // Convenience pointer
+
+ // We need some way to tell if we've seen a browser before, so we
+ // set a property on it with a probabilistically unique string. The
+ // string is a combination of a static string and one passed in by
+ // the caller.
+ G_Assert(this, typeof name == "string" && !!name,
+ "Need a probabilistically unique name");
+ this.name_ = name;
+ this.mark_ = G_TabbedBrowserWatcher.mark_ + "-" + this.name_;
+
+ this.tabbox_ = this.getTabBrowser().mTabBox;
+
+ // We watch for events occuring in previously unseen browsers at
+ // this (the tabbedbrowser) level, and attach listeners to the new
+ // browsers when we see them. In order to do this properly, we need
+ // to watch for the earliest event we're interested in
+ // (DOMContentLoaded), because otherwise we'd miss this event in newly
+ // opening browsers. For example, if instead we hooked load here,
+ // DOMContentLoaded would already have passed by the time we noticed
+ // there was a new browser.
+
+ this.onDOMContentLoadedClosure_ = BindToObject(this.onDOMContentLoaded, this)
+ this.tabbox_.addEventListener("DOMContentLoaded",
+ this.onDOMContentLoadedClosure_, true);
+
+ // We watch for DOM nodes inserted under the tabbox so we can detect when
+ // a user drags a tab to a new location.
+ this.onDOMNodeInsertedClosure_ = BindToObject(this.onDOMNodeInserted, this);
+ this.tabbox_.addEventListener("DOMNodeInserted",
+ this.onDOMNodeInsertedClosure_, true);
+
+ // There's no tabswitch event in Firefox, so we fake it by watching
+ // for selects on the tabbox.
+ this.onTabSwitchClosure_ = BindToObject(this.onTabSwitch, this);
+ this.tabbox_.addEventListener("select",
+ this.onTabSwitchClosure_, true);
+
+ // Used to determine when the user has switched tabs
+ this.lastTab_ = this.getCurrentBrowser();
+
+ // Ensure we hook a G_BrowserWatcher to all tabs that are open at startup
+ this.detectNewTabs_();
+}
+
+// Events for which listeners can register
+G_TabbedBrowserWatcher.events = {
+ DOMCONTENTLOADED: "domcontentloaded",
+ PAGESHOW: "pageshow",
+ PAGEHIDE: "pagehide",
+ LOAD: "load",
+ UNLOAD: "unload",
+ TABLOAD: "tabload",
+ TABUNLOAD: "tabunload",
+ TABSWITCH: "tabswitch",
+ TABMOVE: "tabmove",
+ };
+
+// We mark new tabs as we see them
+G_TabbedBrowserWatcher.mark_ = "watcher-marked";
+
+/**
+ * Remove all the event handlers and clean up circular refs.
+ */
+G_TabbedBrowserWatcher.prototype.shutdown = function() {
+ G_Debug(this, "Removing event listeners");
+ if (this.tabbox_) {
+ this.tabbox_.removeEventListener("DOMContentLoaded",
+ this.onDOMContentLoadedClosure_, true);
+ this.tabbox_.removeEventListener("DOMNodeInserted",
+ this.onDOMNodeInsertedClosure_, true);
+ this.tabbox_.removeEventListener("select",
+ this.onTabSwitchClosure_, true);
+ // Break circular ref so we can be gc'ed.
+ this.tabbox_ = null;
+ }
+ // Break circular ref so we can be gc'ed.
+ if (this.lastTab_) {
+ this.lastTab_ = null;
+ }
+
+ if (this.tabBrowser_) {
+ this.tabBrowser_ = null;
+ }
+}
+
+/**
+ * Check to see if we've seen a browser before
+ *
+ * @param browser Browser to check
+ * @returns Boolean indicating if we've attached a BrowserWatcher to this
+ * browser
+ */
+G_TabbedBrowserWatcher.prototype.isInstrumented_ = function(browser) {
+ return !!browser[this.mark_];
+}
+
+/**
+ * Attaches a BrowserWatcher to a browser and marks it as seen
+ *
+ * @param browser Browser to which to attach a G_BrowserWatcher
+ */
+G_TabbedBrowserWatcher.prototype.instrumentBrowser_ = function(browser) {
+ G_Assert(this, !this.isInstrumented_(browser),
+ "Browser already instrumented!");
+
+ // The browserwatcher will hook itself into the browser and its parent (us)
+ new G_BrowserWatcher(this, browser);
+ browser[this.mark_] = true;
+}
+
+/**
+ * Attach BrowserWatchers to all open, unseen tabs
+ */
+G_TabbedBrowserWatcher.prototype.detectNewTabs_ = function() {
+ var tb = this.getTabBrowser();
+
+ for (var i = 0; i < tb.browsers.length; ++i)
+ this.maybeFireTabLoad(tb.browsers[i]);
+}
+
+/**
+ * Register to receive events of a particular type
+ *
+ * @param eventType String indicating the event (see
+ * G_TabbedBrowserWatcher.events)
+ * @param listener Function to invoke when the event occurs. See top-
+ * level comments for parameters.
+ */
+G_TabbedBrowserWatcher.prototype.registerListener = function(eventType,
+ listener) {
+ this.registrar_.registerListener(eventType, listener);
+}
+
+/**
+ * Unregister a listener.
+ *
+ * @param eventType String one of G_TabbedBrowserWatcher.events' members
+ * @param listener Function to remove as listener
+ */
+G_TabbedBrowserWatcher.prototype.removeListener = function(eventType,
+ listener) {
+ this.registrar_.removeListener(eventType, listener);
+}
+
+/**
+ * Send an event to all listeners for that type.
+ *
+ * @param eventType String indicating the event to trigger
+ * @param e Object to pass to each listener (NOT copied -- be careful)
+ */
+G_TabbedBrowserWatcher.prototype.fire = function(eventType, e) {
+ this.registrar_.fire(eventType, e);
+}
+
+/**
+ * Convenience function to send a document-related event. We use this
+ * convenience function because the event constructing logic and
+ * parameters are the same for all these events. (Document-related
+ * events are load, unload, pagehide, pageshow, and domcontentloaded).
+ *
+ * @param eventType String indicating the type of event to fire (one of
+ * the document-related events)
+ *
+ * @param doc Reference to the HTMLDocument the event is occuring to
+ *
+ * @param browser Reference to the browser in which the document is contained
+ */
+G_TabbedBrowserWatcher.prototype.fireDocEvent_ = function(eventType,
+ doc,
+ browser) {
+ // If we've already shutdown, don't bother firing any events.
+ if (!this.tabBrowser_) {
+ G_Debug(this, "Firing event after shutdown: " + eventType);
+ return;
+ }
+
+ try {
+ // Could be that the browser's contentDocument has already been torn
+ // down. If so, this throws, and we can't tell without keeping more
+ // state whether doc was the top frame.
+ var isTop = (doc == browser.contentDocument);
+ } catch(e) {
+ var isTop = undefined;
+ }
+
+ var inSelected = (browser == this.getCurrentBrowser());
+
+ var location = doc ? doc.location.href : undefined;
+
+ // Only send notifications for about:config's if we're supposed to
+ if (!this.filterAboutBlank_ || location != "about:blank") {
+
+ G_Debug(this, "firing " + eventType + " for " + location +
+ (isTop ? " (isTop)" : "") + (inSelected ? " (inSelected)" : ""));
+ this.fire(eventType, { "doc": doc,
+ "isTop": isTop,
+ "inSelected": inSelected,
+ "browser": browser});
+ }
+}
+
+/**
+ * Invoked on a browser to ensure we've seen it before. If we haven't,
+ * the browser is instrumented and the tabload event is fired.
+ *
+ * @param browser Reference to the browser to check
+ */
+G_TabbedBrowserWatcher.prototype.maybeFireTabLoad = function(browser) {
+ if (!this.isInstrumented_(browser)) { // Is it a new browser?
+ this.instrumentBrowser_(browser); // Add a G_BrowserWatcher
+ G_Debug(this, "firing tabload");
+ // And shoot notification
+ this.fire(this.events.TABLOAD, { "browser": browser });
+ }
+}
+
+/**
+ * Invoked when the document content has loaded for a document. Externally
+ * linked in content might not yet have loaded.
+ *
+ * @param e Event object
+ */
+G_TabbedBrowserWatcher.prototype.onDOMContentLoaded = function(e) {
+ G_Debug(this, "onDOMContentLoaded for a " + e.target);
+
+ var doc = e.target;
+ var browser = this.getBrowserFromDocument(doc);
+
+ if (!browser) {
+ G_Debug(this, "domcontentloaded: no browser for " + doc.location.href);
+ return;
+ }
+
+ this.maybeFireTabLoad(browser);
+ G_Debug(this, "DOMContentLoaded broken for forward/back buttons.");
+ this.fireDocEvent_(this.events.DOMCONTENTLOADED, doc, browser);
+}
+
+/**
+ * Invoked when a new xul node is inserted under the tabbox. We use this
+ * to detect tab moves.
+ *
+ * @param e Event object
+ */
+G_TabbedBrowserWatcher.prototype.onDOMNodeInserted = function(e) {
+ G_Debug(this, "onDOMNodeInserted for a " + e.target +
+ " related: " + e.relatedNode);
+
+ // Ignore the node insertion if it isn't a tab
+ if (e.target.localName != "tab") {
+ return;
+ }
+
+ // If the tab was just inserted (it's a new tab, not a moved tab), the
+ // pos value will be undefined.
+ if (!isDef(e.target._tPos)) {
+ return;
+ }
+
+ // Get the target tab's old position
+ var fromPos = e.target._tPos;
+
+ // Get the target tab's new position.
+ // Would like to avoid a linear search through the tabs but I'm not sure
+ // how to get around this.
+ var toPos;
+ for (var i = 0; i < e.relatedNode.childNodes.length; i++) {
+ var child = e.relatedNode.childNodes[i];
+ if (child == e.target) {
+ toPos = i;
+ break;
+ }
+ }
+
+ G_Debug(this, "firing tabmove");
+ this.fire(this.events.TABMOVE, { "tab": e.target,
+ "fromIndex": fromPos,
+ "toIndex": toPos } );
+}
+
+/**
+ * Invoked when the user might have switched tabs
+ *
+ * @param e Event object
+ */
+G_TabbedBrowserWatcher.prototype.onTabSwitch = function(e) {
+ // Filter spurious events
+ // The event target is usually tabs but can be tabpanels when tabs were opened
+ // programatically via tabbrowser.addTab().
+ if (e.target == null ||
+ (e.target.localName != "tabs" && e.target.localName != "tabpanels"))
+ return;
+
+ var fromBrowser = this.lastTab_;
+ var toBrowser = this.getCurrentBrowser();
+
+ if (fromBrowser != toBrowser) {
+ this.lastTab_ = toBrowser;
+ G_Debug(this, "firing tabswitch");
+ this.fire(this.events.TABSWITCH, { "fromBrowser": fromBrowser,
+ "toBrowser": toBrowser });
+ }
+}
+
+// Utility functions
+
+/**
+ * Returns a reference to the tabbed browser this G_TabbedBrowserWatcher
+ * was initialized with.
+ */
+G_TabbedBrowserWatcher.prototype.getTabBrowser = function() {
+ return this.tabBrowser_;
+}
+
+/**
+ * Returns a reference to the currently selected tab.
+ */
+G_TabbedBrowserWatcher.prototype.getCurrentBrowser = function() {
+ return this.getTabBrowser().selectedBrowser;
+}
+
+/**
+ * Returns a reference to the top window in the currently selected tab.
+ */
+G_TabbedBrowserWatcher.prototype.getCurrentWindow = function() {
+ return this.getCurrentBrowser().contentWindow;
+}
+
+/**
+ * Find the browser corresponding to a Document
+ *
+ * @param doc Document we want the browser for
+ * @returns Reference to the browser in which the given document is found
+ * or null if not found
+ */
+G_TabbedBrowserWatcher.prototype.getBrowserFromDocument = function(doc) {
+ // Could instead get the top window of the browser in which the doc
+ // is found via doc.defaultView.top, but sometimes the document
+ // isn't in a browser at all (it's being unloaded, for example), so
+ // defaultView won't be valid.
+
+ // Helper: return true if doc is a sub-document of win
+ function docInWindow(doc, win) {
+ if (win.document == doc)
+ return true;
+
+ if (win.frames)
+ for (var i = 0; i < win.frames.length; i++)
+ if (docInWindow(doc, win.frames[i]))
+ return true;
+
+ return false;
+ }
+
+ var browsers = this.getTabBrowser().browsers;
+ for (var i = 0; i < browsers.length; i++)
+ if (docInWindow(doc, browsers[i].contentWindow))
+ return browsers[i];
+
+ return null;
+}
+
+/**
+ * Find the Document that has the given URL loaded. Returns on the
+ * _first_ such document found, so be careful.
+ *
+ * TODO make doc/window searches more elegant, and don't use inner functions
+ *
+ * @param url String indicating the URL we're searching for
+ * @param opt_browser Optional reference to a browser. If given, the
+ * search will be confined to only this browser.
+ * @returns Reference to the Document with that URL or null if not found
+ */
+G_TabbedBrowserWatcher.prototype.getDocumentFromURL = function(url,
+ opt_browser) {
+
+ // Helper function: return the Document in win that has location of url
+ function docWithURL(win, url) {
+ if (win.document.location.href == url)
+ return win.document;
+
+ if (win.frames)
+ for (var i = 0; i < win.frames.length; i++) {
+ var rv = docWithURL(win.frames[i], url);
+ if (rv)
+ return rv;
+ }
+
+ return null;
+ }
+
+ if (opt_browser)
+ return docWithURL(opt_browser.contentWindow, url);
+
+ var browsers = this.getTabBrowser().browsers;
+ for (var i=0; i < browsers.length; i++) {
+ var rv = docWithURL(browsers[i].contentWindow, url);
+ if (rv)
+ return rv;
+ }
+
+ return null;
+}
+
+/**
+ * Find the all Documents that have the given URL loaded.
+ *
+ * TODO make doc/window searches more elegant, and don't use inner functions
+ *
+ * @param url String indicating the URL we're searching for
+ *
+ * @param opt_browser Optional reference to a browser. If given, the
+ * search will be confined to only this browser.
+ *
+ * @returns Array of Documents with the given URL (zero length if none found)
+ */
+G_TabbedBrowserWatcher.prototype.getDocumentsFromURL = function(url,
+ opt_browser) {
+
+ var docs = [];
+
+ // Helper function: add Docs in win with the location of url
+ function getDocsWithURL(win, url) {
+ if (win.document.location.href == url)
+ docs.push(win.document);
+
+ if (win.frames)
+ for (var i = 0; i < win.frames.length; i++)
+ getDocsWithURL(win.frames[i], url);
+ }
+
+ if (opt_browser)
+ return getDocsWithURL(opt_browser.contentWindow, url);
+
+ var browsers = this.getTabBrowser().browsers;
+ for (var i=0; i < browsers.length; i++)
+ getDocsWithURL(browsers[i].contentWindow, url);
+
+ return docs;
+}
+
+
+/**
+ * Finds the browser in which a Window resides.
+ *
+ * @param sub Window to find
+ * @returns Reference to the browser in which sub resides, else null
+ * if not found
+ */
+G_TabbedBrowserWatcher.prototype.getBrowserFromWindow = function(sub) {
+
+ // Helpfer function: return true if sub is a sub-window of win
+ function containsSubWindow(sub, win) {
+ if (win == sub)
+ return true;
+
+ if (win.frames)
+ for (var i=0; i < win.frames.length; i++)
+ if (containsSubWindow(sub, win.frames[i]))
+ return true;
+
+ return false;
+ }
+
+ var browsers = this.getTabBrowser().browsers;
+ for (var i=0; i < browsers.length; i++)
+ if (containsSubWindow(sub, browsers[i].contentWindow))
+ return browsers[i];
+
+ return null;
+}
+
+/**
+ * Finds the XUL tag corresponding to a given browser.
+ *
+ * @param tabBrowser Reference to the tabbed browser in which browser lives
+ * @param browser Reference to the browser we wish to find the tab of
+ * @returns Reference to the browser's tab element, or null
+ * @static
+ */
+G_TabbedBrowserWatcher.getTabElementFromBrowser = function(tabBrowser,
+ browser) {
+
+ for (var i=0; i < tabBrowser.browsers.length; i++)
+ if (tabBrowser.browsers[i] == browser)
+ return tabBrowser.mTabContainer.childNodes[i];
+
+ return null;
+}
+
+if (G_GDEBUG) {
+ G_debugService.loggifier.loggify(G_TabbedBrowserWatcher.prototype);
+}
+
+
+/**
+ * The G_TabbedBrowserWatcher delegates watching most events in browsers
+ * to this object. It calls into its parent (the G_TabbedBrowserWatcher)
+ * to signal events and is garbage collected when the browser goes away
+ * because we don't hold a reference to it.
+ *
+ * @constructor
+ * @param tabbedBrowserWatcher The high-level watcher through which we
+ * should send notifications
+ * @param browser The browser to which we should attach
+ */
+function G_BrowserWatcher(tabbedBrowserWatcher, browser) {
+ this.debugZone = "browserwatcher";
+ this.parent_ = tabbedBrowserWatcher;
+ this.browser_ = browser;
+
+ G_Debug(this, "new G_BrowserWatcher");
+ // Now register to hear most of the doc-related events
+ this.onPageShowClosure_ = BindToObject(this.onPageShow, this);
+ this.browser_.addEventListener("pageshow", this.onPageShowClosure_, true);
+
+ this.onPageHideClosure_ = BindToObject(this.onPageHide, this);
+ this.browser_.addEventListener("pagehide", this.onPageHideClosure_, true);
+
+ this.onLoadClosure_ = BindToObject(this.onLoad, this);
+ this.browser_.addEventListener("load", this.onLoadClosure_, true);
+
+ this.onUnloadClosure_ = BindToObject(this.onUnload, this);
+ this.browser_.addEventListener("unload", this.onUnloadClosure_, true);
+}
+
+/**
+ * Invoked when pageshow fires
+ *
+ * @param e Event object passed in by event system
+ */
+G_BrowserWatcher.prototype.onPageShow = function(e) {
+ G_Debug(this, "onPageShow for " + ((e.target) ? (e.target) : ("undefined")));
+
+ if (e.target && e.target.nodeName == "#document") {
+ var doc = e.target;
+ this.parent_.fireDocEvent_(this.parent_.events.PAGESHOW,
+ doc,
+ this.browser_);
+ }
+}
+
+/**
+ * Invoked when load fires
+ *
+ * @param e Event object passed in by event system
+ */
+G_BrowserWatcher.prototype.onLoad = function(e) {
+ G_Debug(this, "onLoad for a " + e.target);
+
+ if (e.target.nodeName != "#document")
+ return;
+
+ var doc = e.target;
+ this.parent_.fireDocEvent_(this.parent_.events.LOAD, doc, this.browser_);
+}
+
+/**
+ * Invoked when unload fires
+ *
+ * @param e Event object passed in by event system
+ */
+G_BrowserWatcher.prototype.onUnload = function(e) {
+ G_Debug(this, "onUnload for " + ((e.target) ? (e.target) : ("undefined")));
+
+ var doc = e.target;
+
+ // We get spurious unloads for non-docs :(
+ if (doc && doc.nodeName == "#document")
+ this.parent_.fireDocEvent_("unload", doc, this.browser_);
+
+
+ if (!doc) { // This is a closing tab
+ G_Debug(this, "firing tabunload for a " + this.browser_ + "(" +
+ this.browser_.nodename + ")");
+
+ // fire tabunload event
+ this.parent_.fire(this.parent_.events.TABUNLOAD,
+ { "browser": this.browser_ });
+ // unregister event listeners
+ this.browser_.removeEventListener("pageshow", this.onPageShowClosure_, true);
+ this.browser_.removeEventListener("pagehide", this.onPageHideClosure_, true);
+ this.browser_.removeEventListener("load", this.onLoadClosure_, true);
+ this.browser_.removeEventListener("unload", this.onUnloadClosure_, true);
+ this.parent_ = null;
+ this.browser_ = null;
+ G_Debug(this, "Removing event listeners");
+ }
+}
+
+/**
+ * Invoked when pagehide fires
+ *
+ * @param e Event object passed in by event system
+ */
+G_BrowserWatcher.prototype.onPageHide = function(e) {
+ G_Debug(this, "onPageHide for a " + e.target + "(" +
+ e.target.nodeName + ")");
+
+ if (e.target.nodeName != "#document") // Ignore non-documents
+ return;
+
+ var doc = e.target;
+ this.parent_.fireDocEvent_(this.parent_.events.PAGEHIDE, doc, this.browser_);
+}
diff --git a/mozilla/browser/components/safebrowsing/content/phishing-afterload-displayer.js b/mozilla/browser/components/safebrowsing/content/phishing-afterload-displayer.js
new file mode 100644
index 00000000000..2c634c8a265
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/phishing-afterload-displayer.js
@@ -0,0 +1,643 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// Implementation of the warning message we show users when we
+// notice navigation to a phishing page after it has loaded. The
+// browser view encapsulates all the hide/show logic, so the displayer
+// doesn't need to know when to display itself, only how.
+//
+// Displayers implement the following interface:
+//
+// start() -- fired to initialize the displayer (to make it active). When
+// called, this displayer starts listening for and responding to
+// events. At most one displayer per tab should be active at a
+// time, and start() should be called at most once.
+// declineAction() -- fired when the user declines the warning.
+// acceptAction() -- fired when the user declines the warning
+// explicitShow() -- fired when the user wants to see the warning again
+// browserSelected() -- the browser is the top tab
+// browserUnselected() -- the browser is no long the top tab
+// done() -- clean up. May be called once (even if the displayer wasn't
+// activated).
+//
+// At the moment, all displayers share access to the same xul in
+// safebrowsing-overlay.xul. Hence the need for at most one displayer
+// to be active per tab at a time.
+
+/**
+ * Factory that knows how to create a displayer appropriate to the
+ * user's platform. We use a clunky canvas-based displayer for all
+ * platforms until such time as we can overlay semi-transparent
+ * areas over browser content.
+ *
+ * See the base object for a description of the constructor args
+ *
+ * @constructor
+ */
+function PROT_PhishMsgDisplayer(msgDesc, browser, doc, url) {
+
+ // TODO: Change this to return a PhishMsgDisplayerTransp on windows
+ // (and maybe other platforms) when Firefox 2.0 hits.
+
+ return new PROT_PhishMsgDisplayerCanvas(msgDesc, browser, doc, url);
+}
+
+
+/**
+ * Base class that implements most of the plumbing required to hide
+ * and show a phishing warning. Subclasses implement the actual
+ * showMessage and hideMessage methods.
+ *
+ * This class is not meant to be instantiated directly.
+ *
+ * @param msgDesc String describing the kind of warning this is
+ * @param browser Reference to the browser over which we display the msg
+ * @param doc Reference to the document in which browser is found
+ * @param url String containing url of the problem document
+ * @constructor
+ */
+function PROT_PhishMsgDisplayerBase(msgDesc, browser, doc, url) {
+ this.debugZone = "phisdisplayer";
+ this.msgDesc_ = msgDesc; // currently unused
+ this.browser_ = browser;
+ this.doc_ = doc;
+ this.url_ = url;
+
+ // We'll need to manipulate the XUL in safebrowsing-overlay.xul
+ this.messageId_ = "safebrowsing-palm-message";
+ this.messageTailId_ = "safebrowsing-palm-message-tail-container";
+ this.messageContentId_ = "safebrowsing-palm-message-content";
+ this.extendedMessageId_ = "safebrowsing-palm-extended-message";
+ this.showmoreLinkId_ = "safebrowsing-palm-showmore-link";
+ this.urlbarIconId_ = "safebrowsing-urlbar-icon";
+ this.refElementId_ = this.urlbarIconId_;
+
+ // We use this to report user actions to the server
+ this.reporter_ = new PROT_Reporter();
+
+ // The active UI elements in our warning send these commands; bind them
+ // to their handlers but don't register the commands until we start
+ // (because another displayer might be active)
+ this.commandHandlers_ = {
+ "safebrowsing-palm-showmore":
+ BindToObject(this.showMore_, this),
+ "safebrowsing-palm-phishingorg":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getAntiPhishingURL()),
+ "safebrowsing-palm-phishingfaq":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getPhishingFaqURL()),
+ "safebrowsing-palm-fraudpage" :
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getHomePageURL()),
+ "safebrowsing-palm-falsepositive":
+ BindToObject(this.showURL_, this, PROT_GlobalStore.getFalsePositiveURL()),
+ };
+
+ this.windowWatcher_ =
+ Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+ .getService(Components.interfaces.nsIWindowWatcher);
+}
+
+/**
+ * @returns The default background color of the browser
+ */
+PROT_PhishMsgDisplayerBase.prototype.getBackgroundColor_ = function() {
+ var pref = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ return pref.getCharPref("browser.display.background_color");
+}
+
+/**
+ * Fired when the user declines our warning. Report it!
+ */
+PROT_PhishMsgDisplayerBase.prototype.declineAction = function() {
+ G_Debug(this, "User declined warning.");
+ G_Assert(this, this.started_, "Decline on a non-active displayer?");
+ this.reporter_.report("phishdecline", this.url_);
+
+ this.messageShouldShow_ = false;
+ if (this.messageShowing_)
+ this.hideMessage_();
+}
+
+/**
+ * Fired when the user accepts our warning
+ */
+PROT_PhishMsgDisplayerBase.prototype.acceptAction = function() {
+ G_Assert(this, this.started_, "Accept on an unstarted displayer?");
+ G_Assert(this, this.done_, "Accept on a finished displayer?");
+ G_Debug(this, "User accepted warning.");
+ this.reporter_.report("phishaccept", this.url_);
+
+ var url = PROT_GlobalStore.getGetMeOutOfHereURL();
+ this.browser_.loadURI(url);
+}
+
+/**
+ * Invoked when the browser is resized
+ */
+PROT_PhishMsgDisplayerBase.prototype.onBrowserResized_ = function(event) {
+
+ G_Debug(this, "Got resize for " + event.target.nodeName);
+
+ if (event.target == this.doc_) {
+ G_Debug(this, "User resized browser.");
+
+ if (this.messageShowing_) {
+ this.hideMessage_();
+ this.showMessage_();
+ }
+ }
+}
+
+/**
+ * Invoked by the browser view when our browser is switched to
+ */
+PROT_PhishMsgDisplayerBase.prototype.browserSelected = function() {
+ G_Assert(this, this.started_, "Displayer selected before being started???");
+
+ // If messageshowing hasn't been set, then this is the first time this
+ // problematic browser tab has been on top, so do our setup and show
+ // the warning.
+ if (this.messageShowing_ === undefined) {
+ this.messageShouldShow_ = true;
+ this.ensureNavBarShowing_();
+ }
+
+ this.hideLockIcon_(); // Comes back when we are unselected or unloaded
+ this.addWarningInUrlbar_(); // Goes away when we are unselected or unloaded
+
+ // messageShouldShow might be false if the user dismissed the warning,
+ // switched tabs, and then switched back. We're still active, but don't
+ // want to show the warning again. The user can cause it to show by
+ // clicking our icon in the urlbar.
+ if (this.messageShouldShow_)
+ this.showMessage_();
+}
+
+/**
+ * Invoked to display the warning message explicitly, for example if the user
+ * clicked the url warning icon.
+ */
+PROT_PhishMsgDisplayerBase.prototype.explicitShow = function() {
+ this.messageShouldShow_ = true;
+ if (!this.messageShowing_)
+ this.showMessage_();
+}
+
+/**
+ * Invoked by the browser view when our browser is switched away from
+ */
+PROT_PhishMsgDisplayerBase.prototype.browserUnselected = function() {
+ this.removeWarningInUrlbar_();
+ this.unhideLockIcon_();
+ if (this.messageShowing_)
+ this.hideMessage_();
+}
+
+/**
+ * Invoked to make this displayer active. The displayer will now start
+ * responding to notifications such as commands and resize events. We
+ * can't do this in the constructor because there might be many
+ * displayers instantiated waiting in the problem queue for a particular
+ * browser (e.g., a page has multiple framed problem pages), and we
+ * don't want them all responding to commands!
+ *
+ * Invoked zero (the page we're a warning for was nav'd away from
+ * before it reaches the head of the problem queue) or one (we're
+ * displaying this warning) times by the browser view.
+ */
+PROT_PhishMsgDisplayerBase.prototype.start = function() {
+ G_Assert(this, this.started_ == undefined, "Displayer started twice?");
+ this.started_ = true;
+
+ this.commandController_ = new PROT_CommandController(this.commandHandlers_);
+ this.doc_.defaultView.controllers.appendController(this.commandController_);
+
+ this.resizeHandler_ = BindToObject(this.onBrowserResized_, this);
+ this.doc_.defaultView.addEventListener("resize",
+ this.resizeHandler_,
+ true);
+}
+
+/**
+ * @returns Boolean indicating whether this displayer is currently
+ * active
+ */
+PROT_PhishMsgDisplayerBase.prototype.isActive = function() {
+ return !!this.started_;
+}
+
+/**
+ * Invoked by the browser view to clean up after the user is done
+ * interacting with the message. Should be called once by the browser
+ * view.
+ */
+PROT_PhishMsgDisplayerBase.prototype.done = function() {
+ G_Assert(this, !this.done_, "Called done more than once?");
+ this.done_ = true;
+
+ // If the Document we're showing the warning for was nav'd away from
+ // before we had a chance to get started, we have nothing to do.
+ if (this.started_) {
+
+ // If we were started, we must be the current problem, so these things
+ // must be showing
+ this.removeWarningInUrlbar_();
+ this.unhideLockIcon_();
+
+ // Could be though that they've closed the warning dialog
+ if (this.messageShowing_)
+ this.hideMessage_();
+
+ if (this.resizeHandler_) {
+ this.doc_.defaultView.removeEventListener("resize",
+ this.resizeHandler_,
+ true);
+ this.resizeHandler_ = null;
+ }
+
+ var win = this.doc_.defaultView;
+ win.controllers.removeController(this.commandController_);
+ this.commandController_ = null;
+ }
+}
+
+/**
+ * Helper function to remove a substring from inside a string.
+ *
+ * @param orig String to remove substring from
+ *
+ * @param toRemove String to remove (if it is present)
+ *
+ * @returns String with the substring removed
+ */
+PROT_PhishMsgDisplayerBase.prototype.removeIfExists_ = function(orig,
+ toRemove) {
+ var pos = orig.indexOf(toRemove);
+ if (pos != -1)
+ orig = orig.substring(0, pos) + orig.substring(pos + toRemove.length);
+
+ return orig;
+}
+
+/**
+ * Our message is displayed relative to an icon in the urlbar. However
+ * it could be the case that the urlbar isn't showing, in which case
+ * we would show the warning message some place crazy, such as
+ * offscreen. So here we put it back if it's not there. This is a
+ * good thing anyway: the user should be able to see the URL of the
+ * site if we think it is suspicious.
+ */
+PROT_PhishMsgDisplayerBase.prototype.ensureNavBarShowing_ = function() {
+ // Could be that the navbar was unselected in the Toolbars menu; fix it
+ var navbar = this.doc_.getElementById("nav-bar");
+ navbar.setAttribute("collapsed", false);
+
+ // Now for the more complicated case: a popup window with toolbar=no. This
+ // adds toolbar and location (among other things) to the chromhidden
+ // attribute on the window. We need to undo this, and manually fill in the
+ // location.
+ var win = this.doc_.getElementById("main-window");
+ var hidden = win.getAttribute("chromehidden");
+ G_Debug(this, "Old chromehidden string: " + hidden);
+
+ var newHidden = this.removeIfExists_(hidden, "toolbar");
+ newHidden = this.removeIfExists_(newHidden, "location");
+
+ if (newHidden != hidden) {
+ G_Debug(this, "New chromehidden string: " + newHidden);
+ win.setAttribute("chromehidden", newHidden);
+
+ // Now manually reflect the location
+ var urlbar = this.doc_.getElementById("urlbar");
+ urlbar.value = this.doc_.getElementById("content")
+ .contentDocument.location.href;
+ }
+}
+
+/**
+ * We don't want to confuse users if they land on a phishy page that uses
+ * SSL, so ensure that the lock icon never shows when we're showing our
+ * warning.
+ */
+PROT_PhishMsgDisplayerBase.prototype.hideLockIcon_ = function() {
+ var lockIcon = this.doc_.getElementById("lock-icon");
+ lockIcon.hidden = true;
+}
+
+/**
+ * Ensure they can see it after our warning is finished.
+ */
+PROT_PhishMsgDisplayerBase.prototype.unhideLockIcon_ = function() {
+ var lockIcon = this.doc_.getElementById("lock-icon");
+ lockIcon.hidden = false;
+}
+
+/**
+ * This method makes our warning icon visible in the location bar. It will
+ * be removed only when the problematic document is navigated awy from
+ * (i.e., when done() is called), and not when the warning is dismissed.
+ */
+PROT_PhishMsgDisplayerBase.prototype.addWarningInUrlbar_ = function() {
+ var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
+ urlbarIcon.style.display = "";
+}
+
+/**
+ * Hides our urlbar icon
+ */
+PROT_PhishMsgDisplayerBase.prototype.removeWarningInUrlbar_ = function() {
+ var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
+ urlbarIcon.style.display = "none";
+}
+
+/**
+ * VIRTUAL -- Displays the warning message
+ */
+PROT_PhishMsgDisplayerBase.prototype.showMessage_ = function() { };
+
+/**
+ * VIRTUAL -- Hide the warning message from the user.
+ */
+PROT_PhishMsgDisplayerBase.prototype.hideMessage_ = function() { };
+
+/**
+ * Reposition the message relative to refElement in the parent window
+ *
+ * @param message Reference to the element to position
+ * @param tail Reference to the message tail
+ * @param refElement Reference to element relative to which we position
+ * ourselves
+ */
+PROT_PhishMsgDisplayerBase.prototype.adjustLocation_ = function(message,
+ tail,
+ refElement) {
+
+ var refX = refElement.boxObject.x;
+ var refY = refElement.boxObject.y;
+ var refHeight = refElement.boxObject.height;
+ var refWidth = refElement.boxObject.width;
+ G_Debug(this, "Ref element is at [window-relative] (" + refX + ", " +
+ refY + ")");
+
+ var pixelsIntoRefY = -2;
+ var tailY = refY + refHeight - pixelsIntoRefY;
+ var tailPixelsLeftOfRefX = tail.boxObject.width;
+ var tailPixelsIntoRefX = Math.round(refWidth / 2);
+ var tailX = refX - tailPixelsLeftOfRefX + tailPixelsIntoRefX;
+
+ var messageY = tailY + tail.boxObject.height + 3;
+ var messagePixelsLeftOfRefX = 375;
+ var messageX = refX - messagePixelsLeftOfRefX;
+ G_Debug(this, "Message is at [window-relative] (" + messageX + ", " +
+ messageY + ")");
+ G_Debug(this, "Tail is at [window-relative] (" + tailX + ", " +
+ tailY + ")");
+
+ tail.style.top = tailY + "px";
+ tail.style.left = tailX + "px";
+ message.style.top = messageY + "px";
+ message.style.left = messageX + "px";
+}
+
+/**
+ * Show the extended warning message
+ */
+PROT_PhishMsgDisplayerBase.prototype.showMore_ = function() {
+ this.doc_.getElementById(this.extendedMessageId_).hidden = false;
+ this.doc_.getElementById(this.showmoreLinkId_).style.display = "none";
+}
+
+/**
+ * The user clicked on one of the links in the buble. Display the
+ * corresponding page in a new window with all the chrome enabled.
+ *
+ * @param url The URL to display in a new window
+ */
+PROT_PhishMsgDisplayerBase.prototype.showURL_ = function(url) {
+ this.windowWatcher_.openWindow(this.windowWatcher_.activeWindow,
+ url,
+ "_blank",
+ null,
+ null);
+}
+
+
+/**
+ * A specific implementation of the dislpayer using a canvas. This
+ * class is meant for use on platforms that don't support transparent
+ * elements over browser content (currently: all platforms).
+ *
+ * The main ugliness is the fact that we're hiding the content area and
+ * painting the page to canvas. As a result, we must periodically
+ * re-paint the canvas to reflect updates to the page. Otherwise if
+ * the page was half-loaded when we showed our warning, it would
+ * stay that way even though the page actually finished loading.
+ *
+ * See base constructor for full details of constructor args.
+ *
+ * @constructor
+ */
+function PROT_PhishMsgDisplayerCanvas(msgDesc, browser, doc, url) {
+ PROT_PhishMsgDisplayerBase.call(this, msgDesc, browser, doc, url);
+
+ this.dimAreaId_ = "safebrowsing-dim-area-canvas";
+ this.contentStackId_ = "safebrowsing-content-stack";
+ this.pageCanvasId_ = "safebrowsing-page-canvas";
+ this.xhtmlNS_ = "http://www.w3.org/1999/xhtml"; // we create html:canvas
+}
+
+PROT_PhishMsgDisplayerCanvas.inherits(PROT_PhishMsgDisplayerBase);
+
+/**
+ * Displays the warning message.
+ */
+PROT_PhishMsgDisplayerCanvas.prototype.showMessage_ = function() {
+
+ G_Debug(this, "Showing message.");
+ this.messageShowing_ = true;
+
+ // Unhide our stack. Order here is significant, but don't ask me why
+ // for some of these. You need to:
+ // 1. add canvas to the stack in a hidden state
+ // 2. unhide the stack
+ // 3. get browser dimensions
+ // 4. unhide stack contents
+ // 5. display to the canvas
+ // 6. unhide the warning message
+
+ // (1)
+ // We add the canvas dynamically and remove it when we're done because
+ // leaving it hanging around consumes a lot of memory.
+ var pageCanvas = this.doc_.createElementNS(this.xhtmlNS_, "html:canvas");
+ pageCanvas.id = this.pageCanvasId_;
+ pageCanvas.hidden = "true";
+ var contentStack = this.doc_.getElementById(this.contentStackId_);
+ contentStack.insertBefore(pageCanvas, contentStack.firstChild);
+
+ // (2)
+ contentStack.hidden = false;
+
+ // (3)
+ var w = this.browser_.boxObject.width;
+ G_Debug(this, "browser w=" + w);
+ var h = this.browser_.boxObject.height;
+ G_Debug(this, "browser h=" + h);
+
+ var win = this.browser_.contentWindow;
+ var scrollX = win.scrollX;
+ G_Debug(this, "win scrollx=" + scrollX);
+ var scrollY = win.scrollY;
+ G_Debug(this, "win scrolly=" + scrollY);
+
+ // (4)
+ var dimarea = this.doc_.getElementById(this.dimAreaId_);
+ dimarea.hidden = false;
+
+ this.browser_.parentNode.collapsed = true; // And now hide the browser
+
+ // (5)
+ pageCanvas.setAttribute("width", w);
+ pageCanvas.setAttribute("height", h);
+
+ var bgcolor = this.getBackgroundColor_();
+
+ var cx = pageCanvas.getContext("2d");
+ cx.drawWindow(win, scrollX, scrollY, w, h, bgcolor);
+
+ // Now repaint the window every so often in case the content hasn't fully
+ // loaded at this point.
+ var debZone = this.debugZone;
+ function repaint() {
+ G_Debug(debZone, "Repainting canvas...");
+ cx.drawWindow(win, scrollX, scrollY, w, h, bgcolor);
+ };
+ this.repainter_ = new PROT_PhishMsgCanvasRepainter(repaint);
+
+ // (6)
+ var refElement = this.doc_.getElementById(this.refElementId_);
+ var message = this.doc_.getElementById(this.messageId_);
+ var tail = this.doc_.getElementById(this.messageTailId_);
+ message.hidden = false;
+ message.style.display = "block";
+ tail.hidden = false;
+ tail.style.display = "block";
+ this.adjustLocation_(message, tail, refElement);
+}
+
+/**
+ * Hide the warning message from the user.
+ */
+PROT_PhishMsgDisplayerCanvas.prototype.hideMessage_ = function() {
+ G_Debug(this, "Hiding phishing warning.");
+ G_Assert(this, this.messageShowing_, "Hide message called but not showing?");
+
+ this.messageShowing_ = false;
+ this.repainter_.cancel();
+ this.repainter_ = null;
+
+ var message = this.doc_.getElementById(this.messageId_);
+ message.hidden = true;
+ message.style.display = "none";
+
+ var tail = this.doc_.getElementById(this.messageTailId_);
+ tail.hidden = true;
+ tail.style.display = "none";
+ this.browser_.parentNode.collapsed = false;
+
+ var contentStack = this.doc_.getElementById(this.contentStackId_);
+ contentStack.hidden = true;
+
+ var dimarea = this.doc_.getElementById(this.dimAreaId_);
+ dimarea.hidden = true;
+
+ var pageCanvas = this.doc_.getElementById(this.pageCanvasId_);
+ contentStack.removeChild(pageCanvas);
+}
+
+
+/**
+ * Helper class that periodically repaints the canvas. We repaint
+ * frequently at first, and then back off to a less frequent schedule
+ * at "steady state," and finally just stop altogether. We have to do
+ * this because we're not sure if the page has finished loading when
+ * we first paint the canvas, and because we want to reflect any
+ * dynamically written content into the canvas as it appears in the
+ * page after load.
+ *
+ * @param repaintFunc Function to call to repaint browser.
+ *
+ * @constructor
+ */
+function PROT_PhishMsgCanvasRepainter(repaintFunc) {
+ this.count_ = 0;
+ this.repaintFunc_ = repaintFunc;
+ this.initPeriodMS_ = 500; // Initially repaint every 500ms
+ this.steadyStateAtMS_ = 10 * 1000; // Go slowly after 10 seconds,
+ this.steadyStatePeriodMS_ = 3 * 1000; // repainting every 3 seconds, and
+ this.quitAtMS_ = 20 * 1000; // stop after 20 seconds
+ this.startMS_ = (new Date).getTime();
+ this.alarm_ = new G_Alarm(BindToObject(this.repaint, this),
+ this.initPeriodMS_);
+}
+
+/**
+ * Called periodically to repaint the canvas
+ */
+PROT_PhishMsgCanvasRepainter.prototype.repaint = function() {
+ this.repaintFunc_();
+
+ var nextRepaint;
+ // If we're in "steady state", use the slow repaint rate, else fast
+ if ((new Date).getTime() - this.startMS_ > this.steadyStateAtMS_)
+ nextRepaint = this.steadyStatePeriodMS_;
+ else
+ nextRepaint = this.initPeriodMS_;
+
+ if (!((new Date).getTime() - this.startMS_ > this.quitAtMS_))
+ this.alarm_ = new G_Alarm(BindToObject(this.repaint, this), nextRepaint);
+}
+
+/**
+ * Called to stop repainting the canvas
+ */
+PROT_PhishMsgCanvasRepainter.prototype.cancel = function() {
+ if (this.alarm_) {
+ this.alarm_.cancel();
+ this.alarm_ = null;
+ }
+ this.repaintFunc_ = null;
+}
diff --git a/mozilla/browser/components/safebrowsing/content/phishing-warden.js b/mozilla/browser/components/safebrowsing/content/phishing-warden.js
new file mode 100644
index 00000000000..aac59387d55
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/phishing-warden.js
@@ -0,0 +1,476 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// The warden checks request to see if they are for phishy pages. It
+// does so by either querying a remote server with the URL (advanced
+// protectoin mode) or querying our locally stored blacklists (privacy
+// mode).
+//
+// When the warden notices a problem, it queries all browser views
+// (each of which corresopnds to an open browser window) to see
+// whether one of them can handle it. A browser view can handle a
+// problem if its browser window has an HTMLDocument loaded with the
+// given URL and that Document hasn't already been flagged as a
+// problem. For every problematic URL we notice loading, at most one
+// Document is flagged as problematic. Otherwise you can get into
+// trouble if multiple concurrent phishy pages load with the same URL.
+//
+// Since we check URLs very early in the request cycle (in a progress
+// listener), the URL might not yet be associated with a Document when
+// we determine that it is phishy. So the the warden retries finding
+// a browser view to handle the problem until one can, or until it
+// determines it should give up (see complicated logic below).
+//
+// The warden has displayers that the browser view uses to render
+// different kinds of warnings (e.g., one that's shown before a page
+// loads as opposed to one that's shown after the page has already
+// loaded).
+//
+// Note: There is a single warden for the whole application.
+//
+// TODO better way to expose displayers/views to browser view
+/**
+ * Abtracts the checking of user/browser actions for signs of
+ * phishing.
+ *
+ * @constructor
+ */
+function PROT_PhishingWarden() {
+ PROT_ListWarden.call(this);
+
+ this.debugZone = "phishwarden";
+ this.testing_ = false;
+ this.browserViews_ = [];
+
+ // Use this to query preferences
+ this.prefs_ = new G_Preferences();
+
+ // Only one displayer so far; perhaps we'll have others in the future
+ this.displayers_ = {
+ "afterload": PROT_PhishMsgDisplayer,
+ };
+
+ // We use this dude to do lookups on our remote server
+ this.fetcher_ = new PROT_TRFetcher();
+
+ // Register w/NavWatcher to hear notifications about requests for docs
+ if (!this.testing_) {
+ this.navWatcher_ = new G_NavWatcher(true /* filter spurious navs */);
+ this.navWatcher_.registerListener("docnavstart",
+ BindToObject(this.onDocNavStart,
+ this));
+ }
+
+ // We need to know whether we're enabled and whether we're in advanced
+ // mode, so reflect the appropriate preferences into our state.
+
+ // Read state: should we be checking remote preferences?
+ var checkRemotePrefName = PROT_GlobalStore.getServerCheckEnabledPrefName();
+ this.checkRemote_ = this.prefs_.getPref(checkRemotePrefName, null);
+
+ // Get notifications when the remote check preference changes
+ var checkRemotePrefObserver = BindToObject(this.onCheckRemotePrefChanged,
+ this);
+ this.prefs_.addObserver(checkRemotePrefName, checkRemotePrefObserver);
+
+ // Global preference to enable the phishing warden
+ var phishWardenPrefName = PROT_GlobalStore.getPhishWardenEnabledPrefName();
+ this.phishWardenEnabled_ = this.prefs_.getPref(phishWardenPrefName, null);
+
+ // Get notifications when the phishing warden enabled pref changes
+ var phishWardenPrefObserver =
+ BindToObject(this.onPhishWardenEnabledPrefChanged, this);
+ this.prefs_.addObserver(phishWardenPrefName, phishWardenPrefObserver);
+
+ // We have a hardcoded URLs we let people navigate to in order to
+ // check out the warning.
+ this.testURLs_ = PROT_GlobalStore.getTestURLs();
+
+ G_Debug(this, "phishWarden initialized");
+}
+
+PROT_PhishingWarden.inherits(PROT_ListWarden);
+
+/**
+ * When a preference (either advanced features or the phishwarden
+ * enabled) changes, we might have to start or stop asking for updates.
+ *
+ * This is a little tricky; we start or stop management only when we
+ * have complete information we can use to determine whether we
+ * should. It could be the case that one pref or the other isn't set
+ * yet (e.g., they haven't opted in/out of advanced features). So do
+ * nothing unless we have both pref values -- we get notifications for
+ * both, so eventually we will start correctly.
+ */
+PROT_PhishingWarden.prototype.maybeToggleUpdateChecking = function() {
+ if (this.testing_)
+ return;
+
+ var phishWardenPrefName = PROT_GlobalStore.getPhishWardenEnabledPrefName();
+ var phishWardenEnabled = this.prefs_.getPref(phishWardenPrefName, null);
+
+ var checkRemotePrefName = PROT_GlobalStore.getServerCheckEnabledPrefName();
+ this.checkRemote_ = this.prefs_.getPref(checkRemotePrefName, null);
+
+ G_Debug(this, "Maybe toggling update checking. " +
+ "Warden enabled? " + phishWardenEnabled + " || " +
+ "Check remote? " + this.checkRemote_);
+
+ // Do nothing unless both prefs are set. They can be null (unset), true, or
+ // false.
+ if (phishWardenEnabled === null || this.checkRemote_ === null)
+ return;
+
+ // We update and save to disk all tables if we don't have remote checking
+ // enabled. However, as long as the warden is enabled we always update the
+ // whitelist, even if remote checking is disabled. This gives a performance
+ // benefit since we use the WL to suppress BL lookups.
+ //
+ // phishEnabled remote WLupdates BLupdates
+ // T T T F
+ // T F T T
+ // F T F F
+ // F F F F
+
+ if (phishWardenEnabled === true) {
+ this.enableWhitelistTableUpdates();
+ if (this.checkRemote_ === true) {
+ this.disableBlacklistTableUpdates();
+ } else if (this.checkRemote_ === false) {
+ this.enableBlacklistTableUpdates();
+ }
+ } else if (phishWardenEnabled === false) {
+ this.disableBlacklistTableUpdates();
+ this.disableWhitelistTableUpdates();
+ }
+}
+
+/**
+ * Controllers register their browser views with us
+ *
+ * @param view Reference to a browser view
+ */
+PROT_PhishingWarden.prototype.addBrowserView = function(view) {
+ G_Debug(this, "New browser view registered.");
+ this.browserViews_.push(view);
+}
+
+/**
+ * Controllers unregister their views when their window closes
+ *
+ * @param view Reference to a browser view
+ */
+PROT_PhishingWarden.prototype.removeBrowserView = function(view) {
+ for (var i = 0; i < this.browserViews_.length; i++)
+ if (this.browserViews_[i] === view) {
+ G_Debug(this, "Browser view unregistered.");
+ this.browserViews_.splice(i, 1);
+ return;
+ }
+ G_Assert(this, false, "Tried to unregister non-existent browser view!");
+}
+
+/**
+ * Deal with a user changing the pref that says whether we should check
+ * the remote server (i.e., whether we're in advanced mode)
+ *
+ * @param prefName Name of the pref holding the value indicating whether
+ * we should check remote server
+ */
+PROT_PhishingWarden.prototype.onCheckRemotePrefChanged = function(prefName) {
+ this.checkRemote_ = this.prefs_.getBoolPrefOrDefault(prefName,
+ this.checkRemote_);
+ this.maybeToggleUpdateChecking();
+}
+
+/**
+ * Deal with a user changing the pref that says whether we should
+ * enable the phishing warden (i.e., that SafeBrowsing is active)
+ *
+ * @param prefName Name of the pref holding the value indicating whether
+ * we should enable the phishing warden
+ */
+PROT_PhishingWarden.prototype.onPhishWardenEnabledPrefChanged = function(
+ prefName) {
+ this.phishWardenEnabled_ =
+ this.prefs_.getBoolPrefOrDefault(prefName, this.phishWardenEnabled_);
+ this.maybeToggleUpdateChecking();
+}
+
+/**
+ * A request for a Document has been initiated somewhere. Check it!
+ *
+ * @param e Event object passed in by the NavWatcher
+ */
+PROT_PhishingWarden.prototype.onDocNavStart = function(e) {
+
+ var url = e.url;
+ var request = e.request;
+
+ G_Debug(this, "phishWarden: " +
+ (this.phishWardenEnabled_ ? "enabled" : "disabled"));
+ G_Debug(this, "checkRemote: " +
+ (this.checkRemote_ ? "yes" : "no"));
+ G_Debug(this, "isTestURL: " +
+ (this.isBlacklistTestURL(url) ? "yes" : "no"));
+
+ // This logic is a bit involved. In some instances of SafeBrowsing
+ // (the stand-alone extension, for example), the user might yet have
+ // opted into or out of advanced protection mode. In this case we
+ // would like to show them a modal dialog requiring them
+ // to. However, there are links from that dialog to the test page,
+ // and the warden starts out as disabled. So we want to show the
+ // warning on the test page so long as the extension hasn't been
+ // explicitly disabled.
+
+ // If we're on the test page and we're not explicitly disabled
+ if (this.isBlacklistTestURL(url) &&
+ (this.phishWardenEnabled_ === true ||
+ this.phishWardenEnabled_ === null)) {
+ this.houstonWeHaveAProblem_(request);
+ } else if (this.phishWardenEnabled_ === true) {
+ // We're enabled. Either send a request off or check locally
+ // TODO: move this logic to checkUrl, formalize the error callback
+ if (this.checkRemote_) {
+ // Use local whitelists to suppress remote BL lookups.
+ if (!this.isWhiteURL_(url)) {
+ G_Debug(this, "Local whitelist lookup failed");
+ this.fetcher_.get(url,
+ BindToObject(this.onTRFetchComplete,
+ this,
+ request));
+ } else {
+ G_Debug(this, "WL suppressing BL lookup for " + url);
+ }
+ } else {
+ if (this.checkUrl(url)) {
+ this.houstonWeHaveAProblem_(request);
+ }
+ }
+ }
+}
+
+/**
+ * Invoked with the result of a lookupserver request.
+ *
+ * @param request The nsIRequest in which we're interested
+ *
+ * @param trValues Object holding name/value pairs parsed from the
+ * lookupserver's response
+ */
+PROT_PhishingWarden.prototype.onTRFetchComplete = function(request,
+ trValues) {
+ var callback = BindToObject(this.houstonWeHaveAProblem_, this, request);
+ this.checkRemoteData(callback, trValues);
+}
+
+/**
+ * One of our Check* methods found a problem with a request. Why do we
+ * need to keep the nsIRequest (instead of just passing in the URL)?
+ * Because we need to know when to stop looking for the URL its
+ * fetching, and to know this we need the nsIRequest.isPending flag.
+ *
+ * @param request nsIRequest that is problematic
+ */
+PROT_PhishingWarden.prototype.houstonWeHaveAProblem_ = function(request) {
+
+ // We have a problem request that might or might not be associated
+ // with a Document that's currently in a browser. If it is, we
+ // want that Document. If it's not, we want to give it a chance to
+ // be loaded. See below for complete details.
+
+ if (this.maybeLocateProblem_(request)) // Cases 1 and 2 (see below)
+ return;
+
+ // OK, so the request isn't associated with any currently accessible
+ // Document, and we want to give it the chance to be. We don't want
+ // to retry forever (e.g., what if the Document was already displayed
+ // and navigated away from?), so we'll use nsIRequest.isPending to help
+ // us decide what to do.
+ //
+ // A complication arises because there is a lag between when a
+ // request transitions from pending to not-pending and when it's
+ // associated with a Document in a browser. The transition from
+ // pending to not occurs just before the notification corresponding
+ // to NavWatcher.DOCNAVSTART (see NavWatcher), but the association
+ // occurs afterwards. Unfortunately, we're probably in DOCNAVSTART.
+ //
+ // Diagnosis by Darin:
+ // ---------------------------------------------------------------------------
+ // Here's a summary of what happens:
+ //
+ // RestorePresentation() {
+ // Dispatch_OnStateChange(dummy_request, STATE_START)
+ // PostCompletionEvent()
+ // }
+ //
+ // CompletionEvent() {
+ // ReallyRestorePresentation()
+ // Dispatch_OnStateChange(dummy_request, STATE_STOP)
+ // }
+ //
+ // So, now your code receives that initial OnStateChange event and sees
+ // that the dummy_request is not pending and not loaded in any window.
+ // So, you put a timeout(0) event in the queue. Then, the CompletionEvent
+ // is added to the queue. The stack unwinds....
+ //
+ // Your timeout runs, and you find that the dummy_request is still not
+ // pending and not loaded in any window. Then the CompletionEvent
+ // runs, and it hooks up the cached presentation.
+ //
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=319527
+ // ---------------------------------------------------------------------------
+ //
+ // So the logic is:
+ //
+ // request found an unhandled
+ // case pending? doc with the url? action
+ // ----------------------------------------------------------------
+ // 1 yes yes Use that doc (handled above)
+ // 2 no yes Use that doc (handled above)
+ // 3 yes no Retry
+ // 4 no no Retry twice (case described above)
+ //
+ // We don't get into trouble with Docs with the same URL "stealing" the
+ // warning because there is exactly one warning signaled per nav to
+ // a problem URL, and each Doc can be marked as problematic at most once.
+
+ if (request.isPending()) { // Case 3
+
+ G_Debug(this, "Can't find problem Doc; Req pending. Retrying.");
+ new G_Alarm(BindToObject(this.houstonWeHaveAProblem_,
+ this,
+ request),
+ 200 /*ms*/);
+
+ } else { // Case 4
+
+ G_Debug(this,
+ "Can't find problem Doc; Req completed. Retrying at most twice.");
+ new G_ConditionalAlarm(BindToObject(this.maybeLocateProblem_,
+ this,
+ request),
+ 0 /* next event loop */,
+ true /* repeat */,
+ 2 /* at most twice */);
+ }
+}
+
+/**
+ * Query all browser views we know about and offer them the chance to
+ * handle the problematic request.
+ *
+ * @param request nsIRequest that is problematic
+ *
+ * @returns Boolean indicating if someone decided to handle it
+ */
+PROT_PhishingWarden.prototype.maybeLocateProblem_ = function(request) {
+ G_Debug(this, "Trying to find the problem.");
+
+ G_Debug(this, this.browserViews_.length + " browser views to check.");
+ for (var i = 0; i < this.browserViews_.length; i++) {
+ if (this.browserViews_[i].tryToHandleProblemRequest(this, request)) {
+ G_Debug(this, "Found browser view willing to handle problem!");
+ return true;
+ }
+ G_Debug(this, "wrong browser view");
+ }
+ return false;
+}
+
+/**
+ * Indicates if this URL is one of the possible blacklist test URLs.
+ * These test URLs should always be considered as phishy.
+ *
+ * @param url URL to check
+ * @return A boolean indicating whether this is one of our blacklist
+ * test URLs
+ */
+PROT_PhishingWarden.prototype.isBlacklistTestURL = function(url) {
+ for (var i = 0, testURL = null; testURL = this.testURLs_[i]; ++i) {
+ if (testURL === url) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Look the URL up in our local blacklists
+ *
+ * @param callback Function to invoke if there is a problem.
+ *
+ * @param url URL to check
+ */
+PROT_PhishingWarden.prototype.checkUrl = function(url) {
+ G_Debug(this, "Checking URL for " + url);
+
+ if (this.isEvilURL_(url) || this.isBlacklistTestURL(url)) {
+ G_Debug(this, "Local blacklist hit");
+ // maybe send a report
+ (new PROT_Reporter).report("phishblhit", url);
+ return true;
+ }
+
+ G_Debug(this, "Local blacklist miss");
+ return false;
+}
+
+/**
+ * Examine data fetched from a lookup server for evidence of a
+ * phishing problem.
+ *
+ * @param callback Function to invoke if there is a problem.
+ * @param trValues Object containing name/value pairs the server returned
+ */
+PROT_PhishingWarden.prototype.checkRemoteData = function(callback,
+ trValues) {
+
+ if (!trValues) {
+ G_Debug(this, "Didn't get TR values from the server.");
+ return;
+ }
+
+ G_Debug(this, "Page has phishiness " + trValues["phishy"]);
+
+ if (trValues["phishy"] == 1) { // It's on our blacklist
+ G_Debug(this, "Remote blacklist hit");
+ callback(this);
+ } else {
+ G_Debug(this, "Remote blacklist miss");
+ }
+}
diff --git a/mozilla/browser/components/safebrowsing/content/reporter.js b/mozilla/browser/components/safebrowsing/content/reporter.js
new file mode 100644
index 00000000000..89348d546d6
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/reporter.js
@@ -0,0 +1,91 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// A tiny class to do reporting for us. We report interesting user actions
+// such as the user hitting a blacklisted page, and the user accepting
+// or declining the warning.
+//
+// Each report has a subject and data. Current reports are:
+//
+// subject data meaning
+// --------------------------------
+// phishnavaway url the user navigated away from a phishy page
+// phishdecline url the user declined our warning
+// phishaccept url the user accepted our warning
+// phishblhit url the user loaded a phishing page
+//
+// We only send reports in advanced protection mode, and even then we
+// strip cookies from the request before sending it.
+
+/**
+ * A very complicated class to send pings to the provider. The class does
+ * nothing if we're not in advanced protection mode.
+ *
+ * @constructor
+ */
+function PROT_Reporter() {
+ this.debugZone = "reporter";
+ this.prefs_ = new G_Preferences();
+}
+
+/**
+ * Send a report!
+ *
+ * @param subject String indicating what this report is about (will be
+ * urlencoded)
+ * @param data String giving extra information about this report (will be
+ * urlencoded)
+ */
+PROT_Reporter.prototype.report = function(subject, data) {
+ // Send a report iff we're in advanced protection mode
+ if (!this.prefs_.getPref(PROT_GlobalStore.getSendUserReportsPrefName(),
+ false))
+ return;
+ // Make sure a report url is defined
+ var url = null;
+ try {
+ url = PROT_GlobalStore.getActionReportURL();
+ } catch (e) {
+ }
+ if (!url)
+ return;
+
+ url += "evts=" + encodeURIComponent(subject)
+ + "&evtd=" + encodeURIComponent(data);
+ G_Debug(this, "Sending report: " + url);
+ (new PROT_XMLFetcher(true /* strip cookies */)).get(url, null /* no cb */);
+}
diff --git a/mozilla/browser/components/safebrowsing/content/safebrowsing.inc b/mozilla/browser/components/safebrowsing/content/safebrowsing.inc
new file mode 100644
index 00000000000..f9a6afe8f69
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/safebrowsing.inc
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &safeb.palm.message.part1;
+
+ &safeb.palm.message.link1.more;
+
+
+
+
+
+
+
+
+ &safeb.palm.message.part2;
+
+ &safeb.palm.message.link2.phishing.faq;
+ &safeb.palm.message.part3;
+
+ &safeb.palm.message.link3.antiphishing.org;&safeb.palm.message.part3.dot;
+
+
+
+ &safeb.palm.message.part4;
+
+ &safeb.palm.message.link4.gwghome;&safeb.palm.message.part4.dot;
+
+ &safeb.palm.message.part5;
+ &safeb.palm.message.link5.report;&safeb.palm.message.part5.dot;
+
+
+
+
+
+
+
+
+ &safeb.palm.accept.label;
+
+
+
+
+ &safeb.palm.decline.label;
+
+
+
+
+
+
+
+
+ [
+ &safeb.palm.sendreport.label;]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mozilla/browser/components/safebrowsing/content/sb-loader.js b/mozilla/browser/components/safebrowsing/content/sb-loader.js
new file mode 100644
index 00000000000..3c64885acbf
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/sb-loader.js
@@ -0,0 +1,153 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+/**
+ * This file is included into the main browser chrome from
+ * browser/base/content/global-scripts.inc
+ */
+
+window.addEventListener("load", SB_startup, false);
+
+var SB_controller; // Exported so it's accessible by child windows
+var SB_appContext; // The context in which our Application lives
+ // TODO: appContext does not need to be global
+
+function SB_startup() {
+ var Cc = Components.classes;
+ SB_appContext = Cc["@mozilla.org/safebrowsing/application;1"]
+ .getService();
+ SB_appContext = SB_appContext.wrappedJSObject;
+
+ // Each new browser window needs its own controller.
+
+ var contentArea = document.getElementById("content");
+ var tabWatcher = new SB_appContext.G_TabbedBrowserWatcher(
+ contentArea,
+ "safebrowsing-watcher",
+ true /*ignore about:blank*/);
+
+ var phishWarden = new SB_appContext.PROT_PhishingWarden();
+
+
+ // Register tables
+ // TODO: move table names to a pref
+ phishWarden.registerWhiteTable("goog-white-domain");
+ phishWarden.registerWhiteTable("goog-white-url");
+ phishWarden.registerBlackTable("goog-black-url");
+ phishWarden.registerBlackTable("goog-black-enchash");
+
+ // Download/update lists if we're in non-enhanced mode
+ phishWarden.maybeToggleUpdateChecking();
+
+ SB_controller = new SB_appContext.PROT_Controller(
+ window,
+ tabWatcher,
+ phishWarden);
+
+ // clean up
+ window.removeEventListener("load", SB_startup, false);
+}
+
+
+// Some utils for our UI.
+
+/**
+ * Execute a command on a window
+ *
+ * @param cmd String containing command to execute
+ * @param win Reference to Window on which to execute it
+ */
+function SB_executeCommand(cmd, win) {
+ try {
+ var disp = win.document.commandDispatcher;
+ var ctrl = disp.getControllerForCommand(cmd);
+ ctrl.doCommand(cmd);
+ } catch (e) {
+ dump("Exception on command: " + cmd + "\n");
+ dump(e);
+ }
+}
+
+/**
+ * Execute a command on this window
+ *
+ * @param cmd String containing command to execute
+ */
+function SB_executeCommandLocally(cmd) {
+ SB_executeCommand(cmd, window);
+}
+
+/**
+ * Set status text for a particular link. We look the URLs up in our
+ * globalstore.
+ *
+ * @param link ID of a link for which we should show status text
+ */
+function SB_setStatusFor(link) {
+ var gs = SB_appContext.PROT_GlobalStore;
+ var msg;
+ if (link == "safebrowsing-palm-faq-link")
+ msg = gs.getPhishingFaqURL();
+ else if (link == "safebrowsing-palm-phishingorg-link")
+ msg = gs.getAntiPhishingURL();
+ else if (link == "safebrowsing-palm-fraudpage-link")
+ msg = gs.getHomePageURL();
+ else if (link == "safebrowsing-palm-falsepositive-link")
+ msg = gs.getFalsePositiveURL();
+ else if (link == "safebrowsing-palm-report-link")
+ msg = gs.getSubmitUrl();
+ else
+ msg = "";
+
+ SB_setStatus(msg);
+}
+
+/**
+ * Actually display the status text
+ *
+ * @param msg String that we should show in the statusbar
+ */
+function SB_setStatus(msg) {
+ document.getElementById("statusbar-display").label = msg;
+}
+
+/**
+ * Clear the status text
+ */
+function SB_clearStatus() {
+ document.getElementById("statusbar-display").label = "";
+}
diff --git a/mozilla/browser/components/safebrowsing/content/tr-fetcher.js b/mozilla/browser/components/safebrowsing/content/tr-fetcher.js
new file mode 100644
index 00000000000..03c134c96dd
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/content/tr-fetcher.js
@@ -0,0 +1,205 @@
+/* ***** 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 Google Safe Browsing.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fritz Schneider (original author)
+ *
+ * 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 ***** */
+
+
+// A helper class that does "trustrank" lookups on a remote
+// server. Right now this lookup just indicates if a page is
+// phishing. The response format is protocol4 name/value pairs.
+//
+// Since we're sending full URLs to the server, we try to encrypt
+// them before transmission. Else HTTPS query params could leak.
+
+/**
+ * A helper class that fetches trustrank values, parses them, and
+ * passes them via an object to a callback.
+ *
+ * @constructor
+ */
+function PROT_TRFetcher(opt_noCrypto) {
+ this.debugZone = "trfetcher";
+ this.useCrypto_ = !opt_noCrypto;
+ this.lookupserverURL_ = PROT_GlobalStore.getLookupserverURL();
+ this.protocol4Parser_ = new G_Protocol4Parser();
+
+ // We lazily instantiate the UrlCrypto object due to:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=321024
+ //
+ // Otherwise here we would use:
+ // this.urlCrypto_ = new PROT_UrlCrypto();
+}
+
+PROT_TRFetcher.TRY_REKEYING_RESPONSE = "pleaserekey";
+
+/**
+ * Query params we'll send. Don't touch unless you know what you're
+ * doing and are prepared to carefully test.
+ */
+PROT_TRFetcher.prototype.extraQueryParams = {
+ sourceid: "firefox-antiphish",
+ features: "TrustRank",
+ client: "navclient-auto-ffox"
+};
+
+/**
+ * Get the URL of the request that will fetch us TR for the argument URL
+ *
+ * @param url String containing the URL we'd like to fetch info about
+ *
+ * @returns String containing the url we should use to fetch tr info
+ */
+PROT_TRFetcher.prototype.getRequestURL_ = function(url) {
+
+ if (!this.urlCrypto_)
+ this.urlCrypto_ = new PROT_UrlCrypto();
+
+ G_Debug(this, "Fetching for " + url);
+
+ var requestURL = this.lookupserverURL_;
+ for (var param in this.extraQueryParams)
+ requestURL += param + "=" + this.extraQueryParams[param] + "&";
+
+ if (this.useCrypto_) {
+ var maybeCryptedParams = this.urlCrypto_.maybeCryptParams({ "q": url});
+
+ for (var param in maybeCryptedParams)
+ requestURL += param + "=" +
+ encodeURIComponent(maybeCryptedParams[param]) + "&";
+ } else {
+ requestURL += "q=" + encodeURIComponent(url);
+ }
+
+ G_Debug(this, "Request URL: " + requestURL);
+
+ return requestURL;
+};
+
+/**
+ * Fetches information about a page.
+ *
+ * @param forPage URL for which to fetch info
+ *
+ * @param callback Function to call back when complete.
+ */
+PROT_TRFetcher.prototype.get = function(forPage, callback) {
+
+ var url = this.getRequestURL_(forPage);
+ var closure = BindToObject(this.onFetchComplete_, this, callback);
+ (new PROT_XMLFetcher()).get(url, closure);
+
+ // Make this true if you want to dump URLs in a format we can use
+ // for testing
+ if (false)
+ dump("\ntests[\"" + url + "\"] = \"" + forPage + "\";\n");
+};
+
+/**
+ * Invoked when a fetch has completed.
+ *
+ * @param callback Function to invoke with parsed response object
+ *
+ * @param responseText Text of the protocol4 message
+ */
+PROT_TRFetcher.prototype.onFetchComplete_ = function(callback, responseText) {
+ var responseObj = this.extractResponse_(responseText);
+
+ // The server might tell us to rekey, for example if it sees that
+ // our request was unencrypted (meaning that we might not yet have
+ // a key). If so, pass this hint along to the crypto key manager.
+
+ if (responseObj[PROT_TRFetcher.TRY_REKEYING_RESPONSE] == "1" &&
+ this.urlCrypto_) {
+ G_Debug(this, "We're supposed to re-key. Trying.");
+ var manager = this.urlCrypto_.getManager();
+ if (manager)
+ manager.maybeReKey();
+ }
+
+ G_Debug(this, "TR Response:");
+ for (var field in responseObj)
+ G_Debug(this, field + "=" + responseObj[field]);
+
+ callback(responseObj);
+};
+
+/**
+ * Parse a protocol4 message (lookup server response)
+ *
+ * @param responseText String containing the server's response
+ *
+ * @returns Object containing the returned values or null if no
+ * response was received
+ */
+PROT_TRFetcher.prototype.extractResponse_ = function(responseText) {
+ return this.protocol4Parser_.parse(responseText);
+};
+
+// Unittests
+
+function TEST_PROT_TRFetcher() {
+ if (G_GDEBUG) {
+ var z = "trfetcher UNITTEST";
+ G_debugService.enableZone(z);
+ G_Debug(z, "Starting");
+
+ // Old test from before protocol4parser was broken out on its own.
+ // But they do no harm, so we leave 'em.
+ var fetcher = new PROT_TRFetcher();
+ var fakeResponse = "foo:3:foo\nbar:3:bar\nbaz:1:b";
+ var fields = fetcher.extractResponse_(fakeResponse);
+ G_Assert(z, fields["foo"] == "foo", "Bad parse: foo");
+ G_Assert(z, fields["bar"] == "bar", "Bad parse: bar");
+ G_Assert(z, fields["baz"] == "b", "Bad parse: baz");
+ G_Assert(z, !fields["yourmom"], "Bad parse: yourmom");
+
+ // Test re-keying by stubbing the fetcher's urlCrypto_ object.
+
+ var calledRekey = false;
+ var fakeManager = {
+ maybeReKey: function() {
+ calledRekey = true;
+ }
+ };
+
+ fetcher.urlCrypto_ = {};
+ fetcher.urlCrypto_.getManager = function() { return fakeManager; };
+ var rekeyText = "foo:1:bar\n" +
+ PROT_TRFetcher.TRY_REKEYING_RESPONSE + ":1:1";
+ fetcher.onFetchComplete_(function() {}, rekeyText);
+ G_Assert(z, calledRekey, "rekey didn't trigger call to maybeReKey()");
+
+ G_Debug(z, "PASSED");
+ }
+}
diff --git a/mozilla/browser/components/safebrowsing/jar.mn b/mozilla/browser/components/safebrowsing/jar.mn
new file mode 100644
index 00000000000..3a9d6eb43cb
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/jar.mn
@@ -0,0 +1,22 @@
+browser.jar:
+# http://developer.mozilla.org/en/docs/jar.mn
+# script file included into main browser.js
++ content/browser/safebrowsing/sb-loader.js (content/sb-loader.js)
+#
+# main src files
++ content/browser/safebrowsing/application.js (content/application.js)
++ content/browser/safebrowsing/browser-view.js (content/browser-view.js)
++ content/browser/safebrowsing/controller.js (content/controller.js)
++ content/browser/safebrowsing/firefox-commands.js (content/firefox-commands.js)
++ content/browser/safebrowsing/globalstore.js (content/globalstore.js)
++ content/browser/safebrowsing/list-warden.js (content/list-warden.js)
++ content/browser/safebrowsing/phishing-afterload-displayer.js (content/phishing-afterload-displayer.js)
++ content/browser/safebrowsing/phishing-warden.js (content/phishing-warden.js)
++ content/browser/safebrowsing/reporter.js (content/reporter.js)
++ content/browser/safebrowsing/tr-fetcher.js (content/tr-fetcher.js)
+#
+# general library files
++ content/browser/safebrowsing/js/eventregistrar.js (content/js/eventregistrar.js)
++ content/browser/safebrowsing/js/listdictionary.js (content/js/listdictionary.js)
++ content/browser/safebrowsing/moz/navwatcher.js (content/moz/navwatcher.js)
++ content/browser/safebrowsing/moz/tabbedbrowserwatcher.js (content/moz/tabbedbrowserwatcher.js)
diff --git a/mozilla/browser/components/safebrowsing/src/Makefile.in b/mozilla/browser/components/safebrowsing/src/Makefile.in
new file mode 100644
index 00000000000..037ebbffb66
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/src/Makefile.in
@@ -0,0 +1,49 @@
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# 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 *****
+
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+# EXTRA_COMPONENTS installs components written in JS to dist/bin/components
+EXTRA_COMPONENTS = safebrowsingApplication.js
+
+include $(topsrcdir)/config/rules.mk
diff --git a/mozilla/browser/components/safebrowsing/src/safebrowsingApplication.js b/mozilla/browser/components/safebrowsing/src/safebrowsingApplication.js
new file mode 100644
index 00000000000..e3476b6ea6c
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/src/safebrowsingApplication.js
@@ -0,0 +1,99 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const G_GDEBUG = true;
+
+// Use subscript loader to load files. The files in ../content get mapped
+// to chrome://browser/content/safebrowsing/. Order matters if one file depends
+// on another file during initialization.
+const LIB_FILES = [
+ // some files are from toolkit
+ "chrome://global/content/url-classifier/js/lang.js",
+ "chrome://global/content/url-classifier/moz/preferences.js",
+ "chrome://global/content/url-classifier/moz/filesystem.js",
+ "chrome://global/content/url-classifier/moz/debug.js", // req js/lang.js moz/prefs.js moz/filesystem.js
+
+ "chrome://global/content/url-classifier/js/arc4.js",
+ "chrome://global/content/url-classifier/moz/alarm.js",
+ "chrome://global/content/url-classifier/moz/base64.js",
+ "chrome://global/content/url-classifier/moz/cryptohasher.js",
+ "chrome://global/content/url-classifier/moz/lang.js",
+ "chrome://global/content/url-classifier/moz/objectsafemap.js",
+ "chrome://global/content/url-classifier/moz/observer.js",
+ "chrome://global/content/url-classifier/moz/protocol4.js",
+ "chrome://global/content/url-classifier/application.js",
+ "chrome://global/content/url-classifier/url-crypto.js",
+ "chrome://global/content/url-classifier/url-crypto-key-manager.js",
+ "chrome://global/content/url-classifier/xml-fetcher.js",
+
+ // some are in browser
+ "chrome://browser/content/safebrowsing/js/eventregistrar.js",
+ "chrome://browser/content/safebrowsing/js/listdictionary.js",
+ "chrome://browser/content/safebrowsing/moz/navwatcher.js",
+ "chrome://browser/content/safebrowsing/moz/tabbedbrowserwatcher.js",
+
+ "chrome://browser/content/safebrowsing/application.js",
+ "chrome://browser/content/safebrowsing/browser-view.js",
+ "chrome://browser/content/safebrowsing/controller.js",
+ "chrome://browser/content/safebrowsing/firefox-commands.js",
+ "chrome://browser/content/safebrowsing/globalstore.js",
+ "chrome://browser/content/safebrowsing/list-warden.js",
+ "chrome://browser/content/safebrowsing/phishing-afterload-displayer.js",
+ "chrome://browser/content/safebrowsing/phishing-warden.js",
+ "chrome://browser/content/safebrowsing/reporter.js",
+ "chrome://browser/content/safebrowsing/tr-fetcher.js",
+];
+
+for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
+ //dump('*** loading subscript ' + libFile + '\n');
+ Cc["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Ci.mozIJSSubScriptLoader)
+ .loadSubScript(libFile);
+}
+
+// Module object
+function SafebrowsingApplicationMod() {
+ this.firstTime = true;
+ this.cid = Components.ID("{c64d0bcb-8270-4ca7-a0b3-3380c8ffecb5}");
+ this.progid = "@mozilla.org/safebrowsing/application;1";
+}
+
+SafebrowsingApplicationMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
+ if (this.firstTime) {
+ this.firstTime = false;
+ throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
+ }
+ compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.registerFactoryLocation(this.cid,
+ "Safebrowsing Application Module",
+ this.progid,
+ fileSpec,
+ loc,
+ type);
+};
+
+SafebrowsingApplicationMod.prototype.getClassObject = function(compMgr, cid, iid) {
+ if (!cid.equals(this.cid))
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ if (!iid.equals(Ci.nsIFactory))
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+
+ return this.factory;
+}
+
+SafebrowsingApplicationMod.prototype.canUnload = function(compMgr) {
+ return true;
+}
+
+SafebrowsingApplicationMod.prototype.factory = {
+ createInstance: function(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return new PROT_Application();
+ }
+};
+
+var ApplicationModInst = new SafebrowsingApplicationMod();
+
+function NSGetModule(compMgr, fileSpec) {
+ return ApplicationModInst;
+}
diff --git a/mozilla/browser/components/safebrowsing/tests/Makefile.in b/mozilla/browser/components/safebrowsing/tests/Makefile.in
new file mode 100644
index 00000000000..6bc0c7f69a9
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/tests/Makefile.in
@@ -0,0 +1,47 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# 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 *****
+
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
+
diff --git a/mozilla/browser/components/safebrowsing/tests/jar.mn b/mozilla/browser/components/safebrowsing/tests/jar.mn
new file mode 100644
index 00000000000..33fd75b24e8
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/tests/jar.mn
@@ -0,0 +1,2 @@
+browser.jar:
++ content/browser/protection/unittests.xul (unittests.xul)
diff --git a/mozilla/browser/components/safebrowsing/tests/unittests.xul b/mozilla/browser/components/safebrowsing/tests/unittests.xul
new file mode 100644
index 00000000000..91b94bfeae6
--- /dev/null
+++ b/mozilla/browser/components/safebrowsing/tests/unittests.xul
@@ -0,0 +1,70 @@
+
+
+
+
+
diff --git a/mozilla/browser/locales/jar.mn b/mozilla/browser/locales/jar.mn
index e1236a985f5..b205708bcb8 100644
--- a/mozilla/browser/locales/jar.mn
+++ b/mozilla/browser/locales/jar.mn
@@ -38,6 +38,9 @@
* locale/browser/bookmarks/bookmarks.properties (%chrome/browser/bookmarks/bookmarks.properties)
* locale/browser/bookmarks/bookmarksProperties.dtd (%chrome/browser/bookmarks/bookmarksProperties.dtd)
#endif
+#ifdef MOZ_SAFE_BROWSING
+ locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
+#endif
#ifdef MOZ_FEEDS
locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd)
locale/browser/feeds/options.dtd (%chrome/browser/feeds/options.dtd)
diff --git a/mozilla/browser/themes/pinstripe/browser/jar.mn b/mozilla/browser/themes/pinstripe/browser/jar.mn
index 242a30e8be4..dcbe3cc994d 100644
--- a/mozilla/browser/themes/pinstripe/browser/jar.mn
+++ b/mozilla/browser/themes/pinstripe/browser/jar.mn
@@ -64,6 +64,14 @@ classic.jar:
skin/classic/browser/bookmarks/livemark-item.png (bookmarks/livemark-item.png)
skin/classic/browser/bookmarks/folderarrow-hover.png (bookmarks/folderarrow-hover.png)
skin/classic/browser/bookmarks/folderarrow.png (bookmarks/folderarrow.png)
+#endif
+#ifdef MOZ_SAFE_BROWSING
+ skin/classic/browser/safebrowsing/browser-protection.css (safebrowsing/browser-protection.css)
+ skin/classic/browser/safebrowsing/close16x16.png (safebrowsing/close16x16.png)
+ skin/classic/browser/safebrowsing/dim.png (safebrowsing/dim.png)
+ skin/classic/browser/safebrowsing/tail.png (safebrowsing/tail.png)
+ skin/classic/browser/safebrowsing/warning16x16.png (safebrowsing/warning16x16.png)
+ skin/classic/browser/safebrowsing/warning24x24.png (safebrowsing/warning24x24.png)
#endif
skin/classic/browser/preferences/Options.png (preferences/Options.png)
skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
diff --git a/mozilla/browser/themes/pinstripe/browser/safebrowsing/browser-protection.css b/mozilla/browser/themes/pinstripe/browser/safebrowsing/browser-protection.css
new file mode 100644
index 00000000000..a42102fbafd
--- /dev/null
+++ b/mozilla/browser/themes/pinstripe/browser/safebrowsing/browser-protection.css
@@ -0,0 +1,130 @@
+#safebrowsing-palm-message {
+ display: none;
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ z-index: 2;
+}
+
+.safebrowsing-palm-message-bubble {
+ background-color: white;
+ -moz-box-sizing: border-box;
+ -moz-border-radius: 10px;
+ margin-top: -5px;
+ padding: 10px;
+ border-top: 1px solid;
+ border-left: 1px solid;
+ border-right: 2px solid;
+ border-bottom: 2px solid;
+ z-index: 1;
+}
+
+.safebrowsing-palm-fixed-width {
+ width: 400px;
+ max-width: 400px;
+}
+
+.safebrowsing-palm-title {
+ font-size: 20px;
+ font-weight: bold;
+}
+
+#safebrowsing-palm-title-icon {
+ height: 24px;
+ width: 24px;
+ max-height: 24px;
+}
+
+.safebrowsing-palm-link {
+ color: blue;
+ cursor: pointer;
+ text-decoration: underline
+}
+
+.safebrowsing-palm-paragraph {
+ padding-bottom: 2em;
+}
+
+#safebrowsing-palm-close {
+ cursor: pointer;
+ max-width: 16px !important;
+ height: 16px;
+ width: 16px;
+}
+
+#safebrowsing-palm-message-tail-container {
+ display: none;
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ border-right: 2px solid;
+ z-index: 3;
+ height: 67px;
+ margin: 0;
+ padding: 0;
+}
+
+#safebrowsing-palm-message-tail {
+ height: 67px;
+ width: 24px;
+ max-width: 24px;
+ z-index: 3;
+}
+
+#safebrowsing-palm-message-titlebox {
+ padding-bottom: 5px;
+ padding-left: 10px;
+}
+
+#safebrowsing-palm-message-title {
+ padding-left: 5px;
+}
+
+#safebrowsing-palm-message-actionbox {
+ padding-top: 10px;
+ padding-bottom: 15px;
+}
+
+.safebrowsing-palm-bigtext {
+ font-size: 14px;
+ font-weight: bold;
+}
+
+.safebrowsing-palm-smalltext {
+ font-size: 10px;
+}
+
+#safebrowsing-palm-google-logo {
+ height: 32px;
+ max-height: 32px;
+ width: 78px;
+}
+
+#safebrowsing-dim-area-canvas {
+ background-image: url("chrome://browser/skin/safebrowsing/dim.png");
+ height: 0px;
+}
+
+#safebrowsing-dim-area-transp {
+ background-image: url("chrome://browser/skin/safebrowsing/dim.png");
+ height: 0px;
+ left: 0px;
+ top: 0px;
+ position: fixed;
+ display: none;
+}
+
+#safebrowsing-pref-caption {
+ background-color: #2222EE;
+}
+
+#safebrowsing-pref-caption text {
+ text-align: center;
+ color: #FFFFFF;
+ font-size: 150%;
+ font-family: arial, helvetica, sans-serif;
+}
+
+#safebrowsing-urlbar-icon {
+ padding-right: 2px;
+}
diff --git a/mozilla/browser/themes/pinstripe/browser/safebrowsing/close16x16.png b/mozilla/browser/themes/pinstripe/browser/safebrowsing/close16x16.png
new file mode 100644
index 00000000000..abf7f386f8b
Binary files /dev/null and b/mozilla/browser/themes/pinstripe/browser/safebrowsing/close16x16.png differ
diff --git a/mozilla/browser/themes/pinstripe/browser/safebrowsing/dim.png b/mozilla/browser/themes/pinstripe/browser/safebrowsing/dim.png
new file mode 100644
index 00000000000..9e458d0563f
Binary files /dev/null and b/mozilla/browser/themes/pinstripe/browser/safebrowsing/dim.png differ
diff --git a/mozilla/browser/themes/pinstripe/browser/safebrowsing/tail.png b/mozilla/browser/themes/pinstripe/browser/safebrowsing/tail.png
new file mode 100644
index 00000000000..a973739885f
Binary files /dev/null and b/mozilla/browser/themes/pinstripe/browser/safebrowsing/tail.png differ
diff --git a/mozilla/browser/themes/pinstripe/browser/safebrowsing/warning16x16.png b/mozilla/browser/themes/pinstripe/browser/safebrowsing/warning16x16.png
new file mode 100644
index 00000000000..53f65e09fee
Binary files /dev/null and b/mozilla/browser/themes/pinstripe/browser/safebrowsing/warning16x16.png differ
diff --git a/mozilla/browser/themes/pinstripe/browser/safebrowsing/warning24x24.png b/mozilla/browser/themes/pinstripe/browser/safebrowsing/warning24x24.png
new file mode 100644
index 00000000000..274fc131b65
Binary files /dev/null and b/mozilla/browser/themes/pinstripe/browser/safebrowsing/warning24x24.png differ
diff --git a/mozilla/browser/themes/winstripe/browser/jar.mn b/mozilla/browser/themes/winstripe/browser/jar.mn
index d98ffc87c12..baa247b308f 100644
--- a/mozilla/browser/themes/winstripe/browser/jar.mn
+++ b/mozilla/browser/themes/winstripe/browser/jar.mn
@@ -40,6 +40,14 @@ classic.jar:
skin/classic/browser/bookmarks/addBookmark.css (bookmarks/addBookmark.css)
skin/classic/browser/bookmarks/bookmarksManager.css (bookmarks/bookmarksManager.css)
skin/classic/browser/bookmarks/Bookmarks-toolbar.png (bookmarks/Bookmarks-toolbar.png)
+#endif
+#ifdef MOZ_SAFE_BROWSING
+ skin/classic/browser/safebrowsing/browser-protection.css (safebrowsing/browser-protection.css)
+ skin/classic/browser/safebrowsing/close16x16.png (safebrowsing/close16x16.png)
+ skin/classic/browser/safebrowsing/dim.png (safebrowsing/dim.png)
+ skin/classic/browser/safebrowsing/tail.png (safebrowsing/tail.png)
+ skin/classic/browser/safebrowsing/warning16x16.png (safebrowsing/warning16x16.png)
+ skin/classic/browser/safebrowsing/warning24x24.png (safebrowsing/warning24x24.png)
#endif
skin/classic/browser/preferences/Options.png (preferences/Options.png)
skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
diff --git a/mozilla/browser/themes/winstripe/browser/safebrowsing/browser-protection.css b/mozilla/browser/themes/winstripe/browser/safebrowsing/browser-protection.css
new file mode 100644
index 00000000000..a42102fbafd
--- /dev/null
+++ b/mozilla/browser/themes/winstripe/browser/safebrowsing/browser-protection.css
@@ -0,0 +1,130 @@
+#safebrowsing-palm-message {
+ display: none;
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ z-index: 2;
+}
+
+.safebrowsing-palm-message-bubble {
+ background-color: white;
+ -moz-box-sizing: border-box;
+ -moz-border-radius: 10px;
+ margin-top: -5px;
+ padding: 10px;
+ border-top: 1px solid;
+ border-left: 1px solid;
+ border-right: 2px solid;
+ border-bottom: 2px solid;
+ z-index: 1;
+}
+
+.safebrowsing-palm-fixed-width {
+ width: 400px;
+ max-width: 400px;
+}
+
+.safebrowsing-palm-title {
+ font-size: 20px;
+ font-weight: bold;
+}
+
+#safebrowsing-palm-title-icon {
+ height: 24px;
+ width: 24px;
+ max-height: 24px;
+}
+
+.safebrowsing-palm-link {
+ color: blue;
+ cursor: pointer;
+ text-decoration: underline
+}
+
+.safebrowsing-palm-paragraph {
+ padding-bottom: 2em;
+}
+
+#safebrowsing-palm-close {
+ cursor: pointer;
+ max-width: 16px !important;
+ height: 16px;
+ width: 16px;
+}
+
+#safebrowsing-palm-message-tail-container {
+ display: none;
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ border-right: 2px solid;
+ z-index: 3;
+ height: 67px;
+ margin: 0;
+ padding: 0;
+}
+
+#safebrowsing-palm-message-tail {
+ height: 67px;
+ width: 24px;
+ max-width: 24px;
+ z-index: 3;
+}
+
+#safebrowsing-palm-message-titlebox {
+ padding-bottom: 5px;
+ padding-left: 10px;
+}
+
+#safebrowsing-palm-message-title {
+ padding-left: 5px;
+}
+
+#safebrowsing-palm-message-actionbox {
+ padding-top: 10px;
+ padding-bottom: 15px;
+}
+
+.safebrowsing-palm-bigtext {
+ font-size: 14px;
+ font-weight: bold;
+}
+
+.safebrowsing-palm-smalltext {
+ font-size: 10px;
+}
+
+#safebrowsing-palm-google-logo {
+ height: 32px;
+ max-height: 32px;
+ width: 78px;
+}
+
+#safebrowsing-dim-area-canvas {
+ background-image: url("chrome://browser/skin/safebrowsing/dim.png");
+ height: 0px;
+}
+
+#safebrowsing-dim-area-transp {
+ background-image: url("chrome://browser/skin/safebrowsing/dim.png");
+ height: 0px;
+ left: 0px;
+ top: 0px;
+ position: fixed;
+ display: none;
+}
+
+#safebrowsing-pref-caption {
+ background-color: #2222EE;
+}
+
+#safebrowsing-pref-caption text {
+ text-align: center;
+ color: #FFFFFF;
+ font-size: 150%;
+ font-family: arial, helvetica, sans-serif;
+}
+
+#safebrowsing-urlbar-icon {
+ padding-right: 2px;
+}
diff --git a/mozilla/browser/themes/winstripe/browser/safebrowsing/close16x16.png b/mozilla/browser/themes/winstripe/browser/safebrowsing/close16x16.png
new file mode 100644
index 00000000000..abf7f386f8b
Binary files /dev/null and b/mozilla/browser/themes/winstripe/browser/safebrowsing/close16x16.png differ
diff --git a/mozilla/browser/themes/winstripe/browser/safebrowsing/dim.png b/mozilla/browser/themes/winstripe/browser/safebrowsing/dim.png
new file mode 100644
index 00000000000..9e458d0563f
Binary files /dev/null and b/mozilla/browser/themes/winstripe/browser/safebrowsing/dim.png differ
diff --git a/mozilla/browser/themes/winstripe/browser/safebrowsing/tail.png b/mozilla/browser/themes/winstripe/browser/safebrowsing/tail.png
new file mode 100644
index 00000000000..a973739885f
Binary files /dev/null and b/mozilla/browser/themes/winstripe/browser/safebrowsing/tail.png differ
diff --git a/mozilla/browser/themes/winstripe/browser/safebrowsing/warning16x16.png b/mozilla/browser/themes/winstripe/browser/safebrowsing/warning16x16.png
new file mode 100644
index 00000000000..53f65e09fee
Binary files /dev/null and b/mozilla/browser/themes/winstripe/browser/safebrowsing/warning16x16.png differ
diff --git a/mozilla/browser/themes/winstripe/browser/safebrowsing/warning24x24.png b/mozilla/browser/themes/winstripe/browser/safebrowsing/warning24x24.png
new file mode 100644
index 00000000000..274fc131b65
Binary files /dev/null and b/mozilla/browser/themes/winstripe/browser/safebrowsing/warning24x24.png differ
diff --git a/mozilla/toolkit/components/Makefile.in b/mozilla/toolkit/components/Makefile.in
index 483046ea86d..746c44ed4f2 100644
--- a/mozilla/toolkit/components/Makefile.in
+++ b/mozilla/toolkit/components/Makefile.in
@@ -77,6 +77,10 @@ ifdef MOZ_XPINSTALL
DIRS += downloads
endif
+ifdef MOZ_URL_CLASSIFIER
+DIRS += url-classifier
+endif
+
DIRS += \
commandlines \
startup \
@@ -87,8 +91,4 @@ EXTRA_PP_COMPONENTS = nsDefaultCLH.js
endif # MOZ_XUL_APP
-ifdef MOZ_URL_CLASSIFIER
-DIRS += url-classifier
-endif
-
include $(topsrcdir)/config/rules.mk
diff --git a/mozilla/toolkit/components/url-classifier/content/application.js b/mozilla/toolkit/components/url-classifier/content/application.js
index 95775604f14..e3a09f4a7d2 100644
--- a/mozilla/toolkit/components/url-classifier/content/application.js
+++ b/mozilla/toolkit/components/url-classifier/content/application.js
@@ -34,6 +34,8 @@
*
* ***** END LICENSE BLOCK ***** */
+// TODO: We don't use this class very much. Try to use native nsIFile instead
+// and remove this file.
/**
* A simple helper class that enables us to get or create the
@@ -42,7 +44,6 @@
function PROT_ApplicationDirectory() {
this.debugZone = "appdir";
this.appDir_ = G_File.getProfileFile();
- this.appDir_.append(PROT_GlobalStore.getAppDirectoryName());
G_Debug(this, "Application directory is " + this.appDir_.path);
}
diff --git a/mozilla/toolkit/components/url-classifier/content/listmanager.js b/mozilla/toolkit/components/url-classifier/content/listmanager.js
index 3bd063acfd1..4368b706f4a 100644
--- a/mozilla/toolkit/components/url-classifier/content/listmanager.js
+++ b/mozilla/toolkit/components/url-classifier/content/listmanager.js
@@ -67,11 +67,23 @@
// of the list comprise its name, so the listmanager should easily
// be able to figure out what to do with what list (i.e., no
// need for step 4).
-// TODO reading, writing, and whatnot code should be cleaned up
-// TODO read/write asynchronously
// TODO more comprehensive update tests, for example add unittest check
// that the listmanagers tables are properly written on updates
+/**
+ * The base pref name for where we keep table version numbers.
+ * We add append the table name to this and set the value to
+ * the version. E.g., tableversion.goog-black-enchash may have
+ * a value of 1.1234.
+ */
+const kTableVersionPrefPrefix = "urlclassifier.tableversion.";
+
+/**
+ * Pref name for the update server.
+ * TODO: Add support for multiple providers.
+ */
+const kUpdateServerUrl = "urlclassifier.provider.0.updateURL";
+
/**
* A ListManager keeps track of black and white lists and knows
@@ -83,46 +95,23 @@ function PROT_ListManager() {
this.debugZone = "listmanager";
G_debugService.enableZone(this.debugZone);
- // We run long-lived operations on "threads" (user-level
- // time-slices) in order to prevent locking up the UI
- var threadConfig = {
- "interleave": true,
- "runtime": 80,
- "delay": 400,
- };
- this.threadQueue_ = new TH_ThreadQueue(threadConfig);
- this.appDir_ = null;
- this.initAppDir_(); // appDir can be changed with setAppDir
this.currentUpdateChecker_ = null; // set when we toggle updates
this.rpcPending_ = false;
- this.readingData_ = false;
+ this.prefs_ = new G_Preferences();
- // We don't want to start checking against our lists until we've
- // read them. But then again, we don't want to queue URLs to check
- // forever. So if we haven't successfully read our lists after a
- // certain amount of time, just pretend.
- this.dataImportedAtLeastOnce_ = false;
- var self = this;
- new G_Alarm(function() { self.dataImportedAtLeastOnce_ = true; }, 60 * 1000);
-
- // TOOD(tc): PREF
- this.updateserverURL_ = PROT_GlobalStore.getUpdateserverURL();
+ this.updateserverURL_ = this.prefs_.getPref(kUpdateServerUrl, null);
// The lists we know about and the parses we can use to read
// them. Default all to the earlies possible version (1.-1); this
// version will get updated when successfully read from disk or
// fetch updates.
this.tablesKnown_ = {};
+ this.isTesting_ = false;
- if (PROT_GlobalStore.isTesting()) {
+ if (this.isTesting_) {
// populate with some tables for unittesting
this.tablesKnown_ = {
- // "goog-white-domain": new PROT_VersionParser("goog-white-domain", 1, -1),
- // "goog-black-url" : new PROT_VersionParser("goog-black-url", 1, -1),
- // "goog-black-enchash": new PROT_VersionParser("goog-black-enchash", 1, -1),
- // "goog-white-url": new PROT_VersionParser("goog-white-url", 1, -1),
- //
- // A major version of zero means local, so don't ask for updates
+ // A major version of zero means local, so don't ask for updates
"test1-foo-domain" : new PROT_VersionParser("test1-foo-domain", 0, -1),
"test2-foo-domain" : new PROT_VersionParser("test2-foo-domain", 0, -1),
"test-white-domain" :
@@ -137,6 +126,7 @@ function PROT_ListManager() {
this.tablesData = {};
+ // Lazily create urlCrypto (see tr-fetcher.js)
this.urlCrypto_ = null;
}
@@ -152,6 +142,7 @@ PROT_ListManager.prototype.registerTable = function(tableName,
if (!table)
return false;
this.tablesKnown_[tableName] = table;
+ this.tablesData[tableName] = newUrlClassifierTable(tableName);
return true;
}
@@ -214,7 +205,7 @@ PROT_ListManager.prototype.requireTableUpdates = function() {
* (where we store the lists) might not be available.
*/
PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
- if (PROT_GlobalStore.isTesting())
+ if (this.isTesting_)
return;
// We might have been told about tables already, so see if we should be
@@ -229,16 +220,14 @@ PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
// If we are testing or dont have an application directory yet, we should
// not start reading tables from disk or schedule remote updates
- if (PROT_GlobalStore.isTesting() || !this.appDir_)
+ if (this.isTesting_)
return;
// We update tables if we have some tables that want updates. If there
// are no tables that want to be updated - we dont need to check anything.
if (this.requireTableUpdates() === true) {
G_Debug(this, "Starting managing lists");
- // Read new table data if necessary - if we already have data we just
- // skip reading from disk
- new G_Alarm(BindToObject(this.readDataFiles, this), 0);
+ this.loadTableVersions_();
// Multiple warden can ask us to reenable updates at the same time, but we
// really just need to schedule a single update.
if (!this.currentUpdateChecker_)
@@ -274,45 +263,6 @@ PROT_ListManager.prototype.stopUpdateChecker = function() {
}
}
-/**
- *
- */
-PROT_ListManager.prototype.initAppDir_ = function() {
- var dir = G_File.getProfileFile();
- dir.append(PROT_GlobalStore.getAppDirectoryName());
- if (!dir.exists()) {
- try {
- dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
- } catch (e) {
- G_Error(this, dir.path + " couldn't be created.");
- }
- }
- this.setAppDir(dir);
-}
-
-/**
- * Set the directory in which we should serialize data
- *
- * @param appDir An nsIFile pointing to our directory (should exist)
- */
-PROT_ListManager.prototype.setAppDir = function(appDir) {
- G_Debug(this, "setAppDir: " + appDir.path);
- this.appDir_ = appDir;
-}
-
-/**
- * Clears the specified table
- * @param table Name of the table that we want to consult
- * @returns true if the table exists, false otherwise
- */
-PROT_ListManager.prototype.clearList = function(table) {
- if (!this.tablesKnown_[table])
- return false;
-
- this.tablesData[table] = newUrlClassifierTable(this.tablesKnown_[table].type);
- return true;
-}
-
/**
* Provides an exception free way to look up the data in a table. We
* use this because at certain points our tables might not be loaded,
@@ -379,112 +329,62 @@ PROT_ListManager.prototype.safeRemove = function(table, key) {
return this.tablesData[table].remove(key);
}
-PROT_ListManager.prototype.getTable = function(tableName) {
- return this.tablesData[tableName];
+/**
+ * We store table versions in user prefs. This method pulls the values out of
+ * the user prefs and into the tablesKnown objects.
+ */
+PROT_ListManager.prototype.loadTableVersions_ = function() {
+ // Pull values out of prefs.
+ var prefBase = kTableVersionPrefPrefix;
+ for (var table in this.tablesKnown_) {
+ var version = this.prefs_.getPref(prefBase + table, "1.-1");
+ G_Debug(this, "loadTableVersion " + table + ": " + version);
+ var tokens = version.split(".");
+ G_Assert(this, tokens.length == 2, "invalid version number");
+
+ this.tablesKnown_[table].major = tokens[0];
+ this.tablesKnown_[table].minor = tokens[1];
+ }
}
/**
- * Returns a list of files that we should try to read. We do not return
- * tables that already have data or that we tried to read in the past.
- * @returns array of file names
+ * Get lines of the form "[goog-black-enchash 1.1234]" or
+ * "[goog-white-url 1.1234 update]" and set the version numbers in the user
+ * pref.
+ * @param updateString String containing the response from the server.
+ * @return Array a list of table names
*/
+PROT_ListManager.prototype.setTableVersions_ = function(updateString) {
+ var updatedTables = [];
-PROT_ListManager.prototype.listUnreadDataFiles = function() {
- // Now we need to read all of our nice data files.
- var files = [];
- for (var type in this.tablesKnown_) {
- var table = this.tablesKnown_[type];
- // Do not read data for tables that we already know about
- if (this.tablesData[type] || table.didRead === true)
- continue;
+ var prefBase = kTableVersionPrefPrefix;
+ var startPos = updateString.indexOf('[');
+ var endPos;
+ while (startPos != -1) {
+ // [ needs to be the start of a new line
+ if (0 == startPos || ('\n' == updateString[startPos - 1] &&
+ '\n' == updateString[startPos - 2])) {
+ endPos = updateString.indexOf('\n', startPos);
+ if (endPos != -1) {
+ var line = updateString.substring(startPos, endPos);
+ var versionParser = new PROT_VersionParser("dummy");
- // Tables that dont require updates are not read from disk either
- if (table.needsUpdate === false)
- continue;
+ if (versionParser.fromString(line)) {
+ var tableName = versionParser.type;
+ var version = versionParser.major + '.' + versionParser.minor;
+ G_Debug(this, "Set table version for " + tableName + ": " + version);
+ this.prefs_.setPref(prefBase + tableName, version);
+ this.tablesKnown_[tableName].ImportVersion(versionParser);
- var filename = type + ".sst";
- var file = this.appDir_.clone();
- file.append(filename);
- if (file.exists() && file.isFile() && file.isReadable()) {
- G_Debug(this, "Found saved data for: " + type);
- files.push(file);
- } else {
- G_Debug(this, "Failed to find saved data for: " + type);
+ updatedTables.push(tableName);
+ }
+ }
}
+ // This could catch option params, but that's ok. The double newline
+ // check will skip over it.
+ startPos = updateString.indexOf('[', startPos + 1);
}
-
- return files;
-}
-
-/**
- * Reads all data files from storage
- * @returns true if we started reading data from disk, false otherwise.
- */
-PROT_ListManager.prototype.readDataFiles = function() {
- if (this.readingData_ === true) {
- G_Debug(this, "Already reading data from disk");
- return true;
- }
-
- if (this.rpcPending_ === true) {
- G_Debug(this, "Cannot read data files while an update RPC is pending");
- new G_Alarm(BindToObject(this.readDataFiles, this), 10 * 1000);
- return false;
- }
-
- // If we have no files there is nothing more for us todo
- var files = this.listUnreadDataFiles();
- if (!files.length)
- return true;
-
- this.readingData_ = true;
-
- // Remember that we attempted to read from all tables
- for (var type in this.tablesKnown_)
- this.tablesKnown_[type].didRead = true;
-
- // TODO: Should probably break this up on a thread
- var data = "";
- for (var i = 0; i < files.length; ++i) {
- G_Debug(this, "Trying to read: " + files[i].path);
- var gFile = new G_FileReader(files[i]);
- data += gFile.read() + "\n";
- gFile.close();
- }
-
- this.deserialize_(data, BindToObject(this.dataFromDisk, this));
- return true;
-}
-
-/**
- * Creates a WireFormatReader and calls deserialize on it.
- */
-PROT_ListManager.prototype.deserialize_ = function(data, callback) {
- var wfr = new PROT_WireFormatReader(this.threadQueue_, this.tablesData);
- wfr.deserialize(data, callback);
-}
-
-/**
- * A callback that is executed when we have read our table data from
- * disk.
- *
- * @param tablesKnown An array that maps table name to current version
- * @param tablesData An array that maps a table name to a Map which
- * contains key value pairs.
- */
-PROT_ListManager.prototype.dataFromDisk = function(tablesKnown, tablesData) {
- G_Debug(this, "Called dataFromDisk");
-
- this.importData_(tablesKnown, tablesData);
-
- this.readingData_ = false;
-
- // If we have more files to read schedule another round of reading
- var files = this.listUnreadDataFiles();
- if (files.length)
- new G_Alarm(BindToObject(this.readDataFiles, this), 0);
-
- return true;
+ return updatedTables;
}
/**
@@ -500,7 +400,7 @@ PROT_ListManager.prototype.getRequestURL_ = function(url) {
var firstElement = true;
var requestMac = false;
- for(var type in this.tablesKnown_) {
+ for (var type in this.tablesKnown_) {
// All tables with a major of 0 are internal tables that we never
// update remotely.
if (this.tablesKnown_[type].major == 0)
@@ -551,14 +451,10 @@ PROT_ListManager.prototype.checkForUpdates = function() {
return false;
}
- if (this.readingData_) {
- G_Debug(this, 'checkForUpdate: still reading data from disk...');
-
- // Reschedule the update to happen in a little while
- new G_Alarm(BindToObject(this.checkForUpdates, this), 500);
+ if (!this.updateserverURL_) {
+ G_Debug(this, 'checkForUpdates: no update server url');
return false;
}
-
G_Debug(this, 'checkForUpdates: scheduling request..');
this.rpcPending_ = true;
this.xmlFetcher_ = new PROT_XMLFetcher();
@@ -574,170 +470,110 @@ PROT_ListManager.prototype.checkForUpdates = function() {
*/
PROT_ListManager.prototype.rpcDone = function(data) {
G_Debug(this, "Called rpcDone");
- /* Runs in a thread and executes the callback when ready */
- this.deserialize_(data, BindToObject(this.dataReady, this));
-
+ this.rpcPending_ = false;
this.xmlFetcher_ = null;
+
+ if (!data || !data.length) {
+ G_Debug(this, "No data. Returning");
+ return;
+ }
+
+ // Only use the the data if the mac matches.
+ data = this.checkMac_(data);
+
+ if (data.length == 0) {
+ return;
+ }
+
+ // List updates (local lists) don't work yet. See bug 336203.
+ throw Exception("dbservice not yet implemented.");
+
+ var dbUpdateSrv = Cc["@mozilla.org/url-classifier/dbservice;1"]
+ .getService(Ci.nsIUrlClassifierDBService);
+
+ // Update the tables on disk.
+ try {
+ dbUpdateSrv.updateTables(data);
+ } catch (e) {
+ // dbUpdateSrv will throw an error if the background thread is already
+ // working. In this case, we just wait for the next scheduled update.
+ G_Debug(this, "Skipping update, write thread busy.");
+ return;
+ }
+
+ // While the update is being processed by a background thread, we need
+ // to also update the table versions.
+ var tableNames = this.setTableVersions_(data);
+ G_Debug(this, "Updated tables: " + tableNames);
+
+ for (var t = 0, name = null; name = tableNames[t]; ++t) {
+ // Create the table object if it doesn't exist.
+ if (!this.tablesData[name])
+ this.tablesData[name] = newUrlClassifierTable(name);
+ }
}
/**
- * @returns Boolean indicating if it has read data from somewhere (e.g.,
- * disk)
- */
-PROT_ListManager.prototype.hasData = function() {
- return !!this.dataImportedAtLeastOnce_;
-}
-
-/**
- * Check if the mac passed, if required.
+ * Given the server response, extract out the new table lines and table
+ * version numbers. If the table has a mac, also check to see if it matches
+ * the data.
*
- * @param oldParser The current version parser for the table
- * @param newParser A version parser returned by the WireFormatReader
- *
- * @returns true if no mac is required, or if it is and the newParser says it
- * didn't fail, false otherwise
+ * @param data String update string from the server
+ * @return String The same update string sans tables with invalid macs.
*/
-PROT_ListManager.prototype.maybeCheckMac = function(oldParser, newParser) {
- if (!oldParser.requireMac)
- return true;
+PROT_ListManager.prototype.checkMac_ = function(data) {
+ var dataTables = data.split('\n\n');
+ var returnString = "";
- if (newParser.mac && !newParser.macFailed)
- return true;
+ for (var table = null, t = 0; table = dataTables[t]; ++t) {
+ var firstLineEnd = table.indexOf("\n");
+ while (firstLineEnd == 0) {
+ // Skip leading blank lines
+ table = table.substring(1);
+ firstLineEnd = table.indexOf("\n");
+ }
+ if (firstLineEnd == -1) {
+ continue;
+ }
- G_Debug(this, "mac required and it failed");
- return false;
-}
+ var versionLine = table.substring(0, firstLineEnd);
+ var versionParser = new PROT_VersionParser("dummy");
+ if (!versionParser.fromString(versionLine)) {
+ // Failed to parse the version string, skip this table.
+ G_Debug(this, "Failed to parse version string");
+ continue;
+ }
-/**
- * We've deserialized tables from disk or the update server, now let's
- * swap them into the tables we're currently using.
- *
- * @param tablesKnown An array that maps table name to current version
- * @param tablesData An array that maps a table name to a Map which
- * contains key value pairs.
- * @returns Array of strings holding the names of tables we updated
- */
-PROT_ListManager.prototype.importData_ = function(tablesKnown, tablesData) {
- this.dataImportedAtLeastOnce_ = true;
+ if (versionParser.mac && versionParser.macval.length > 0) {
+ // Includes a mac, so we check it.
+ var updateData = table.substring(firstLineEnd + 1) + '\n';
+ if (!this.urlCrypto_)
+ this.urlCrypto_ = new PROT_UrlCrypto();
- var changes = [];
-
- // If our data has changed, update it
- if (tablesKnown && tablesData) {
- // Update our tables with the new data
- for (var name in tablesKnown) {
- // WireFormatReader constructs VersionParsers from scratch and doesn't
- // know about the requireMac flag, so check it now
- if (tablesKnown[name] && this.tablesKnown_[name] &&
- this.tablesKnown_[name] != tablesKnown[name] &&
- this.maybeCheckMac(this.tablesKnown_[name], tablesKnown[name])) {
- changes.push(name);
-
- this.tablesKnown_[name].ImportVersion(tablesKnown[name]);
- this.tablesData[name] = tablesData[name];
+ var computedMac = this.urlCrypto_.computeMac(updateData);
+ if (computedMac != versionParser.macval) {
+ G_Debug(this, "mac doesn't match: " + computedMac + " != " +
+ versionParser.macval)
+ continue;
+ }
+ } else {
+ // No mac in the return. Check to see if it's required. If it is
+ // required, skip this data.
+ if (this.tablesKnown_[versionParser.type] &&
+ this.tablesKnown_[versionParser.type].requireMac) {
+ continue;
}
}
+
+ // Everything looks ok, add it to the return string.
+ returnString += table + "\n\n";
}
- return changes;
-}
-
-/**
- * A callback that is executed when we have updated the tables from
- * the server. We are provided with the new table versions and the
- * corresponding data.
- *
- * @param tablesKnown An array that maps table name to current version
- * @param tablesData An array that maps a table name to a Map which
- * contains key value pairs.
- */
-PROT_ListManager.prototype.dataReady = function(tablesKnown, tablesData) {
- G_Debug(this, "Called dataReady");
-
- // First, replace the current tables we're using
- var changes = this.importData_(tablesKnown, tablesData);
-
- // Then serialize the new tables to disk
- if (changes.length) {
- G_Debug(this, "Committing " + changes.length + " changed tables to disk.");
- for (var i = 0; i < changes.length; i++) {
- this.storeTable(changes[i],
- this.tablesData[changes[i]],
- this.tablesKnown_[changes[i]]);
- }
- }
-
- this.rpcPending_ = false; // todo maybe can do away cuz asynch
- G_Debug(this, "Done writing data to disk.");
-}
-
-/**
- * Serialize a table to disk.
- *
- * @param tableName String holding the name of the table to serialize
- *
- * @param opt_table Reference to the Map holding the table (if omitted,
- * we look the table up)
- *
- * @param opt_parser Reference to the versionparser for this table (if
- * omitted we look the table up)
- */
-PROT_ListManager.prototype.storeTable = function(tableName,
- opt_table,
- opt_parser) {
-
- var table = opt_table ? opt_table : this.tablesData[tableName];
- var parser = opt_parser ? opt_parser : this.tablesKnown_[tableName];
-
- if (!table || ! parser)
- G_Error(this, "Tried to serialize a non-existent table: " + tableName);
-
- var wfw = new PROT_WireFormatWriter(this.threadQueue_);
- wfw.serialize(table, parser, BindToObject(this.writeDataFile,
- this,
- tableName));
-}
-
-/**
- * Takes a serialized table and writes it into our application directory.
- *
- * @param tableName String containing the name of the table, used to
- * create the filename
- *
- * @param tableData Serialized version of the table
- */
-PROT_ListManager.prototype.writeDataFile = function(tableName, tableData) {
- var filename = tableName + ".sst";
-
- G_Debug(this, "Serializing to " + filename);
-
- try {
- var tmpFile = G_File.createUniqueTempFile(filename);
- var tmpFileWriter = new G_FileWriter(tmpFile);
-
- tmpFileWriter.write(tableData);
- tmpFileWriter.close();
-
- } catch(e) {
- G_Debug(this, e);
- G_Error(this, "Couldn't write to temp file: " + filename);
- }
-
- // Now overwrite!
- try {
- tmpFile.moveTo(this.appDir_, filename);
- } catch(e) {
- G_Debug(this, e);
- G_Error(this, "Couldn't overwrite existing table: " +
- tmpFile.path + ', ' +
- this.appDir_.path + ', ' + filename);
- tmpFile.remove(false /* not recursive */);
- }
- G_Debug(this, "Serializing to " + filename + " finished.");
+ return returnString;
}
PROT_ListManager.prototype.QueryInterface = function(iid) {
- if (iid.equals(Components.interfaces.nsISample) ||
+ if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIUrlListManager))
return this;
@@ -750,7 +586,7 @@ PROT_ListManager.prototype.QueryInterface = function(iid) {
// provider_name-semantic_type-table_type. For example, goog-white-enchash
// or goog-black-url.
function newUrlClassifierTable(name) {
- G_Debug("protfactory", "Trying to create a new nsIUrlClassifierTable: " + name);
+ G_Debug("protfactory", "Creating a new nsIUrlClassifierTable: " + name);
var tokens = name.split('-');
var type = tokens[2];
var table = Cc['@mozilla.org/url-classifier/table;1?type=' + type]
diff --git a/mozilla/toolkit/components/url-classifier/content/url-crypto-key-manager.js b/mozilla/toolkit/components/url-classifier/content/url-crypto-key-manager.js
index 459d4b41b01..9f9f7361a30 100644
--- a/mozilla/toolkit/components/url-classifier/content/url-crypto-key-manager.js
+++ b/mozilla/toolkit/components/url-classifier/content/url-crypto-key-manager.js
@@ -64,6 +64,12 @@
// the client to re-key. The client will issue a new HTTPS getkey request
// at this time if it has only issued one before
+// We store the user key in this file. The key can be used to verify signed
+// server updates.
+const kKeyFilename = "kf.txt";
+
+// If we don't have a key, we can get one at this url.
+const kGetKeyUrl = "safebrowsing.provider.0.keyURL";
/**
* A key manager for UrlCrypto. There should be exactly one of these
@@ -94,8 +100,7 @@ function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
this.rekeyTries_ = 0;
this.keyFilename_ = opt_keyFilename ?
- opt_keyFilename :
- PROT_GlobalStore.getKeyFilename();
+ opt_keyFilename : kKeyFilename;
// Convenience properties
this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
@@ -154,8 +159,9 @@ PROT_UrlCryptoKeyManager.prototype.reKey = function() {
this.rekeyTries_++;
G_Debug(this, "Attempting to re-key");
- var url = PROT_GlobalStore.getGetKeyURL();
- if (!this.testing_)
+ var prefs = new G_Preferences();
+ var url = prefs.getPref(kGetKeyUrl, null);
+ if (!this.testing_ && url)
(new PROT_XMLFetcher()).get(url,
BindToObject(this.onGetKeyResponse, this));
}
diff --git a/mozilla/toolkit/components/url-classifier/content/wireformat.js b/mozilla/toolkit/components/url-classifier/content/wireformat.js
index fcf90cec55d..2d064495a22 100644
--- a/mozilla/toolkit/components/url-classifier/content/wireformat.js
+++ b/mozilla/toolkit/components/url-classifier/content/wireformat.js
@@ -84,6 +84,7 @@ function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) {
this.requireMac = !!opt_requireMac;
this.update = false;
+ this.needsUpdate = false; // used by ListManager to determine update policy
// Used by ListerManager to see if we have read data for this table from
// disk. Once we read a table from disk, we are not going to do so again
// but instead update remotely if necessary.
@@ -115,8 +116,6 @@ PROT_VersionParser.prototype.ImportVersion = function(version) {
*/
PROT_VersionParser.prototype.toString = function() {
var s = "[" + this.type + " " + this.major + "." + this.minor + "]";
- if (this.mac)
- s += "[mac=" + this.macval + "]";
return s;
}
@@ -157,10 +156,9 @@ PROT_VersionParser.prototype.processOldFormat_ = function(line) {
return true;
}
-/**
- * Takes a string like [name-of-table 1.1] and figures out the type
- * and corresponding version numbers.
- *
+/**
+ * Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the
+ * type and corresponding version numbers.
* @returns true if the string could be parsed, false otherwise
*/
PROT_VersionParser.prototype.fromString = function(line) {
@@ -212,7 +210,7 @@ PROT_VersionParser.prototype.processOptTokens_ = function(line) {
this.mac = true;
if (tokenparts.length < 2) {
G_Debug(this, "Found mac flag but not mac value!");
- return false;
+ return false;
}
// The mac value may have "=" in it, so we can't just use tokenparts[1].
// Instead, just take the rest of tokens[i] after the first "="
@@ -227,550 +225,27 @@ PROT_VersionParser.prototype.processOptTokens_ = function(line) {
return true;
}
-
-/**
- * A WireFormatWriter can serialize table data
- *
- * @param threadQueue A thread queue we should run on
- *
- * @constructor
- */
-function PROT_WireFormatWriter(threadQueue) {
- this.threadQueue_ = threadQueue;
- this.debugZone = "wireformatwriter";
-}
-
-/**
- * Serializes a table to a string.
- *
- * @param tableData Reference to the table data we should serialize
- *
- * @param vParser Reference to the version parser/unparser we should use
- *
- * @param callback Reference to a function we should call when complete.
- * The callback will be invoked with a string holding
- * the serialized data as an argument
- *
- * @returns False if the serializer is busy, else true
- *
- */
-PROT_WireFormatWriter.prototype.serialize = function(tableData,
- vParser,
- callback) {
- if (this.callback_) {
- G_Debug(this, "Serializer busy");
- return false;
- }
-
- this.callback_ = callback;
- this.current_ = 0;
- this.serialized_ = vParser.toString() + "\n";
- var cnt = {};
- this.keyList_ = tableData.getKeys(cnt);
- this.tableData_ = tableData;
-
- this.threadQueue_.addWorker(BindToObject(this.doWorkUnit, this),
- BindToObject(this.isComplete, this));
- this.threadQueue_.run();
-
- return true;
-}
-
-/**
- * Serialize a chunk of data. This is periodically invoked by the
- * threadqueue.
- */
-PROT_WireFormatWriter.prototype.doWorkUnit = function() {
- for (var i = 0; i < 10 && this.current_ < this.keyList_.length; i++) {
- var key = this.keyList_[this.current_];
- if (key) {
- var value = this.tableData_.findValue(key);
- this.serialized_ += "+" + key + "\t" + value + "\n";
- }
- this.current_++;
- }
-
- if (this.isComplete())
- this.onComplete();
-}
-
-/**
- * Are we done serializing? Called by the threadqueue to tell when
- * to stop running this thread.
- *
- * @returns Boolean indicating if we're done serializing
- */
-PROT_WireFormatWriter.prototype.isComplete = function() {
- return this.current_ >= this.keyList_.length;
-}
-
-/**
- * When we're done, call the callback
- */
-PROT_WireFormatWriter.prototype.onComplete = function() {
- this.callback_(this.serialized_);
- this.callback_ = null;
-}
-
-/**
- * A WireFormatReader can deserialize data.
- *
- * @constructor
- *
- * @param threadQueue A global thread queue that we use to schedule our
- * work so that we do not take up too much time at one.
- *
- * @param opt_existingTablesData Optional reference to a map of tables
- * into which we should merge the data being deserialized
- */
-function PROT_WireFormatReader(threadQueue, opt_existingTablesData) {
- this.debugZone = "wireformatreader";
- this.existingTablesData_ = opt_existingTablesData;
- this.threadQueue_ = threadQueue;
- this.callback_ = null;
-
- // For mac'ing updates
- this.urlCrypto_ = null;
- var keyUrl = null;
- try {
- keyUrl = PROT_GlobalStore.getGetKeyURL();
- } catch (e) {
- G_Debug(this, "No key url, disabling mac'ed updates");
- }
- if (keyUrl) {
- this.urlCrypto_ = new PROT_UrlCrypto();
- }
-}
-
-/**
- * Starts a thread to process the input data.
- *
- * @param tableUpdate A string that contains the data for updating the
- * tables that we know about.
- *
- * @param callback A user specificed callback that is being executed
- * when data processing completes. The callback is provided
- * with two arguments: tablesKnown and tablesData
- *
- * @returns true is new data is being process, or false on failure.
- */
-PROT_WireFormatReader.prototype.deserialize = function(tableUpdate, callback) {
- this.tableUpdate_ = tableUpdate;
- this.tablesKnown_ = {}; // version parsers
- this.tablesData_ = {}; // TRTables
-
- if (this.callback_ != null) {
- G_Debug(this, "previous deserialize is still running");
- return false;
- }
-
- this.callback_ = callback;
- this.offset_ = 0;
- this.resetTableState_();
-
- // On empty data, we just invoke the callback directly
- if (!this.tableUpdate_ || !this.tableUpdate_.length) {
- G_Debug(this, "No data. Calling back.");
- this.onComplete();
- return true;
- }
-
- this.threadQueue_.addWorker(BindToObject(this.processLine_, this),
- BindToObject(this.isComplete, this));
- this.threadQueue_.run();
-
- return true;
-}
-
-/**
- * Resets the per table state
- */
-PROT_WireFormatReader.prototype.resetTableState_ = function() {
- this.vParser_ = null;
- this.insert_ = 0;
- this.remove_ = 0;
-}
-
-/**
- * Initalizes our state to process a new table.
- * NOTE: For performance reasons, we might have drive the replacement of
- * the table via a thread.
- *
- * @param line The input line that contains the table name and version
- */
-PROT_WireFormatReader.prototype.processNewTable_ = function(line) {
- this.vParser_ = new PROT_VersionParser("something");
-
- // If there's an error, give up
- if (!this.vParser_.fromString(line)) {
- G_Debug(this, "Error in table header, skipping");
- this.vParser_ = null;
- return;
- }
-
- G_Debug(this, "processNewTable: " + this.vParser_.type + ": " +
- this.vParser_.major + ":" + this.vParser_.minor + " update=" +
- this.vParser_.update + " mac=" + this.vParser_.mac);
-
- // Create temporary table. PROT_TRTable() blows up if it doesn't like its
- // name, so mask and just set vParser to null
- try {
- this.tablesData_[this.vParser_.type] =
- newUrlClassifierTable(this.vParser_.type);
- } catch(e) {
- G_Debug(this,
- "Unable to initialize new TRTable, because of exception: " + e);
- this.vParser_ = null;
- return;
- }
- if (this.vParser_.update && this.existingTablesData_ &&
- this.existingTablesData_[this.vParser_.type]) {
- // If we update an existing table, we need to copy the old table
- // data from the pre-existing tables
- this.tablesData_[this.vParser_.type].replace(
- this.existingTablesData_[this.vParser_.type]);
- }
- G_Debug(this, "processNewTable done");
-}
-
-/**
- * Updates the contents of our temporary table. Exceptions should not
- * be masked here -- they're caught at a higher level.
- *
- * @param line The input line that contains the new data
- */
-PROT_WireFormatReader.prototype.processUpdateTable_ = function(line) {
- // Regular update to the current version
- var tokens = line.split("\t");
- var operation = tokens[0][0];
- var key = tokens[0].slice(1);
- var value = tokens[1];
-
- if (operation == "+") {
- this.tablesData_[this.vParser_.type].insert(key, value);
- this.insert_++;
- } else {
- this.tablesData_[this.vParser_.type].remove(key);
- this.remove_++;
- }
-}
-
-/**
- * Finish processing a table
- */
-PROT_WireFormatReader.prototype.endTable_ = function() {
- G_Debug(this,
- "Finished: " + this.vParser_.type + " +" +
- this.insert_ + " -" + this.remove_ + " mac'ing: " + this.vParser_.mac);
-
- if (this.vParser_.mac) {
- var mac = this.urlCrypto_.finishMac();
- if (mac != this.vParser_.macval) {
- G_Debug(this, "Mac didn't match! (" + mac + " != "
- + this.vParser_.macval + ")");
- this.vParser_.macFailed = true;
- } else {
- G_Debug(this, "Computed mac matched sent mac: " + this.vParser_.macval);
- }
- }
-
- // Rollback if the mac failed.
- if (this.vParser_.mac && this.vParser_.macFailed) {
- this.tablesData_[this.vParser_.type] = undefined;
- this.tablesKnown_[this.vParser_.type] = undefined;
- G_Debug(this, "throwing away " + this.vParser_.type);
- } else {
- this.tablesKnown_[this.vParser_.type] = this.vParser_;
- }
-
- this.resetTableState_();
-}
-
-/**
- * Processes a chunk of data. TODO(MC): Make it so the mac is computed from a
- * saved begin and end offset instead of computing line by line.
- */
-PROT_WireFormatReader.prototype.processLine_ = function() {
- //G_Debug(this, '> processline');
- for (var count = 0;
- count < 5 && this.offset_ < this.tableUpdate_.length; count++) {
- var newOffset = this.tableUpdate_.indexOf("\n", this.offset_);
- var line = "";
- if (newOffset == -1) {
- this.offset_ = this.tableUpdate_.length;
- } else {
- line = this.tableUpdate_.slice(this.offset_, newOffset);
- this.offset_ = newOffset + 1;
- }
-
- // Ignore empty lines if we currently do not have a table
- if (!this.vParser_ && (!line || line[0] != '[')) {
- continue;
- }
-
- if (!line) {
- // End of one table - pop the results
- this.endTable_();
- } else if (line[0] == '[' && line.slice(-1) == ']') {
- // processNewTable doesn't instantiate this.vParser_ in case of malformed
- // headers, so the rest of the table lines will get skipped
- this.processNewTable_(line);
-
- if (this.vParser_ && this.vParser_.mac) {
- this.urlCrypto_.initMac();
- }
- } else {
- if (!this.vParser_) {
- G_Debug(this, "Ignoring: " + line);
- continue;
- }
-
- // Now we try to read a data line. However the table could've
- // been corrupted on disk (e.g., the browser suddenly quit while
- // we were in the middle of writing a line). If so,
- // porcessUpdateTable() will throw. In this case we want to
- // resynch the whole table, so we skip this line and then
- // explicitly set the table's minor version to -1 (the lowest
- // possible -- not 0, which means the table is local), causing a
- // full update the next time we ask for it.
- //
- // We ignore the case where we wrote an incomplete but malformed
- // table -- it fixes itself over time as the missing keys become
- // less relevant.
-
- try {
- this.processUpdateTable_(line);
- // Compute the mac over all the next lines until we finish the table
- // TODO: This is ugly! line doesn't get the final '\n', so add it back.
- // We could save the original line back up top, and use that instead,
- // but that's yet more copying...
- if (this.vParser_.mac) {
- this.urlCrypto_.updateMacFromString(line + "\n");
- }
- } catch(e) {
- G_Debug(this, "MALFORMED TABLE LINE: [" + line + "]\n" +
- "Skipping this line, and resetting table " +
- this.vParser_.type + " to version -1.\n" +
- "(This as a result of exception: " + e + ")");
- this.vParser_.minor = "-1";
- }
- }
- }
-
- // If the table we're reading is the last, then the for loop will
- // fail, causing the table finish logic to be skipped. So here
- // ensure that we finish up whatever table we're working on.
-
- if (this.vParser_ && this.offset_ >= this.tableUpdate_.length) {
- G_Debug(this,
- "Finished (final table): " + this.vParser_.type + " +" +
- this.insert_ + " -" + this.remove_);
- this.endTable_();
- }
-
- if (this.isComplete()) this.onComplete();
- //G_Debug(this, "< processline");
-}
-
-/**
- * Returns true if all input data has been processed
- */
-PROT_WireFormatReader.prototype.isComplete = function() {
- return this.offset_ >= this.tableUpdate_.length;
-}
-
-/**
- * Notifies our caller of completion.
- */
-PROT_WireFormatReader.prototype.onComplete = function() {
- G_Debug(this, "Processing complete. Executing callback.");
- this.callback_(this.tablesKnown_, this.tablesData_);
- this.callback_ = null;
-}
-
-
function TEST_PROT_WireFormat() {
if (G_GDEBUG) {
- // Sorry, this is incredibly ugly. What we need is continuations -- each
- // unittest that passes invokes the next.
-
- var z = "wireformat UNITTEST";
- G_debugService.enableZone(z);
+ var z = "versionparser UNITTEST";
G_Debug(z, "Starting");
- function testMalformedTables() {
- G_Debug(z, "Testing malformed tables...");
-
- var wfr = new PROT_WireFormatReader(new TH_ThreadQueue());
-
- // Damn these unittests are ugly. Ugh. Now test handling corrupt tables
- var data =
- "[test1-black-url 1.1]\n" +
- "+foo1\tbar\n" +
- "+foo2\n" + // Malformed
- "+foo3\tbar\n" +
- "+foo4\tbar\n" +
- "\n" +
- "[test2-black-url 1.2]\n" +
- "+foo1\tbar\n" +
- "+foo2\tbar\n" +
- "+foo3\tbar\n" +
- "+foo4\tbar\n" +
- "\n" +
- "[test3-black-url 1.3]\n" +
- "+foo1\tbar\n" +
- "+foo2\tbar\n" +
- "+foo3\tbar\n" +
- "+foo4\n" + // Malformed
- "\n" +
- "[test4-black-url 1.4]\n" +
- "+foo1\tbar\n" +
- "+foo2\tbar\n" +
- "+foo3\tbar\n" +
- "+foo4\tbar\n" +
- "\n" +
- "[test4-badheader-url 1.asfd dfui]\n" + // Malformed header
- "+foo1\tbar\n" +
- "+foo2\tbar\n" +
- "+foo3\tbar\n" +
- "+foo4\tbar\n";
- "\n" +
- "[test4-bad-name-url 1.asfd dfui]\n" + // Malformed header
- "+foo1\tbar\n" +
- "+foo2\tbar\n" +
- "+foo3\tbar\n" +
- "+foo4\tbar\n";
+ var vp = new PROT_VersionParser("dummy");
+ G_Assert(z, vp.fromString("[foo-bar-url 1.234]"),
+ "failed to parse old format");
+ G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
+ G_Assert(z, "1" == vp.major, "failed to parse major");
+ G_Assert(z, "234" == vp.minor, "failed to parse minor");
- function malformedcb(tablesKnown, tablesData) {
-
- // Table has malformed data
- G_Assert(z, tablesKnown["test1-black-url"].minor == "-1",
- "test table 1 didn't get reset");
- G_Assert(z, !!tablesData["test1-black-url"].exists("foo1"),
- "test table 1 didn't set keys before the error");
- G_Assert(z, !!tablesData["test1-black-url"].exists("foo3"),
- "test table 1 didn't set keys after the error");
+ vp = new PROT_VersionParser("dummy");
+ G_Assert(z, vp.fromString("[foo-bar-url 1.234][mac=567]"),
+ "failed to parse new format");
+ G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
+ G_Assert(z, "1" == vp.major, "failed to parse major");
+ G_Assert(z, "234" == vp.minor, "failed to parse minor");
+ G_Assert(z, true == vp.mac, "failed to parse mac");
+ G_Assert(z, "567" == vp.macval, "failed to parse macval");
- // Table should be good
- G_Assert(z, tablesKnown["test2-black-url"].minor == "2",
- "test table 1 didnt get correct version number");
- G_Assert(z, !!tablesData["test2-black-url"].exists("foo4"),
- "test table 2 didnt parse properly");
-
- // Table is malformed
- G_Assert(z, tablesKnown["test3-black-url"].minor == "-1",
- "test table 3 didn't get reset");
- G_Assert(z, !tablesData["test3-black-url"].exists("foo4"),
- "test table 3 somehow has its malformed line?");
-
- // Table should be good
- G_Assert(z, tablesKnown["test4-black-url"].minor == "4",
- "test table 4 didn't get correct version number");
- G_Assert(z, !!tablesData["test2-black-url"].exists("foo4"),
- "test table 4 didnt parse properly");
-
- // Table is malformed and should be unknown
- G_Assert(z, !tablesKnown["test4-badheader-url"],
- "test table header should't be known");
-
- // Table is malformed and should be unknown
- G_Assert(z, !tablesKnown["test4-bad-name-url"],
- "test table header should't be known");
-
- G_Debug(z, "PASSED");
- };
-
- wfr.deserialize(data, malformedcb);
- };
-
- var testTablesData = {};
-
- var tableName = "test-black-url";
- var data1 = "[" + tableName + " 1.5]\n";
- for (var i = 0; i < 100; i++)
- data1 += "+http://exists" + i + "\t1\n";
- data1 += "-http://exists50\t1\n";
- data1 += "-http://exists666\t1\n";
-
- var data2 = "[" + tableName + " 1.7 update]\n";
- for (var i = 0; i < 100; i++)
- data2 += "-http://exists" + i + "\t1\n";
-
- var set3Name = "test-white-domain";
- var set3data = "";
- // Use this string since it's the same in the update server unittest in
- // /firefox/security/scripts/test/cheese/cheesey something.sh
- for (var i = 1; i <= 3; i++) {
- set3data += "+white" + i + ".com\t1\n";
- }
- var urlCrypto = new PROT_UrlCrypto();
- var computedMac = urlCrypto.computeMac(set3data);
-
- function data1cb(tablesKnown, tablesData) {
- G_Assert(z,
- tablesData[tableName] != null,
- "Didn't get our table back");
-
- for (var i = 0; i < 100; i++)
- if (i != 50)
- G_Assert(z,
- tablesData[tableName].exists("http://exists" + i) == "1",
- "Item addition broken");
-
- G_Assert(z,
- !tablesData[tableName].exists("http://exists50"),
- "Item removal broken");
- G_Assert(z,
- !tablesData[tableName].exists("http://exists666"),
- "Non-existent item");
-
- G_Assert(z, tablesKnown[tableName].major == "1", "Major parsing broke");
- G_Assert(z, tablesKnown[tableName].minor == "5", "Major parsing broke");
-
- var wfr2 = new PROT_WireFormatReader(new TH_ThreadQueue(), tablesData);
- wfr2.deserialize(data2, data2cb);
- };
-
- function data2cb(tablesKnown, tablesData) {
- for (var i = 0; i < 100; i++)
- G_Assert(z,
- !tablesData[tableName].exists("http://exists" + i),
- "Tables merge broken");
-
- G_Assert(z, tablesKnown[tableName].major == "1", "Major parsing broke");
- G_Assert(z, tablesKnown[tableName].minor == "7", "Major parsing broke");
-
- var wfr3 = new PROT_WireFormatReader(new TH_ThreadQueue(), tablesData);
- var data3 = "[test-white-domain 1.1][mac=" + computedMac + "]\n" +
- set3data;
- wfr3.deserialize(data3, data3cb);
- };
-
- // Test the MAC stuff
- function data3cb(tablesKnown, tablesData) {
- G_Assert(z, tablesData["test-white-domain"] != null,
- "Didn't get our table back though the mac was correct");
-
- // Now do the same thing with the wrong mac
- computedMac = "012" + computedMac.substr(3);
- var data4 = "[test-foo-domain 1.1][mac=" + computedMac + "]\n" +
- set3data;
- var wfr4 = new PROT_WireFormatReader(new TH_ThreadQueue(), tablesData);
- wfr4.deserialize(data4, data4cb);
- }
-
- // Test the MAC stuff
- function data4cb(tablesKnown, tablesData) {
- G_Assert(z, !tablesKnown["test-foo-domain"],
- "Deserialized though our mac was wrong");
-
- G_Assert(z, !tablesData["test-foo-domain"],
- "Deserialized though our mac was wrong");
-
- testMalformedTables();
-
- }
- var wfr = new PROT_WireFormatReader(new TH_ThreadQueue());
- wfr.deserialize(data1, data1cb);
+ G_Debug(z, "PASSED");
}
}
diff --git a/mozilla/toolkit/components/url-classifier/content/xml-fetcher.js b/mozilla/toolkit/components/url-classifier/content/xml-fetcher.js
index 7294d522d14..48bf07e1e70 100644
--- a/mozilla/toolkit/components/url-classifier/content/xml-fetcher.js
+++ b/mozilla/toolkit/components/url-classifier/content/xml-fetcher.js
@@ -121,18 +121,18 @@ PROT_XMLFetcher.prototype = {
// headers) when we try to read the response. Mask the exception
// by returning null response.
// TODO maybe masking this should be an option?
+ var responseText = null;
try {
G_Debug(this, "xml fetch status code: \"" +
fetcher._request.status + "\"");
- if (fetcher._callback)
- fetcher._callback(fetcher._request.responseText);
+ var responseText = fetcher._request.responseText;
} catch(e) {
G_Debug(this, "Caught exception trying to read xmlhttprequest " +
"status/response.");
G_Debug(this, e);
- if (fetcher._callback)
- fetcher._callback(null);
}
+ if (fetcher._callback)
+ fetcher._callback(responseText);
}
};
diff --git a/mozilla/toolkit/components/url-classifier/jar.mn b/mozilla/toolkit/components/url-classifier/jar.mn
index 5a75b0a06e9..4ecdde0bd3f 100644
--- a/mozilla/toolkit/components/url-classifier/jar.mn
+++ b/mozilla/toolkit/components/url-classifier/jar.mn
@@ -1,7 +1,6 @@
toolkit.jar:
+ content/global/url-classifier/application.js (content/application.js)
+ content/global/url-classifier/enchash-decrypter.js (content/enchash-decrypter.js)
-+ content/global/url-classifier/globalstore.js (content/globalstore.js)
+ content/global/url-classifier/listmanager.js (content/listmanager.js)
+ content/global/url-classifier/list-warden.js (content/list-warden.js)
+ content/global/url-classifier/map.js (content/map.js)
diff --git a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableDomain.js b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableDomain.js
index 7d6ee70ed19..7ec723f528b 100644
--- a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableDomain.js
+++ b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableDomain.js
@@ -16,7 +16,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
- dump('*** loading subscript ' + libFile + '\n');
+ //dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);
diff --git a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableEnchash.js b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableEnchash.js
index de9105a6023..fd6492efa9b 100644
--- a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableEnchash.js
+++ b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableEnchash.js
@@ -20,7 +20,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
- dump('*** loading subscript ' + libFile + '\n');
+ //dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);
diff --git a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableSite.js b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableSite.js
index c9c988e9502..4a950bc8859 100644
--- a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableSite.js
+++ b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableSite.js
@@ -16,7 +16,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
- dump('*** loading subscript ' + libFile + '\n');
+ //dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);
diff --git a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableUrl.js b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableUrl.js
index c94ee845890..a79684920a9 100644
--- a/mozilla/toolkit/components/url-classifier/src/urlClassifierTableUrl.js
+++ b/mozilla/toolkit/components/url-classifier/src/urlClassifierTableUrl.js
@@ -17,7 +17,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
- dump('*** loading subscript ' + libFile + '\n');
+ //dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);
diff --git a/mozilla/toolkit/components/url-classifier/src/urlListManager.js b/mozilla/toolkit/components/url-classifier/src/urlListManager.js
index ccb9f54c286..cd3ab530e39 100644
--- a/mozilla/toolkit/components/url-classifier/src/urlListManager.js
+++ b/mozilla/toolkit/components/url-classifier/src/urlListManager.js
@@ -21,7 +21,6 @@ const LIB_FILES = [
"chrome://global/content/url-classifier/moz/protocol4.js",
"chrome://global/content/url-classifier/application.js",
- "chrome://global/content/url-classifier/globalstore.js",
"chrome://global/content/url-classifier/listmanager.js",
"chrome://global/content/url-classifier/url-crypto.js",
"chrome://global/content/url-classifier/url-crypto-key-manager.js", // dep url-crypto.js
@@ -30,7 +29,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
- dump('*** loading subscript ' + libFile + '\n');
+ //dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);