Bug 336288 (for tony@ponderer.org) r+a=ben Merge safe browsing UI into browser
git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@195925 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained 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
|
||||
|
||||
@@ -73,6 +73,7 @@ REQUIRES = xpcom \
|
||||
js \
|
||||
nkcache \
|
||||
widget \
|
||||
url-classifier \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../../build
|
||||
|
||||
52
mozilla/browser/components/safebrowsing/Makefile.in
Normal file
52
mozilla/browser/components/safebrowsing/Makefile.in
Normal file
@@ -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
|
||||
@@ -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 <fritz@google.com> (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;
|
||||
}
|
||||
615
mozilla/browser/components/safebrowsing/content/browser-view.js
Normal file
615
mozilla/browser/components/safebrowsing/content/browser-view.js
Normal file
@@ -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 <fritz@google.com> (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();
|
||||
}
|
||||
26
mozilla/browser/components/safebrowsing/content/commands.inc
Normal file
26
mozilla/browser/components/safebrowsing/content/commands.inc
Normal file
@@ -0,0 +1,26 @@
|
||||
<!-- Commands included by the browser overlay
|
||||
browser/base/content/browser-sets.inc -->
|
||||
<command id="safebrowsing-show-warning"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-show-warning')" />
|
||||
<command id="safebrowsing-accept-warning"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-accept-warning')" />
|
||||
<command id="safebrowsing-decline-warning"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-decline-warning')" />
|
||||
<command id="safebrowsing-palm-showmore"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-palm-showmore')" />
|
||||
<command id="safebrowsing-palm-phishingorg"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-palm-phishingorg')" />
|
||||
<command id="safebrowsing-palm-phishingfaq"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-palm-phishingfaq')" />
|
||||
<command id="safebrowsing-palm-fraudpage"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-palm-fraudpage')" />
|
||||
<command id="safebrowsing-palm-falsepositive"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-palm-falsepositive')" />
|
||||
<command id="safebrowsing-submit-blacklist"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-submit-blacklist')" />
|
||||
<command id="safebrowsing-submit-generic-phishing"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-submit-generic-phishing')"/>
|
||||
<command id="safebrowsing-preferences"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-preferences')" />
|
||||
<command id="safebrowsing-test-link"
|
||||
oncommand="SB_executeCommandLocally('safebrowsing-test-link')" />
|
||||
390
mozilla/browser/components/safebrowsing/content/controller.js
Normal file
390
mozilla/browser/components/safebrowsing/content/controller.js
Normal file
@@ -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 <fritz@google.com> (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);
|
||||
}
|
||||
@@ -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 <fritz@google.com> (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) { }
|
||||
|
||||
216
mozilla/browser/components/safebrowsing/content/globalstore.js
Normal file
216
mozilla/browser/components/safebrowsing/content/globalstore.js
Normal file
@@ -0,0 +1,216 @@
|
||||
/* ***** 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 <fritz@google.com> (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 class that encapsulates globals such as the names of things. We
|
||||
// centralize everything here mainly so as to ease their modification,
|
||||
// but also in case we want to version.
|
||||
//
|
||||
// This class does _not_ embody semantics, defaults, or the like. If we
|
||||
// need something that does, we'll add our own preference registry.
|
||||
//
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* A clearinghouse for globals. All interfaces are read-only.
|
||||
*/
|
||||
function PROT_GlobalStore() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a pref value
|
||||
*/
|
||||
PROT_GlobalStore.getPref_ = function(prefname) {
|
||||
var pref = new G_Preferences();
|
||||
return pref.getPref(prefname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The name of the pref determining whether phishing protection
|
||||
* is enabled (i.e., whether SafeBrowsing is enabled)
|
||||
*/
|
||||
PROT_GlobalStore.getPhishWardenEnabledPrefName = function() {
|
||||
return "safebrowsing.enabled";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The name of the pref determining whether we enable remote
|
||||
* checking (advanced protection)
|
||||
*/
|
||||
PROT_GlobalStore.getServerCheckEnabledPrefName = function() {
|
||||
return "safebrowsing.remoteLookups";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The name of the pref determining whether we send reports
|
||||
* about user actions
|
||||
*/
|
||||
PROT_GlobalStore.getSendUserReportsPrefName = function() {
|
||||
// We send reports iff advanced protection mode is on
|
||||
return PROT_GlobalStore.getServerCheckEnabledPrefName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The name of the directory in which we should store data (like
|
||||
* blacklists and whitelists). This is relative to the user's
|
||||
* profile.
|
||||
*/
|
||||
PROT_GlobalStore.getAppDirectoryName = function() {
|
||||
return "safebrowsing_data";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String containing the URL to nav to when the user clicks
|
||||
* "get me out of here"
|
||||
*/
|
||||
PROT_GlobalStore.getGetMeOutOfHereURL = function() {
|
||||
// Try to get their homepage from prefs.
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService).getBranch(null);
|
||||
|
||||
var url = "about:blank";
|
||||
try {
|
||||
url = prefs.getComplexValue("browser.startup.homepage",
|
||||
Ci.nsIPrefLocalizedString).data;
|
||||
} catch(e) {
|
||||
G_Debug(this, "Couldn't get homepage pref: " + e);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: maybe deprecate because antiphishing.org isn't localized
|
||||
* @returns String containing the URL to nav to when the user clicks
|
||||
* the link to antiphishing.org in the bubble.
|
||||
*/
|
||||
PROT_GlobalStore.getAntiPhishingURL = function() {
|
||||
return "http://antiphishing.org/";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String containing the URL to nav to when the user clicks
|
||||
* on the policy link in the preferences.
|
||||
*/
|
||||
PROT_GlobalStore.getPolicyURL = function() {
|
||||
return "TODO";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String containing the URL to nav to when the user wants to
|
||||
* submit a generic phishing report (we're not sure if they
|
||||
* want to report a false positive or negative).
|
||||
*/
|
||||
PROT_GlobalStore.getGenericPhishSubmitURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.genericReportURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String containing the URL to nav to when the user wants to
|
||||
* report a false positive (i.e. a non-phishy page)
|
||||
*/
|
||||
PROT_GlobalStore.getFalsePositiveURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.reportErrorURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String containing the URL to nav to when the user wants to
|
||||
* report a false negative (i.e. a phishy page)
|
||||
*/
|
||||
PROT_GlobalStore.getSubmitUrl = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.reportPhishURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: maybe deprecated because no UI location for it?
|
||||
* @returns String containing the URL to nav to when the user clicks
|
||||
* "more info" in the bubble or the product link in the preferences.
|
||||
*/
|
||||
PROT_GlobalStore.getHomePageURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.homeURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: maybe deprecated because no UI location for it?
|
||||
* @returns String containing the URL to nav to when the user clicks
|
||||
* "phishing FAQ" in the bubble.
|
||||
*/
|
||||
PROT_GlobalStore.getPhishingFaqURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.faqURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String containing the URL to nav to when the user wants to
|
||||
* see the test page
|
||||
*/
|
||||
PROT_GlobalStore.getTestURLs = function() {
|
||||
// TODO: return all test urls
|
||||
return [PROT_GlobalStore.getPref_("safebrowsing.provider.0.testURL")];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String giving url to use for lookups (used in advanced mode)
|
||||
*/
|
||||
PROT_GlobalStore.getLookupserverURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.lookupURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String giving url to use for updates (diff of lists)
|
||||
*/
|
||||
PROT_GlobalStore.getUpdateserverURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.updateURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: maybe deprecate?
|
||||
* @returns String giving url to use to report actions (advanced mode only
|
||||
*/
|
||||
PROT_GlobalStore.getActionReportURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.reportURL");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns String giving url to use for re-keying
|
||||
*/
|
||||
PROT_GlobalStore.getGetKeyURL = function() {
|
||||
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.keyURL");
|
||||
}
|
||||
@@ -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 <fritz@google.com> (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);
|
||||
}
|
||||
@@ -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 <fritz@google.com> (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]);
|
||||
}
|
||||
239
mozilla/browser/components/safebrowsing/content/list-warden.js
Normal file
239
mozilla/browser/components/safebrowsing/content/list-warden.js
Normal file
@@ -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 <niels@google.com> (original author)d
|
||||
|
||||
* Fritz Schneider <fritz@google.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
@@ -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 <fritz@google.com> (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) { }
|
||||
|
||||
@@ -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 <fritz@google.com> (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 <tab> 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_);
|
||||
}
|
||||
@@ -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 <fritz@google.com> (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;
|
||||
}
|
||||
@@ -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 <fritz@google.com> (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");
|
||||
}
|
||||
}
|
||||
91
mozilla/browser/components/safebrowsing/content/reporter.js
Normal file
91
mozilla/browser/components/safebrowsing/content/reporter.js
Normal file
@@ -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 <fritz@google.com> (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 */);
|
||||
}
|
||||
168
mozilla/browser/components/safebrowsing/content/safebrowsing.inc
Normal file
168
mozilla/browser/components/safebrowsing/content/safebrowsing.inc
Normal file
@@ -0,0 +1,168 @@
|
||||
<!-- browser.xul overlay
|
||||
included from browser/base/content/browser.xul -->
|
||||
|
||||
<!-- This dims out the browser content -->
|
||||
|
||||
<vbox id="safebrowsing-dim-area-transp" hidden="true">
|
||||
<spacer flex="1" />
|
||||
</vbox>
|
||||
|
||||
<!-- This is the spike aka tail on top of the warning -->
|
||||
|
||||
<hbox id="safebrowsing-palm-message-tail-container" hidden="true"
|
||||
pack="end">
|
||||
<image id="safebrowsing-palm-message-tail"
|
||||
src="chrome://browser/skin/safebrowsing/tail.png" />
|
||||
</hbox>
|
||||
|
||||
<!-- This is the phishing afterload warning message -->
|
||||
|
||||
<vbox id="safebrowsing-palm-message" hidden="true" pack="center"
|
||||
class="safebrowsing-palm-fixed-width">
|
||||
|
||||
<!-- This is the main warning area -->
|
||||
|
||||
<vbox id="safebrowsing-palm-message-content"
|
||||
class="safebrowsing-palm-fixed-width safebrowsing-palm-message-bubble">
|
||||
|
||||
<!-- Top line -->
|
||||
|
||||
<hbox id="safebrowsing-palm-message-titlebox"
|
||||
class="safebrowsing-palm-fixed-width">
|
||||
<image
|
||||
src="chrome://browser/skin/safebrowsing/warning24x24.png"
|
||||
id="safebrowsing-palm-title-icon" />
|
||||
<description id="safebrowsing-palm-message-title"
|
||||
class="safebrowsing-palm-title"
|
||||
value="&safeb.palm.warning.heading;" />
|
||||
<spacer flex="1" />
|
||||
<vbox pack="start" align="start">
|
||||
<box onclick="goDoCommand('safebrowsing-decline-warning')"><image
|
||||
src="chrome://browser/skin/safebrowsing/close16x16.png"
|
||||
id="safebrowsing-palm-close" /></box>
|
||||
<spacer flex="1" />
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<!-- Content area: short warning -->
|
||||
|
||||
<description id="safebrowsing-palm-content"
|
||||
class="safebrowsing-paragraph">
|
||||
&safeb.palm.message.part1;
|
||||
<html:a class="safebrowsing-palm-link" tabindex="1"
|
||||
statustext="&safeb.palm.showmore.statustext;"
|
||||
onmouseover="SB_setStatus(this.getAttribute('statustext'))"
|
||||
onmouseout="SB_clearStatus()"
|
||||
id="safebrowsing-palm-showmore-link" href="#"
|
||||
onclick="goDoCommand('safebrowsing-palm-showmore')">
|
||||
&safeb.palm.message.link1.more;
|
||||
</html:a>
|
||||
</description>
|
||||
|
||||
<!-- Content area: rest of the warning, revealed if the user
|
||||
hits the more link -->
|
||||
|
||||
<vbox id="safebrowsing-palm-extended-message" hidden="true"
|
||||
class="safebrowsing-palm-fixed-width">
|
||||
|
||||
<description class="safebrowsing-palm-paragraph">
|
||||
&safeb.palm.message.part2;
|
||||
<html:a class="safebrowsing-palm-link" tabindex="2"
|
||||
id="safebrowsing-palm-faq-link"
|
||||
onmouseover="SB_setStatusFor(this.id)"
|
||||
onmouseout="SB_clearStatus()"
|
||||
onclick="goDoCommand('safebrowsing-palm-phishingfaq')">
|
||||
&safeb.palm.message.link2.phishing.faq;</html:a>
|
||||
&safeb.palm.message.part3;
|
||||
<html:a class="safebrowsing-palm-link" tabindex="3"
|
||||
id="safebrowsing-palm-phishingorg-link"
|
||||
onmouseover="SB_setStatusFor(this.id)"
|
||||
onmouseout="SB_clearStatus()"
|
||||
onclick="goDoCommand('safebrowsing-palm-phishingorg')">
|
||||
&safeb.palm.message.link3.antiphishing.org;</html:a>&safeb.palm.message.part3.dot;
|
||||
</description>
|
||||
|
||||
<description class="safebrowsing-palm-paragraph">
|
||||
&safeb.palm.message.part4;
|
||||
<html:a class="safebrowsing-palm-link" tabindex="4"
|
||||
id="safebrowsing-palm-fraudpage-link"
|
||||
onmouseover="SB_setStatusFor(this.id)"
|
||||
onmouseout="SB_clearStatus()"
|
||||
onclick="goDoCommand('safebrowsing-palm-fraudpage')">
|
||||
&safeb.palm.message.link4.gwghome;</html:a>&safeb.palm.message.part4.dot;</description>
|
||||
<description class="safebrowsing-palm-paragraph">
|
||||
&safeb.palm.message.part5;<html:a
|
||||
class="safebrowsing-palm-link" tabindex="6"
|
||||
id="safebrowsing-palm-falsepositive-link"
|
||||
onmouseover="SB_setStatusFor(this.id)"
|
||||
onmouseout="SB_clearStatus()"
|
||||
onclick="goDoCommand('safebrowsing-palm-falsepositive')">
|
||||
&safeb.palm.message.link5.report;</html:a>&safeb.palm.message.part5.dot;
|
||||
</description>
|
||||
</vbox>
|
||||
|
||||
<!-- Main action links -->
|
||||
|
||||
<hbox id="safebrowsing-palm-message-actionbox" pack="center"
|
||||
align="center"
|
||||
class="safebrowsing-palm-fixed-width">
|
||||
<description>
|
||||
<html:a
|
||||
class="safebrowsing-palm-link safebrowsing-palm-bigtext safebrowsing-padded"
|
||||
statustext="&safeb.palm.accept.statustext;"
|
||||
id="safebrowsing-palm-accept-link"
|
||||
onmouseover="SB_setStatus(this.getAttribute('statustext'))"
|
||||
onmouseout="SB_clearStatus()"
|
||||
tabindex="2"
|
||||
onclick="goDoCommand('safebrowsing-accept-warning')">
|
||||
&safeb.palm.accept.label;</html:a></description>
|
||||
<spacer />
|
||||
<spacer />
|
||||
<description>
|
||||
<html:a
|
||||
class="safebrowsing-palm-link safebrowsing-palm-bigtext safebrowsing-padded"
|
||||
statustext="&safeb.palm.decline.statustext;"
|
||||
id="safebrowsing-palm-decline-link"
|
||||
onmouseover="SB_setStatus(this.getAttribute('statustext'))"
|
||||
onmouseout="SB_clearStatus()"
|
||||
tabindex="3"
|
||||
onclick="goDoCommand('safebrowsing-decline-warning')">
|
||||
&safeb.palm.decline.label;</html:a></description>
|
||||
</hbox>
|
||||
|
||||
<!-- Footer -->
|
||||
|
||||
<hbox align="center" class="safebrowsing-palm-fixed-width">
|
||||
<!-- branding logo
|
||||
<image id="safebrowsing-palm-logo"
|
||||
tooltiptext="&safeb.palm.provider.logo.tooltip;"
|
||||
src="chrome://browser/skin/safebrowsing/logo.png" />
|
||||
-->
|
||||
<Spacer flex="1" />
|
||||
<hbox>
|
||||
[<description><html:a id="safebrowsing-palm-report-link"
|
||||
onmouseover="SB_setStatusFor(this.id)"
|
||||
onmouseout="SB_clearStatus()"
|
||||
class="safebrowsing-palm-smalltext safebrowsing-palm-link"
|
||||
onclick="goDoCommand('safebrowsing-submit-generic-phishing')">
|
||||
&safeb.palm.sendreport.label;</html:a></description>]
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
|
||||
<!-- We position things over a canvas; to do so, we need a stack -->
|
||||
|
||||
<stack id="safebrowsing-content-stack" insertbefore="status-bar"
|
||||
hidden="true">
|
||||
|
||||
<!-- The canvas goes here as the first child, but since it takes
|
||||
up so much memory and is rarely used, we add and remove it
|
||||
dynamically -->
|
||||
|
||||
<!-- This dims out the browser content -->
|
||||
|
||||
<vbox id="safebrowsing-dim-area-canvas" hidden="true" flex="1">
|
||||
</vbox>
|
||||
|
||||
</stack>
|
||||
153
mozilla/browser/components/safebrowsing/content/sb-loader.js
Normal file
153
mozilla/browser/components/safebrowsing/content/sb-loader.js
Normal file
@@ -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 <fritz@google.com> (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 = "";
|
||||
}
|
||||
205
mozilla/browser/components/safebrowsing/content/tr-fetcher.js
Normal file
205
mozilla/browser/components/safebrowsing/content/tr-fetcher.js
Normal file
@@ -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 <fritz@google.com> (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");
|
||||
}
|
||||
}
|
||||
22
mozilla/browser/components/safebrowsing/jar.mn
Normal file
22
mozilla/browser/components/safebrowsing/jar.mn
Normal file
@@ -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)
|
||||
49
mozilla/browser/components/safebrowsing/src/Makefile.in
Normal file
49
mozilla/browser/components/safebrowsing/src/Makefile.in
Normal file
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
47
mozilla/browser/components/safebrowsing/tests/Makefile.in
Normal file
47
mozilla/browser/components/safebrowsing/tests/Makefile.in
Normal file
@@ -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
|
||||
|
||||
2
mozilla/browser/components/safebrowsing/tests/jar.mn
Normal file
2
mozilla/browser/components/safebrowsing/tests/jar.mn
Normal file
@@ -0,0 +1,2 @@
|
||||
browser.jar:
|
||||
+ content/browser/protection/unittests.xul (unittests.xul)
|
||||
70
mozilla/browser/components/safebrowsing/tests/unittests.xul
Normal file
70
mozilla/browser/components/safebrowsing/tests/unittests.xul
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0"?>
|
||||
<window id="PROT_unittest"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="onProtUnittestLoad();"
|
||||
title="prot unittests">
|
||||
|
||||
<script><![CDATA[
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
function G_Debug(zone, s) {
|
||||
var label = document.createElement('label');
|
||||
var txt = "[" + zone + "] " + s;
|
||||
label.appendChild(document.createTextNode(txt));
|
||||
|
||||
document.documentElement.appendChild(label);
|
||||
}
|
||||
|
||||
function G_Assert(zone, cond, msg) {
|
||||
if (!cond) {
|
||||
G_Debug(zone, msg);
|
||||
throw msg;
|
||||
}
|
||||
}
|
||||
|
||||
function ProtectionPhishWardenTests() {
|
||||
var z = "phishwarden UNITTEST";
|
||||
G_Debug(z, "Starting");
|
||||
|
||||
var listManager = Cc["@mozilla.org/protection/protectionlistmanager;1"]
|
||||
.getService(Ci.nsIProtectionListManager);
|
||||
var warden = Cc['@mozilla.org/protection/phishwarden;1']
|
||||
.createInstance(Ci.nsIProtectionListWarden);
|
||||
// Register tables that we are interested in.
|
||||
warden.registerBlackTable("test-black-url");
|
||||
|
||||
var blacklistedCount = 0;
|
||||
|
||||
var blackURLs = [
|
||||
"http://foo.com/1",
|
||||
"http://foo.com/2",
|
||||
"http://foo.com/3",
|
||||
"http://foo.com/4",
|
||||
"http://www.goodsite.com/test",
|
||||
];
|
||||
|
||||
for (var i = 0; i < blackURLs.length; i++)
|
||||
listManager.safeInsert("test-black-url", blackURLs[i], "1");
|
||||
|
||||
G_Assert(z, !warden.checkUrl("http://bar.com/"), 'should not have found');
|
||||
G_Assert(z, warden.checkUrl("http://foo.com/1"), 'should have found (1)');
|
||||
G_Assert(z, warden.checkUrl("http://foo.com/2"), 'should have found (2)');
|
||||
G_Assert(z, warden.checkUrl("http://foo.com/3"), 'should have found (3)');
|
||||
G_Assert(z, warden.checkUrl("http://foo.com/4"), 'should have found (4)');
|
||||
|
||||
warden.registerWhiteTable('test-white-domain');
|
||||
listManager.safeInsert("test-white-domain", "http://www.goodsite.com/", "1");
|
||||
|
||||
G_Assert(z, !warden.checkUrl("http://www.goodsite.com/"),
|
||||
'whitelist failed?')
|
||||
|
||||
|
||||
G_Debug(z, "PASSED");
|
||||
}
|
||||
|
||||
function onProtUnittestLoad() {
|
||||
ProtectionPhishWardenTests();
|
||||
}
|
||||
]]></script>
|
||||
</window>
|
||||
Reference in New Issue
Block a user