Bug 336288 (for tony@ponderer.org) r=ben integrate UI portion of safebrowsing into /browser/components/safebrowsing

This commit is contained in:
brettw%gmail.com 2006-05-04 20:36:16 +00:00
Родитель 519fee985f
Коммит c16bd281db
59 изменённых файлов: 5461 добавлений и 914 удалений

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

@ -58,7 +58,8 @@
<menuseparator id="spell-language-separator"/>
<menuitem id="spell-add-dictionaries"
label="&spellAddDictionaries.label;"
accesskey="&spellAddDictionaries.accesskey;"/>
accesskey="&spellAddDictionaries.accesskey;"
oncommand="gContextMenu.addDictionaries();"/>
</menupopup>
</menu>
<menuseparator id="spell-separator"/>

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

@ -15,5 +15,9 @@
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
#endif
#ifdef MOZ_SAFE_BROWSING
<!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
%safebrowsingDTD;
#endif
]>

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

@ -4991,6 +4991,16 @@ nsContextMenu.prototype = {
}
}
return false;
},
addDictionaries : function()
{
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
// FIXME bug 335605: hook this up so that it takes you to the download
// web page
var rv = ps.alert(window, "Add Dictionaries",
"This command hasn't been hooked up yet. Instead, go to Thunderbird's download page:\n\nhttp://www.mozilla.org/products/thunderbird/dictionaries.html\n\nThese plugins will work in Firefox as well.");
}
}

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

@ -52,6 +52,10 @@
<?xml-stylesheet href="chrome://browser/skin/places/browser-places.css" type="text/css"?>
#endif
#ifdef MOZ_SAFE_BROWSING
<?xml-stylesheet href="chrome://browser/skin/safebrowsing/browser-protection.css" type="text/css"?>
#endif
<?xml-stylesheet href="chrome://global/skin/toolbar.css" type="text/css"?>
<?xml-stylesheet href="chrome://global/skin/findBar.css" type="text/css"?>
@ -63,6 +67,9 @@
<window id="main-window"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
#ifdef MOZ_SAFE_BROWSING
xmlns:html="http://www.w3.org/1999/xhtml"
#endif
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="BrowserStartup()" onunload="BrowserShutdown()" onclose="return WindowIsClosing();"
contenttitlesetting="true"
@ -259,6 +266,12 @@
#endif
</button>
<image id="lock-icon" onclick="if (event.button == 0) displaySecurityInfo(); event.stopPropagation();"/>
#ifdef MOZ_SAFE_BROWSING
<image src="chrome://browser/content/safebrowsing/skin/warning16x16.png"
id="safebrowsing-urlbar-icon" tooltiptext="&safeb.urlbaricon.tooltip;"
style="display:none"
onclick="goDoCommand('safebrowsing-show-warning')" />
#endif
</hbox>
</textbox>
</toolbaritem>
@ -486,6 +499,11 @@
/>
</vbox>
</hbox>
#ifdef MOZ_SAFE_BROWSING
#include ../../components/safebrowsing/content/safebrowsing.inc
#endif
#include ../../../toolkit/components/typeaheadfind/content/findBar.inc

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

@ -52,3 +52,6 @@
<script type="application/x-javascript" src="chrome://browser/content/browser.js"/>
<script type="application/x-javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
<script type="application/x-javascript" src="chrome://global/content/viewSourceUtils.js"/>
#ifdef MOZ_SAFE_BROWSING
<script type="application/x-javascript" src="chrome://browser/content/safebrowsing/sb-loader.js"/>
#endif

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

@ -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

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

@ -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;
}

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

@ -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();
}

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

@ -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')" />

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

@ -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) { }

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

@ -45,6 +45,7 @@
// TODO: These values are actually specific to SafeBrowsing, not url
// classifier, so this file should be moved into
// browser/components/safebrowsing
// TODO: many of these values should just be moved directly into code.
// TODO: The code needs to fail more gracefully if these values aren't set
// E.g., createInstance should fail for listmanager without these.
@ -180,8 +181,9 @@ PROT_GlobalStore.getPhishingFaqURL = function() {
* @returns String containing the URL to nav to when the user wants to
* see the test page
*/
PROT_GlobalStore.getTestURL = function() {
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.testURL");
PROT_GlobalStore.getTestURLs = function() {
// TODO: return all test urls
return [PROT_GlobalStore.getPref_("safebrowsing.provider.0.testURL")];
}
/**
@ -212,22 +214,3 @@ PROT_GlobalStore.getActionReportURL = function() {
PROT_GlobalStore.getGetKeyURL = function() {
return PROT_GlobalStore.getPref_("safebrowsing.provider.0.keyURL");
}
/**
* @returns String giving filename to use to store keys
*/
PROT_GlobalStore.getKeyFilename = function() {
return "kf.txt";
}
/**
* For running unittests.
* TODO: make this automatic based on build rules
*/
PROT_GlobalStore.isTesting = function() {
isTesting = false;
try {
isTesting = PROT_GlobalStore.getPref_("safebrowsing.testing")
} catch (e) {}
return isTesting;
}

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

@ -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]);
}

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

@ -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");
}
}

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

@ -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 */);
}

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

@ -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>

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

@ -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 = "";
}

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

@ -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");
}
}

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

@ -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)

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

@ -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;
}

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

@ -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

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

@ -0,0 +1,2 @@
browser.jar:
+ content/browser/protection/unittests.xul (unittests.xul)

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

@ -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>

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

@ -0,0 +1,34 @@
<!ENTITY safeb.palm.warning.heading "Web Forgery">
<!ENTITY safeb.palm.message.part1 "This page is very likely to have been designed to trick users into sharing personal or financial information. Entering any personal information on this page may result in identity theft or other fraud. &#160; ">
<!ENTITY safeb.palm.message.link1.more "more &#187;">
<!ENTITY safeb.palm.showmore.statustext "Read more">
<!ENTITY safeb.palm.message.part2 "Safe Browsing uses automated technology to warn you about web pages that we believe may be unsafe. This page appears to be a part of a scam known as a ">
<!ENTITY safeb.palm.message.link2.phishing.faq "phishing">
<!ENTITY safeb.palm.message.part3 "attack, in which criminals create fraudulent pages to mimic legitimate sources that you may trust. You can read more about phishing here">
<!ENTITY safeb.palm.message.link3.antiphishing.org "antiphishing.org">
<!ENTITY safeb.palm.message.part3.dot ".">
<!ENTITY safeb.palm.message.part4 "You can also learn more about ">
<!ENTITY safeb.palm.message.link4.gwghome "about Safe Browsing">
<!ENTITY safeb.palm.message.part4.dot ".">
<!ENTITY safeb.palm.message.part5 "Finally, if you believe this page has been falsely flagged as a forgery, you can ">
<!ENTITY safeb.palm.message.link5.report "report it">
<!ENTITY safeb.palm.message.part5.dot ".">
<!ENTITY safeb.palm.accept.label "Get me out of here!">
<!ENTITY safeb.palm.accept.statustext "Navigate to my home page">
<!ENTITY safeb.palm.decline.label "Ignore this warning">
<!ENTITY safeb.palm.decline.statustext "Close warning" >
<!ENTITY safeb.palm.sendreport.label "Send report">
<!ENTITY safeb.palm.addbutton.label "Add to blacklist">
<!ENTITY safeb.palm.addbutton.tooltip "Add to blacklist">
<!ENTITY safeb.palm.toolmenu.label "Safe Browsing">
<!ENTITY safeb.palm.toolmenu.pref.label "Preferences...">
<!ENTITY safeb.palm.toolmenu.reportphishing.label "Report phishing...">
<!ENTITY safeb.palm.toolmenu.showphishingtestpage.label "Open Test Page">
<!ENTITY safeb.palm.provider.logo.tooltip "Warning from Safe Browsing">
<!ENTITY safeb.urlbaricon.tooltip "This page might be dangerous; click for details.">

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

@ -38,6 +38,9 @@
* locale/browser/bookmarks/bookmarks.properties (%chrome/browser/bookmarks/bookmarks.properties)
* locale/browser/bookmarks/bookmarksProperties.dtd (%chrome/browser/bookmarks/bookmarksProperties.dtd)
#endif
#ifdef MOZ_SAFE_BROWSING
locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
#endif
#ifdef MOZ_FEEDS
locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd)
locale/browser/feeds/options.dtd (%chrome/browser/feeds/options.dtd)

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

@ -64,6 +64,14 @@ classic.jar:
skin/classic/browser/bookmarks/livemark-item.png (bookmarks/livemark-item.png)
skin/classic/browser/bookmarks/folderarrow-hover.png (bookmarks/folderarrow-hover.png)
skin/classic/browser/bookmarks/folderarrow.png (bookmarks/folderarrow.png)
#endif
#ifdef MOZ_SAFE_BROWSING
skin/classic/browser/safebrowsing/browser-protection.css (safebrowsing/browser-protection.css)
skin/classic/browser/safebrowsing/close16x16.png (safebrowsing/close16x16.png)
skin/classic/browser/safebrowsing/dim.png (safebrowsing/dim.png)
skin/classic/browser/safebrowsing/tail.png (safebrowsing/tail.png)
skin/classic/browser/safebrowsing/warning16x16.png (safebrowsing/warning16x16.png)
skin/classic/browser/safebrowsing/warning24x24.png (safebrowsing/warning24x24.png)
#endif
skin/classic/browser/preferences/Options.png (preferences/Options.png)
skin/classic/browser/preferences/preferences.css (preferences/preferences.css)

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

@ -0,0 +1,130 @@
#safebrowsing-palm-message {
display: none;
position: fixed;
top: 0px;
left: 0px;
z-index: 2;
}
.safebrowsing-palm-message-bubble {
background-color: white;
-moz-box-sizing: border-box;
-moz-border-radius: 10px;
margin-top: -5px;
padding: 10px;
border-top: 1px solid;
border-left: 1px solid;
border-right: 2px solid;
border-bottom: 2px solid;
z-index: 1;
}
.safebrowsing-palm-fixed-width {
width: 400px;
max-width: 400px;
}
.safebrowsing-palm-title {
font-size: 20px;
font-weight: bold;
}
#safebrowsing-palm-title-icon {
height: 24px;
width: 24px;
max-height: 24px;
}
.safebrowsing-palm-link {
color: blue;
cursor: pointer;
text-decoration: underline
}
.safebrowsing-palm-paragraph {
padding-bottom: 2em;
}
#safebrowsing-palm-close {
cursor: pointer;
max-width: 16px !important;
height: 16px;
width: 16px;
}
#safebrowsing-palm-message-tail-container {
display: none;
position: fixed;
top: 0px;
left: 0px;
border-right: 2px solid;
z-index: 3;
height: 67px;
margin: 0;
padding: 0;
}
#safebrowsing-palm-message-tail {
height: 67px;
width: 24px;
max-width: 24px;
z-index: 3;
}
#safebrowsing-palm-message-titlebox {
padding-bottom: 5px;
padding-left: 10px;
}
#safebrowsing-palm-message-title {
padding-left: 5px;
}
#safebrowsing-palm-message-actionbox {
padding-top: 10px;
padding-bottom: 15px;
}
.safebrowsing-palm-bigtext {
font-size: 14px;
font-weight: bold;
}
.safebrowsing-palm-smalltext {
font-size: 10px;
}
#safebrowsing-palm-google-logo {
height: 32px;
max-height: 32px;
width: 78px;
}
#safebrowsing-dim-area-canvas {
background-image: url("chrome://browser/skin/safebrowsing/dim.png");
height: 0px;
}
#safebrowsing-dim-area-transp {
background-image: url("chrome://browser/skin/safebrowsing/dim.png");
height: 0px;
left: 0px;
top: 0px;
position: fixed;
display: none;
}
#safebrowsing-pref-caption {
background-color: #2222EE;
}
#safebrowsing-pref-caption text {
text-align: center;
color: #FFFFFF;
font-size: 150%;
font-family: arial, helvetica, sans-serif;
}
#safebrowsing-urlbar-icon {
padding-right: 2px;
}

Двоичные данные
browser/themes/pinstripe/browser/safebrowsing/close16x16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 406 B

Двоичные данные
browser/themes/pinstripe/browser/safebrowsing/dim.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
browser/themes/pinstripe/browser/safebrowsing/tail.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 759 B

Двоичные данные
browser/themes/pinstripe/browser/safebrowsing/warning16x16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 806 B

Двоичные данные
browser/themes/pinstripe/browser/safebrowsing/warning24x24.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

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

@ -40,6 +40,14 @@ classic.jar:
skin/classic/browser/bookmarks/addBookmark.css (bookmarks/addBookmark.css)
skin/classic/browser/bookmarks/bookmarksManager.css (bookmarks/bookmarksManager.css)
skin/classic/browser/bookmarks/Bookmarks-toolbar.png (bookmarks/Bookmarks-toolbar.png)
#endif
#ifdef MOZ_SAFE_BROWSING
skin/classic/browser/safebrowsing/browser-protection.css (safebrowsing/browser-protection.css)
skin/classic/browser/safebrowsing/close16x16.png (safebrowsing/close16x16.png)
skin/classic/browser/safebrowsing/dim.png (safebrowsing/dim.png)
skin/classic/browser/safebrowsing/tail.png (safebrowsing/tail.png)
skin/classic/browser/safebrowsing/warning16x16.png (safebrowsing/warning16x16.png)
skin/classic/browser/safebrowsing/warning24x24.png (safebrowsing/warning24x24.png)
#endif
skin/classic/browser/preferences/Options.png (preferences/Options.png)
skin/classic/browser/preferences/preferences.css (preferences/preferences.css)

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

@ -0,0 +1,130 @@
#safebrowsing-palm-message {
display: none;
position: fixed;
top: 0px;
left: 0px;
z-index: 2;
}
.safebrowsing-palm-message-bubble {
background-color: white;
-moz-box-sizing: border-box;
-moz-border-radius: 10px;
margin-top: -5px;
padding: 10px;
border-top: 1px solid;
border-left: 1px solid;
border-right: 2px solid;
border-bottom: 2px solid;
z-index: 1;
}
.safebrowsing-palm-fixed-width {
width: 400px;
max-width: 400px;
}
.safebrowsing-palm-title {
font-size: 20px;
font-weight: bold;
}
#safebrowsing-palm-title-icon {
height: 24px;
width: 24px;
max-height: 24px;
}
.safebrowsing-palm-link {
color: blue;
cursor: pointer;
text-decoration: underline
}
.safebrowsing-palm-paragraph {
padding-bottom: 2em;
}
#safebrowsing-palm-close {
cursor: pointer;
max-width: 16px !important;
height: 16px;
width: 16px;
}
#safebrowsing-palm-message-tail-container {
display: none;
position: fixed;
top: 0px;
left: 0px;
border-right: 2px solid;
z-index: 3;
height: 67px;
margin: 0;
padding: 0;
}
#safebrowsing-palm-message-tail {
height: 67px;
width: 24px;
max-width: 24px;
z-index: 3;
}
#safebrowsing-palm-message-titlebox {
padding-bottom: 5px;
padding-left: 10px;
}
#safebrowsing-palm-message-title {
padding-left: 5px;
}
#safebrowsing-palm-message-actionbox {
padding-top: 10px;
padding-bottom: 15px;
}
.safebrowsing-palm-bigtext {
font-size: 14px;
font-weight: bold;
}
.safebrowsing-palm-smalltext {
font-size: 10px;
}
#safebrowsing-palm-google-logo {
height: 32px;
max-height: 32px;
width: 78px;
}
#safebrowsing-dim-area-canvas {
background-image: url("chrome://browser/skin/safebrowsing/dim.png");
height: 0px;
}
#safebrowsing-dim-area-transp {
background-image: url("chrome://browser/skin/safebrowsing/dim.png");
height: 0px;
left: 0px;
top: 0px;
position: fixed;
display: none;
}
#safebrowsing-pref-caption {
background-color: #2222EE;
}
#safebrowsing-pref-caption text {
text-align: center;
color: #FFFFFF;
font-size: 150%;
font-family: arial, helvetica, sans-serif;
}
#safebrowsing-urlbar-icon {
padding-right: 2px;
}

Двоичные данные
browser/themes/winstripe/browser/safebrowsing/close16x16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 406 B

Двоичные данные
browser/themes/winstripe/browser/safebrowsing/dim.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичные данные
browser/themes/winstripe/browser/safebrowsing/tail.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 759 B

Двоичные данные
browser/themes/winstripe/browser/safebrowsing/warning16x16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 806 B

Двоичные данные
browser/themes/winstripe/browser/safebrowsing/warning24x24.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

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

@ -109,6 +109,10 @@ DIRS += downloads
endif
endif # MOZ_SUITE
ifdef MOZ_URL_CLASSIFIER
DIRS += url-classifier
endif
DIRS += \
commandlines \
startup \
@ -119,10 +123,6 @@ EXTRA_PP_COMPONENTS = nsDefaultCLH.js
endif # MOZ_XUL_APP
ifdef MOZ_URL_CLASSIFIER
DIRS += url-classifier
endif
ifdef MOZ_SUITE
DIRS += autocomplete/public
endif

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

@ -34,6 +34,8 @@
*
* ***** END LICENSE BLOCK ***** */
// TODO: We don't use this class very much. Try to use native nsIFile instead
// and remove this file.
/**
* A simple helper class that enables us to get or create the
@ -42,7 +44,6 @@
function PROT_ApplicationDirectory() {
this.debugZone = "appdir";
this.appDir_ = G_File.getProfileFile();
this.appDir_.append(PROT_GlobalStore.getAppDirectoryName());
G_Debug(this, "Application directory is " + this.appDir_.path);
}

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

@ -67,11 +67,23 @@
// of the list comprise its name, so the listmanager should easily
// be able to figure out what to do with what list (i.e., no
// need for step 4).
// TODO reading, writing, and whatnot code should be cleaned up
// TODO read/write asynchronously
// TODO more comprehensive update tests, for example add unittest check
// that the listmanagers tables are properly written on updates
/**
* The base pref name for where we keep table version numbers.
* We add append the table name to this and set the value to
* the version. E.g., tableversion.goog-black-enchash may have
* a value of 1.1234.
*/
const kTableVersionPrefPrefix = "urlclassifier.tableversion.";
/**
* Pref name for the update server.
* TODO: Add support for multiple providers.
*/
const kUpdateServerUrl = "urlclassifier.provider.0.updateURL";
/**
* A ListManager keeps track of black and white lists and knows
@ -83,46 +95,23 @@ function PROT_ListManager() {
this.debugZone = "listmanager";
G_debugService.enableZone(this.debugZone);
// We run long-lived operations on "threads" (user-level
// time-slices) in order to prevent locking up the UI
var threadConfig = {
"interleave": true,
"runtime": 80,
"delay": 400,
};
this.threadQueue_ = new TH_ThreadQueue(threadConfig);
this.appDir_ = null;
this.initAppDir_(); // appDir can be changed with setAppDir
this.currentUpdateChecker_ = null; // set when we toggle updates
this.rpcPending_ = false;
this.readingData_ = false;
this.prefs_ = new G_Preferences();
// We don't want to start checking against our lists until we've
// read them. But then again, we don't want to queue URLs to check
// forever. So if we haven't successfully read our lists after a
// certain amount of time, just pretend.
this.dataImportedAtLeastOnce_ = false;
var self = this;
new G_Alarm(function() { self.dataImportedAtLeastOnce_ = true; }, 60 * 1000);
// TOOD(tc): PREF
this.updateserverURL_ = PROT_GlobalStore.getUpdateserverURL();
this.updateserverURL_ = this.prefs_.getPref(kUpdateServerUrl, null);
// The lists we know about and the parses we can use to read
// them. Default all to the earlies possible version (1.-1); this
// version will get updated when successfully read from disk or
// fetch updates.
this.tablesKnown_ = {};
this.isTesting_ = false;
if (PROT_GlobalStore.isTesting()) {
if (this.isTesting_) {
// populate with some tables for unittesting
this.tablesKnown_ = {
// "goog-white-domain": new PROT_VersionParser("goog-white-domain", 1, -1),
// "goog-black-url" : new PROT_VersionParser("goog-black-url", 1, -1),
// "goog-black-enchash": new PROT_VersionParser("goog-black-enchash", 1, -1),
// "goog-white-url": new PROT_VersionParser("goog-white-url", 1, -1),
//
// A major version of zero means local, so don't ask for updates
// A major version of zero means local, so don't ask for updates
"test1-foo-domain" : new PROT_VersionParser("test1-foo-domain", 0, -1),
"test2-foo-domain" : new PROT_VersionParser("test2-foo-domain", 0, -1),
"test-white-domain" :
@ -137,6 +126,7 @@ function PROT_ListManager() {
this.tablesData = {};
// Lazily create urlCrypto (see tr-fetcher.js)
this.urlCrypto_ = null;
}
@ -152,6 +142,7 @@ PROT_ListManager.prototype.registerTable = function(tableName,
if (!table)
return false;
this.tablesKnown_[tableName] = table;
this.tablesData[tableName] = newUrlClassifierTable(tableName);
return true;
}
@ -214,7 +205,7 @@ PROT_ListManager.prototype.requireTableUpdates = function() {
* (where we store the lists) might not be available.
*/
PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
if (PROT_GlobalStore.isTesting())
if (this.isTesting_)
return;
// We might have been told about tables already, so see if we should be
@ -229,16 +220,14 @@ PROT_ListManager.prototype.maybeStartManagingUpdates = function() {
PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
// If we are testing or dont have an application directory yet, we should
// not start reading tables from disk or schedule remote updates
if (PROT_GlobalStore.isTesting() || !this.appDir_)
if (this.isTesting_)
return;
// We update tables if we have some tables that want updates. If there
// are no tables that want to be updated - we dont need to check anything.
if (this.requireTableUpdates() === true) {
G_Debug(this, "Starting managing lists");
// Read new table data if necessary - if we already have data we just
// skip reading from disk
new G_Alarm(BindToObject(this.readDataFiles, this), 0);
this.loadTableVersions_();
// Multiple warden can ask us to reenable updates at the same time, but we
// really just need to schedule a single update.
if (!this.currentUpdateChecker_)
@ -274,45 +263,6 @@ PROT_ListManager.prototype.stopUpdateChecker = function() {
}
}
/**
*
*/
PROT_ListManager.prototype.initAppDir_ = function() {
var dir = G_File.getProfileFile();
dir.append(PROT_GlobalStore.getAppDirectoryName());
if (!dir.exists()) {
try {
dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
} catch (e) {
G_Error(this, dir.path + " couldn't be created.");
}
}
this.setAppDir(dir);
}
/**
* Set the directory in which we should serialize data
*
* @param appDir An nsIFile pointing to our directory (should exist)
*/
PROT_ListManager.prototype.setAppDir = function(appDir) {
G_Debug(this, "setAppDir: " + appDir.path);
this.appDir_ = appDir;
}
/**
* Clears the specified table
* @param table Name of the table that we want to consult
* @returns true if the table exists, false otherwise
*/
PROT_ListManager.prototype.clearList = function(table) {
if (!this.tablesKnown_[table])
return false;
this.tablesData[table] = newUrlClassifierTable(this.tablesKnown_[table].type);
return true;
}
/**
* Provides an exception free way to look up the data in a table. We
* use this because at certain points our tables might not be loaded,
@ -379,112 +329,62 @@ PROT_ListManager.prototype.safeRemove = function(table, key) {
return this.tablesData[table].remove(key);
}
PROT_ListManager.prototype.getTable = function(tableName) {
return this.tablesData[tableName];
/**
* We store table versions in user prefs. This method pulls the values out of
* the user prefs and into the tablesKnown objects.
*/
PROT_ListManager.prototype.loadTableVersions_ = function() {
// Pull values out of prefs.
var prefBase = kTableVersionPrefPrefix;
for (var table in this.tablesKnown_) {
var version = this.prefs_.getPref(prefBase + table, "1.-1");
G_Debug(this, "loadTableVersion " + table + ": " + version);
var tokens = version.split(".");
G_Assert(this, tokens.length == 2, "invalid version number");
this.tablesKnown_[table].major = tokens[0];
this.tablesKnown_[table].minor = tokens[1];
}
}
/**
* Returns a list of files that we should try to read. We do not return
* tables that already have data or that we tried to read in the past.
* @returns array of file names
* Get lines of the form "[goog-black-enchash 1.1234]" or
* "[goog-white-url 1.1234 update]" and set the version numbers in the user
* pref.
* @param updateString String containing the response from the server.
* @return Array a list of table names
*/
PROT_ListManager.prototype.setTableVersions_ = function(updateString) {
var updatedTables = [];
PROT_ListManager.prototype.listUnreadDataFiles = function() {
// Now we need to read all of our nice data files.
var files = [];
for (var type in this.tablesKnown_) {
var table = this.tablesKnown_[type];
// Do not read data for tables that we already know about
if (this.tablesData[type] || table.didRead === true)
continue;
var prefBase = kTableVersionPrefPrefix;
var startPos = updateString.indexOf('[');
var endPos;
while (startPos != -1) {
// [ needs to be the start of a new line
if (0 == startPos || ('\n' == updateString[startPos - 1] &&
'\n' == updateString[startPos - 2])) {
endPos = updateString.indexOf('\n', startPos);
if (endPos != -1) {
var line = updateString.substring(startPos, endPos);
var versionParser = new PROT_VersionParser("dummy");
// Tables that dont require updates are not read from disk either
if (table.needsUpdate === false)
continue;
if (versionParser.fromString(line)) {
var tableName = versionParser.type;
var version = versionParser.major + '.' + versionParser.minor;
G_Debug(this, "Set table version for " + tableName + ": " + version);
this.prefs_.setPref(prefBase + tableName, version);
this.tablesKnown_[tableName].ImportVersion(versionParser);
var filename = type + ".sst";
var file = this.appDir_.clone();
file.append(filename);
if (file.exists() && file.isFile() && file.isReadable()) {
G_Debug(this, "Found saved data for: " + type);
files.push(file);
} else {
G_Debug(this, "Failed to find saved data for: " + type);
updatedTables.push(tableName);
}
}
}
// This could catch option params, but that's ok. The double newline
// check will skip over it.
startPos = updateString.indexOf('[', startPos + 1);
}
return files;
}
/**
* Reads all data files from storage
* @returns true if we started reading data from disk, false otherwise.
*/
PROT_ListManager.prototype.readDataFiles = function() {
if (this.readingData_ === true) {
G_Debug(this, "Already reading data from disk");
return true;
}
if (this.rpcPending_ === true) {
G_Debug(this, "Cannot read data files while an update RPC is pending");
new G_Alarm(BindToObject(this.readDataFiles, this), 10 * 1000);
return false;
}
// If we have no files there is nothing more for us todo
var files = this.listUnreadDataFiles();
if (!files.length)
return true;
this.readingData_ = true;
// Remember that we attempted to read from all tables
for (var type in this.tablesKnown_)
this.tablesKnown_[type].didRead = true;
// TODO: Should probably break this up on a thread
var data = "";
for (var i = 0; i < files.length; ++i) {
G_Debug(this, "Trying to read: " + files[i].path);
var gFile = new G_FileReader(files[i]);
data += gFile.read() + "\n";
gFile.close();
}
this.deserialize_(data, BindToObject(this.dataFromDisk, this));
return true;
}
/**
* Creates a WireFormatReader and calls deserialize on it.
*/
PROT_ListManager.prototype.deserialize_ = function(data, callback) {
var wfr = new PROT_WireFormatReader(this.threadQueue_, this.tablesData);
wfr.deserialize(data, callback);
}
/**
* A callback that is executed when we have read our table data from
* disk.
*
* @param tablesKnown An array that maps table name to current version
* @param tablesData An array that maps a table name to a Map which
* contains key value pairs.
*/
PROT_ListManager.prototype.dataFromDisk = function(tablesKnown, tablesData) {
G_Debug(this, "Called dataFromDisk");
this.importData_(tablesKnown, tablesData);
this.readingData_ = false;
// If we have more files to read schedule another round of reading
var files = this.listUnreadDataFiles();
if (files.length)
new G_Alarm(BindToObject(this.readDataFiles, this), 0);
return true;
return updatedTables;
}
/**
@ -500,7 +400,7 @@ PROT_ListManager.prototype.getRequestURL_ = function(url) {
var firstElement = true;
var requestMac = false;
for(var type in this.tablesKnown_) {
for (var type in this.tablesKnown_) {
// All tables with a major of 0 are internal tables that we never
// update remotely.
if (this.tablesKnown_[type].major == 0)
@ -551,14 +451,10 @@ PROT_ListManager.prototype.checkForUpdates = function() {
return false;
}
if (this.readingData_) {
G_Debug(this, 'checkForUpdate: still reading data from disk...');
// Reschedule the update to happen in a little while
new G_Alarm(BindToObject(this.checkForUpdates, this), 500);
if (!this.updateserverURL_) {
G_Debug(this, 'checkForUpdates: no update server url');
return false;
}
G_Debug(this, 'checkForUpdates: scheduling request..');
this.rpcPending_ = true;
this.xmlFetcher_ = new PROT_XMLFetcher();
@ -574,170 +470,110 @@ PROT_ListManager.prototype.checkForUpdates = function() {
*/
PROT_ListManager.prototype.rpcDone = function(data) {
G_Debug(this, "Called rpcDone");
/* Runs in a thread and executes the callback when ready */
this.deserialize_(data, BindToObject(this.dataReady, this));
this.rpcPending_ = false;
this.xmlFetcher_ = null;
if (!data || !data.length) {
G_Debug(this, "No data. Returning");
return;
}
// Only use the the data if the mac matches.
data = this.checkMac_(data);
if (data.length == 0) {
return;
}
// List updates (local lists) don't work yet. See bug 336203.
throw Exception("dbservice not yet implemented.");
var dbUpdateSrv = Cc["@mozilla.org/url-classifier/dbservice;1"]
.getService(Ci.nsIUrlClassifierDBService);
// Update the tables on disk.
try {
dbUpdateSrv.updateTables(data);
} catch (e) {
// dbUpdateSrv will throw an error if the background thread is already
// working. In this case, we just wait for the next scheduled update.
G_Debug(this, "Skipping update, write thread busy.");
return;
}
// While the update is being processed by a background thread, we need
// to also update the table versions.
var tableNames = this.setTableVersions_(data);
G_Debug(this, "Updated tables: " + tableNames);
for (var t = 0, name = null; name = tableNames[t]; ++t) {
// Create the table object if it doesn't exist.
if (!this.tablesData[name])
this.tablesData[name] = newUrlClassifierTable(name);
}
}
/**
* @returns Boolean indicating if it has read data from somewhere (e.g.,
* disk)
*/
PROT_ListManager.prototype.hasData = function() {
return !!this.dataImportedAtLeastOnce_;
}
/**
* Check if the mac passed, if required.
* Given the server response, extract out the new table lines and table
* version numbers. If the table has a mac, also check to see if it matches
* the data.
*
* @param oldParser The current version parser for the table
* @param newParser A version parser returned by the WireFormatReader
*
* @returns true if no mac is required, or if it is and the newParser says it
* didn't fail, false otherwise
* @param data String update string from the server
* @return String The same update string sans tables with invalid macs.
*/
PROT_ListManager.prototype.maybeCheckMac = function(oldParser, newParser) {
if (!oldParser.requireMac)
return true;
PROT_ListManager.prototype.checkMac_ = function(data) {
var dataTables = data.split('\n\n');
var returnString = "";
if (newParser.mac && !newParser.macFailed)
return true;
for (var table = null, t = 0; table = dataTables[t]; ++t) {
var firstLineEnd = table.indexOf("\n");
while (firstLineEnd == 0) {
// Skip leading blank lines
table = table.substring(1);
firstLineEnd = table.indexOf("\n");
}
if (firstLineEnd == -1) {
continue;
}
G_Debug(this, "mac required and it failed");
return false;
}
var versionLine = table.substring(0, firstLineEnd);
var versionParser = new PROT_VersionParser("dummy");
if (!versionParser.fromString(versionLine)) {
// Failed to parse the version string, skip this table.
G_Debug(this, "Failed to parse version string");
continue;
}
/**
* We've deserialized tables from disk or the update server, now let's
* swap them into the tables we're currently using.
*
* @param tablesKnown An array that maps table name to current version
* @param tablesData An array that maps a table name to a Map which
* contains key value pairs.
* @returns Array of strings holding the names of tables we updated
*/
PROT_ListManager.prototype.importData_ = function(tablesKnown, tablesData) {
this.dataImportedAtLeastOnce_ = true;
if (versionParser.mac && versionParser.macval.length > 0) {
// Includes a mac, so we check it.
var updateData = table.substring(firstLineEnd + 1) + '\n';
if (!this.urlCrypto_)
this.urlCrypto_ = new PROT_UrlCrypto();
var changes = [];
// If our data has changed, update it
if (tablesKnown && tablesData) {
// Update our tables with the new data
for (var name in tablesKnown) {
// WireFormatReader constructs VersionParsers from scratch and doesn't
// know about the requireMac flag, so check it now
if (tablesKnown[name] && this.tablesKnown_[name] &&
this.tablesKnown_[name] != tablesKnown[name] &&
this.maybeCheckMac(this.tablesKnown_[name], tablesKnown[name])) {
changes.push(name);
this.tablesKnown_[name].ImportVersion(tablesKnown[name]);
this.tablesData[name] = tablesData[name];
var computedMac = this.urlCrypto_.computeMac(updateData);
if (computedMac != versionParser.macval) {
G_Debug(this, "mac doesn't match: " + computedMac + " != " +
versionParser.macval)
continue;
}
} else {
// No mac in the return. Check to see if it's required. If it is
// required, skip this data.
if (this.tablesKnown_[versionParser.type] &&
this.tablesKnown_[versionParser.type].requireMac) {
continue;
}
}
// Everything looks ok, add it to the return string.
returnString += table + "\n\n";
}
return changes;
}
/**
* A callback that is executed when we have updated the tables from
* the server. We are provided with the new table versions and the
* corresponding data.
*
* @param tablesKnown An array that maps table name to current version
* @param tablesData An array that maps a table name to a Map which
* contains key value pairs.
*/
PROT_ListManager.prototype.dataReady = function(tablesKnown, tablesData) {
G_Debug(this, "Called dataReady");
// First, replace the current tables we're using
var changes = this.importData_(tablesKnown, tablesData);
// Then serialize the new tables to disk
if (changes.length) {
G_Debug(this, "Committing " + changes.length + " changed tables to disk.");
for (var i = 0; i < changes.length; i++) {
this.storeTable(changes[i],
this.tablesData[changes[i]],
this.tablesKnown_[changes[i]]);
}
}
this.rpcPending_ = false; // todo maybe can do away cuz asynch
G_Debug(this, "Done writing data to disk.");
}
/**
* Serialize a table to disk.
*
* @param tableName String holding the name of the table to serialize
*
* @param opt_table Reference to the Map holding the table (if omitted,
* we look the table up)
*
* @param opt_parser Reference to the versionparser for this table (if
* omitted we look the table up)
*/
PROT_ListManager.prototype.storeTable = function(tableName,
opt_table,
opt_parser) {
var table = opt_table ? opt_table : this.tablesData[tableName];
var parser = opt_parser ? opt_parser : this.tablesKnown_[tableName];
if (!table || ! parser)
G_Error(this, "Tried to serialize a non-existent table: " + tableName);
var wfw = new PROT_WireFormatWriter(this.threadQueue_);
wfw.serialize(table, parser, BindToObject(this.writeDataFile,
this,
tableName));
}
/**
* Takes a serialized table and writes it into our application directory.
*
* @param tableName String containing the name of the table, used to
* create the filename
*
* @param tableData Serialized version of the table
*/
PROT_ListManager.prototype.writeDataFile = function(tableName, tableData) {
var filename = tableName + ".sst";
G_Debug(this, "Serializing to " + filename);
try {
var tmpFile = G_File.createUniqueTempFile(filename);
var tmpFileWriter = new G_FileWriter(tmpFile);
tmpFileWriter.write(tableData);
tmpFileWriter.close();
} catch(e) {
G_Debug(this, e);
G_Error(this, "Couldn't write to temp file: " + filename);
}
// Now overwrite!
try {
tmpFile.moveTo(this.appDir_, filename);
} catch(e) {
G_Debug(this, e);
G_Error(this, "Couldn't overwrite existing table: " +
tmpFile.path + ', ' +
this.appDir_.path + ', ' + filename);
tmpFile.remove(false /* not recursive */);
}
G_Debug(this, "Serializing to " + filename + " finished.");
return returnString;
}
PROT_ListManager.prototype.QueryInterface = function(iid) {
if (iid.equals(Components.interfaces.nsISample) ||
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIUrlListManager))
return this;
@ -750,7 +586,7 @@ PROT_ListManager.prototype.QueryInterface = function(iid) {
// provider_name-semantic_type-table_type. For example, goog-white-enchash
// or goog-black-url.
function newUrlClassifierTable(name) {
G_Debug("protfactory", "Trying to create a new nsIUrlClassifierTable: " + name);
G_Debug("protfactory", "Creating a new nsIUrlClassifierTable: " + name);
var tokens = name.split('-');
var type = tokens[2];
var table = Cc['@mozilla.org/url-classifier/table;1?type=' + type]

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

@ -64,6 +64,12 @@
// the client to re-key. The client will issue a new HTTPS getkey request
// at this time if it has only issued one before
// We store the user key in this file. The key can be used to verify signed
// server updates.
const kKeyFilename = "kf.txt";
// If we don't have a key, we can get one at this url.
const kGetKeyUrl = "safebrowsing.provider.0.keyURL";
/**
* A key manager for UrlCrypto. There should be exactly one of these
@ -94,8 +100,7 @@ function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
this.rekeyTries_ = 0;
this.keyFilename_ = opt_keyFilename ?
opt_keyFilename :
PROT_GlobalStore.getKeyFilename();
opt_keyFilename : kKeyFilename;
// Convenience properties
this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
@ -154,8 +159,9 @@ PROT_UrlCryptoKeyManager.prototype.reKey = function() {
this.rekeyTries_++;
G_Debug(this, "Attempting to re-key");
var url = PROT_GlobalStore.getGetKeyURL();
if (!this.testing_)
var prefs = new G_Preferences();
var url = prefs.getPref(kGetKeyUrl, null);
if (!this.testing_ && url)
(new PROT_XMLFetcher()).get(url,
BindToObject(this.onGetKeyResponse, this));
}

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

@ -84,6 +84,7 @@ function PROT_VersionParser(type, opt_major, opt_minor, opt_requireMac) {
this.requireMac = !!opt_requireMac;
this.update = false;
this.needsUpdate = false; // used by ListManager to determine update policy
// Used by ListerManager to see if we have read data for this table from
// disk. Once we read a table from disk, we are not going to do so again
// but instead update remotely if necessary.
@ -115,8 +116,6 @@ PROT_VersionParser.prototype.ImportVersion = function(version) {
*/
PROT_VersionParser.prototype.toString = function() {
var s = "[" + this.type + " " + this.major + "." + this.minor + "]";
if (this.mac)
s += "[mac=" + this.macval + "]";
return s;
}
@ -157,10 +156,9 @@ PROT_VersionParser.prototype.processOldFormat_ = function(line) {
return true;
}
/**
* Takes a string like [name-of-table 1.1] and figures out the type
* and corresponding version numbers.
*
/**
* Takes a string like [name-of-table 1.1 [update]][mac=MAC] and figures out the
* type and corresponding version numbers.
* @returns true if the string could be parsed, false otherwise
*/
PROT_VersionParser.prototype.fromString = function(line) {
@ -212,7 +210,7 @@ PROT_VersionParser.prototype.processOptTokens_ = function(line) {
this.mac = true;
if (tokenparts.length < 2) {
G_Debug(this, "Found mac flag but not mac value!");
return false;
return false;
}
// The mac value may have "=" in it, so we can't just use tokenparts[1].
// Instead, just take the rest of tokens[i] after the first "="
@ -227,550 +225,27 @@ PROT_VersionParser.prototype.processOptTokens_ = function(line) {
return true;
}
/**
* A WireFormatWriter can serialize table data
*
* @param threadQueue A thread queue we should run on
*
* @constructor
*/
function PROT_WireFormatWriter(threadQueue) {
this.threadQueue_ = threadQueue;
this.debugZone = "wireformatwriter";
}
/**
* Serializes a table to a string.
*
* @param tableData Reference to the table data we should serialize
*
* @param vParser Reference to the version parser/unparser we should use
*
* @param callback Reference to a function we should call when complete.
* The callback will be invoked with a string holding
* the serialized data as an argument
*
* @returns False if the serializer is busy, else true
*
*/
PROT_WireFormatWriter.prototype.serialize = function(tableData,
vParser,
callback) {
if (this.callback_) {
G_Debug(this, "Serializer busy");
return false;
}
this.callback_ = callback;
this.current_ = 0;
this.serialized_ = vParser.toString() + "\n";
var cnt = {};
this.keyList_ = tableData.getKeys(cnt);
this.tableData_ = tableData;
this.threadQueue_.addWorker(BindToObject(this.doWorkUnit, this),
BindToObject(this.isComplete, this));
this.threadQueue_.run();
return true;
}
/**
* Serialize a chunk of data. This is periodically invoked by the
* threadqueue.
*/
PROT_WireFormatWriter.prototype.doWorkUnit = function() {
for (var i = 0; i < 10 && this.current_ < this.keyList_.length; i++) {
var key = this.keyList_[this.current_];
if (key) {
var value = this.tableData_.findValue(key);
this.serialized_ += "+" + key + "\t" + value + "\n";
}
this.current_++;
}
if (this.isComplete())
this.onComplete();
}
/**
* Are we done serializing? Called by the threadqueue to tell when
* to stop running this thread.
*
* @returns Boolean indicating if we're done serializing
*/
PROT_WireFormatWriter.prototype.isComplete = function() {
return this.current_ >= this.keyList_.length;
}
/**
* When we're done, call the callback
*/
PROT_WireFormatWriter.prototype.onComplete = function() {
this.callback_(this.serialized_);
this.callback_ = null;
}
/**
* A WireFormatReader can deserialize data.
*
* @constructor
*
* @param threadQueue A global thread queue that we use to schedule our
* work so that we do not take up too much time at one.
*
* @param opt_existingTablesData Optional reference to a map of tables
* into which we should merge the data being deserialized
*/
function PROT_WireFormatReader(threadQueue, opt_existingTablesData) {
this.debugZone = "wireformatreader";
this.existingTablesData_ = opt_existingTablesData;
this.threadQueue_ = threadQueue;
this.callback_ = null;
// For mac'ing updates
this.urlCrypto_ = null;
var keyUrl = null;
try {
keyUrl = PROT_GlobalStore.getGetKeyURL();
} catch (e) {
G_Debug(this, "No key url, disabling mac'ed updates");
}
if (keyUrl) {
this.urlCrypto_ = new PROT_UrlCrypto();
}
}
/**
* Starts a thread to process the input data.
*
* @param tableUpdate A string that contains the data for updating the
* tables that we know about.
*
* @param callback A user specificed callback that is being executed
* when data processing completes. The callback is provided
* with two arguments: tablesKnown and tablesData
*
* @returns true is new data is being process, or false on failure.
*/
PROT_WireFormatReader.prototype.deserialize = function(tableUpdate, callback) {
this.tableUpdate_ = tableUpdate;
this.tablesKnown_ = {}; // version parsers
this.tablesData_ = {}; // TRTables
if (this.callback_ != null) {
G_Debug(this, "previous deserialize is still running");
return false;
}
this.callback_ = callback;
this.offset_ = 0;
this.resetTableState_();
// On empty data, we just invoke the callback directly
if (!this.tableUpdate_ || !this.tableUpdate_.length) {
G_Debug(this, "No data. Calling back.");
this.onComplete();
return true;
}
this.threadQueue_.addWorker(BindToObject(this.processLine_, this),
BindToObject(this.isComplete, this));
this.threadQueue_.run();
return true;
}
/**
* Resets the per table state
*/
PROT_WireFormatReader.prototype.resetTableState_ = function() {
this.vParser_ = null;
this.insert_ = 0;
this.remove_ = 0;
}
/**
* Initalizes our state to process a new table.
* NOTE: For performance reasons, we might have drive the replacement of
* the table via a thread.
*
* @param line The input line that contains the table name and version
*/
PROT_WireFormatReader.prototype.processNewTable_ = function(line) {
this.vParser_ = new PROT_VersionParser("something");
// If there's an error, give up
if (!this.vParser_.fromString(line)) {
G_Debug(this, "Error in table header, skipping");
this.vParser_ = null;
return;
}
G_Debug(this, "processNewTable: " + this.vParser_.type + ": " +
this.vParser_.major + ":" + this.vParser_.minor + " update=" +
this.vParser_.update + " mac=" + this.vParser_.mac);
// Create temporary table. PROT_TRTable() blows up if it doesn't like its
// name, so mask and just set vParser to null
try {
this.tablesData_[this.vParser_.type] =
newUrlClassifierTable(this.vParser_.type);
} catch(e) {
G_Debug(this,
"Unable to initialize new TRTable, because of exception: " + e);
this.vParser_ = null;
return;
}
if (this.vParser_.update && this.existingTablesData_ &&
this.existingTablesData_[this.vParser_.type]) {
// If we update an existing table, we need to copy the old table
// data from the pre-existing tables
this.tablesData_[this.vParser_.type].replace(
this.existingTablesData_[this.vParser_.type]);
}
G_Debug(this, "processNewTable done");
}
/**
* Updates the contents of our temporary table. Exceptions should not
* be masked here -- they're caught at a higher level.
*
* @param line The input line that contains the new data
*/
PROT_WireFormatReader.prototype.processUpdateTable_ = function(line) {
// Regular update to the current version
var tokens = line.split("\t");
var operation = tokens[0][0];
var key = tokens[0].slice(1);
var value = tokens[1];
if (operation == "+") {
this.tablesData_[this.vParser_.type].insert(key, value);
this.insert_++;
} else {
this.tablesData_[this.vParser_.type].remove(key);
this.remove_++;
}
}
/**
* Finish processing a table
*/
PROT_WireFormatReader.prototype.endTable_ = function() {
G_Debug(this,
"Finished: " + this.vParser_.type + " +" +
this.insert_ + " -" + this.remove_ + " mac'ing: " + this.vParser_.mac);
if (this.vParser_.mac) {
var mac = this.urlCrypto_.finishMac();
if (mac != this.vParser_.macval) {
G_Debug(this, "Mac didn't match! (" + mac + " != "
+ this.vParser_.macval + ")");
this.vParser_.macFailed = true;
} else {
G_Debug(this, "Computed mac matched sent mac: " + this.vParser_.macval);
}
}
// Rollback if the mac failed.
if (this.vParser_.mac && this.vParser_.macFailed) {
this.tablesData_[this.vParser_.type] = undefined;
this.tablesKnown_[this.vParser_.type] = undefined;
G_Debug(this, "throwing away " + this.vParser_.type);
} else {
this.tablesKnown_[this.vParser_.type] = this.vParser_;
}
this.resetTableState_();
}
/**
* Processes a chunk of data. TODO(MC): Make it so the mac is computed from a
* saved begin and end offset instead of computing line by line.
*/
PROT_WireFormatReader.prototype.processLine_ = function() {
//G_Debug(this, '> processline');
for (var count = 0;
count < 5 && this.offset_ < this.tableUpdate_.length; count++) {
var newOffset = this.tableUpdate_.indexOf("\n", this.offset_);
var line = "";
if (newOffset == -1) {
this.offset_ = this.tableUpdate_.length;
} else {
line = this.tableUpdate_.slice(this.offset_, newOffset);
this.offset_ = newOffset + 1;
}
// Ignore empty lines if we currently do not have a table
if (!this.vParser_ && (!line || line[0] != '[')) {
continue;
}
if (!line) {
// End of one table - pop the results
this.endTable_();
} else if (line[0] == '[' && line.slice(-1) == ']') {
// processNewTable doesn't instantiate this.vParser_ in case of malformed
// headers, so the rest of the table lines will get skipped
this.processNewTable_(line);
if (this.vParser_ && this.vParser_.mac) {
this.urlCrypto_.initMac();
}
} else {
if (!this.vParser_) {
G_Debug(this, "Ignoring: " + line);
continue;
}
// Now we try to read a data line. However the table could've
// been corrupted on disk (e.g., the browser suddenly quit while
// we were in the middle of writing a line). If so,
// porcessUpdateTable() will throw. In this case we want to
// resynch the whole table, so we skip this line and then
// explicitly set the table's minor version to -1 (the lowest
// possible -- not 0, which means the table is local), causing a
// full update the next time we ask for it.
//
// We ignore the case where we wrote an incomplete but malformed
// table -- it fixes itself over time as the missing keys become
// less relevant.
try {
this.processUpdateTable_(line);
// Compute the mac over all the next lines until we finish the table
// TODO: This is ugly! line doesn't get the final '\n', so add it back.
// We could save the original line back up top, and use that instead,
// but that's yet more copying...
if (this.vParser_.mac) {
this.urlCrypto_.updateMacFromString(line + "\n");
}
} catch(e) {
G_Debug(this, "MALFORMED TABLE LINE: [" + line + "]\n" +
"Skipping this line, and resetting table " +
this.vParser_.type + " to version -1.\n" +
"(This as a result of exception: " + e + ")");
this.vParser_.minor = "-1";
}
}
}
// If the table we're reading is the last, then the for loop will
// fail, causing the table finish logic to be skipped. So here
// ensure that we finish up whatever table we're working on.
if (this.vParser_ && this.offset_ >= this.tableUpdate_.length) {
G_Debug(this,
"Finished (final table): " + this.vParser_.type + " +" +
this.insert_ + " -" + this.remove_);
this.endTable_();
}
if (this.isComplete()) this.onComplete();
//G_Debug(this, "< processline");
}
/**
* Returns true if all input data has been processed
*/
PROT_WireFormatReader.prototype.isComplete = function() {
return this.offset_ >= this.tableUpdate_.length;
}
/**
* Notifies our caller of completion.
*/
PROT_WireFormatReader.prototype.onComplete = function() {
G_Debug(this, "Processing complete. Executing callback.");
this.callback_(this.tablesKnown_, this.tablesData_);
this.callback_ = null;
}
function TEST_PROT_WireFormat() {
if (G_GDEBUG) {
// Sorry, this is incredibly ugly. What we need is continuations -- each
// unittest that passes invokes the next.
var z = "wireformat UNITTEST";
G_debugService.enableZone(z);
var z = "versionparser UNITTEST";
G_Debug(z, "Starting");
function testMalformedTables() {
G_Debug(z, "Testing malformed tables...");
var wfr = new PROT_WireFormatReader(new TH_ThreadQueue());
// Damn these unittests are ugly. Ugh. Now test handling corrupt tables
var data =
"[test1-black-url 1.1]\n" +
"+foo1\tbar\n" +
"+foo2\n" + // Malformed
"+foo3\tbar\n" +
"+foo4\tbar\n" +
"\n" +
"[test2-black-url 1.2]\n" +
"+foo1\tbar\n" +
"+foo2\tbar\n" +
"+foo3\tbar\n" +
"+foo4\tbar\n" +
"\n" +
"[test3-black-url 1.3]\n" +
"+foo1\tbar\n" +
"+foo2\tbar\n" +
"+foo3\tbar\n" +
"+foo4\n" + // Malformed
"\n" +
"[test4-black-url 1.4]\n" +
"+foo1\tbar\n" +
"+foo2\tbar\n" +
"+foo3\tbar\n" +
"+foo4\tbar\n" +
"\n" +
"[test4-badheader-url 1.asfd dfui]\n" + // Malformed header
"+foo1\tbar\n" +
"+foo2\tbar\n" +
"+foo3\tbar\n" +
"+foo4\tbar\n";
"\n" +
"[test4-bad-name-url 1.asfd dfui]\n" + // Malformed header
"+foo1\tbar\n" +
"+foo2\tbar\n" +
"+foo3\tbar\n" +
"+foo4\tbar\n";
var vp = new PROT_VersionParser("dummy");
G_Assert(z, vp.fromString("[foo-bar-url 1.234]"),
"failed to parse old format");
G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
G_Assert(z, "1" == vp.major, "failed to parse major");
G_Assert(z, "234" == vp.minor, "failed to parse minor");
function malformedcb(tablesKnown, tablesData) {
// Table has malformed data
G_Assert(z, tablesKnown["test1-black-url"].minor == "-1",
"test table 1 didn't get reset");
G_Assert(z, !!tablesData["test1-black-url"].exists("foo1"),
"test table 1 didn't set keys before the error");
G_Assert(z, !!tablesData["test1-black-url"].exists("foo3"),
"test table 1 didn't set keys after the error");
vp = new PROT_VersionParser("dummy");
G_Assert(z, vp.fromString("[foo-bar-url 1.234][mac=567]"),
"failed to parse new format");
G_Assert(z, "foo-bar-url" == vp.type, "failed to parse type");
G_Assert(z, "1" == vp.major, "failed to parse major");
G_Assert(z, "234" == vp.minor, "failed to parse minor");
G_Assert(z, true == vp.mac, "failed to parse mac");
G_Assert(z, "567" == vp.macval, "failed to parse macval");
// Table should be good
G_Assert(z, tablesKnown["test2-black-url"].minor == "2",
"test table 1 didnt get correct version number");
G_Assert(z, !!tablesData["test2-black-url"].exists("foo4"),
"test table 2 didnt parse properly");
// Table is malformed
G_Assert(z, tablesKnown["test3-black-url"].minor == "-1",
"test table 3 didn't get reset");
G_Assert(z, !tablesData["test3-black-url"].exists("foo4"),
"test table 3 somehow has its malformed line?");
// Table should be good
G_Assert(z, tablesKnown["test4-black-url"].minor == "4",
"test table 4 didn't get correct version number");
G_Assert(z, !!tablesData["test2-black-url"].exists("foo4"),
"test table 4 didnt parse properly");
// Table is malformed and should be unknown
G_Assert(z, !tablesKnown["test4-badheader-url"],
"test table header should't be known");
// Table is malformed and should be unknown
G_Assert(z, !tablesKnown["test4-bad-name-url"],
"test table header should't be known");
G_Debug(z, "PASSED");
};
wfr.deserialize(data, malformedcb);
};
var testTablesData = {};
var tableName = "test-black-url";
var data1 = "[" + tableName + " 1.5]\n";
for (var i = 0; i < 100; i++)
data1 += "+http://exists" + i + "\t1\n";
data1 += "-http://exists50\t1\n";
data1 += "-http://exists666\t1\n";
var data2 = "[" + tableName + " 1.7 update]\n";
for (var i = 0; i < 100; i++)
data2 += "-http://exists" + i + "\t1\n";
var set3Name = "test-white-domain";
var set3data = "";
// Use this string since it's the same in the update server unittest in
// /firefox/security/scripts/test/cheese/cheesey something.sh
for (var i = 1; i <= 3; i++) {
set3data += "+white" + i + ".com\t1\n";
}
var urlCrypto = new PROT_UrlCrypto();
var computedMac = urlCrypto.computeMac(set3data);
function data1cb(tablesKnown, tablesData) {
G_Assert(z,
tablesData[tableName] != null,
"Didn't get our table back");
for (var i = 0; i < 100; i++)
if (i != 50)
G_Assert(z,
tablesData[tableName].exists("http://exists" + i) == "1",
"Item addition broken");
G_Assert(z,
!tablesData[tableName].exists("http://exists50"),
"Item removal broken");
G_Assert(z,
!tablesData[tableName].exists("http://exists666"),
"Non-existent item");
G_Assert(z, tablesKnown[tableName].major == "1", "Major parsing broke");
G_Assert(z, tablesKnown[tableName].minor == "5", "Major parsing broke");
var wfr2 = new PROT_WireFormatReader(new TH_ThreadQueue(), tablesData);
wfr2.deserialize(data2, data2cb);
};
function data2cb(tablesKnown, tablesData) {
for (var i = 0; i < 100; i++)
G_Assert(z,
!tablesData[tableName].exists("http://exists" + i),
"Tables merge broken");
G_Assert(z, tablesKnown[tableName].major == "1", "Major parsing broke");
G_Assert(z, tablesKnown[tableName].minor == "7", "Major parsing broke");
var wfr3 = new PROT_WireFormatReader(new TH_ThreadQueue(), tablesData);
var data3 = "[test-white-domain 1.1][mac=" + computedMac + "]\n" +
set3data;
wfr3.deserialize(data3, data3cb);
};
// Test the MAC stuff
function data3cb(tablesKnown, tablesData) {
G_Assert(z, tablesData["test-white-domain"] != null,
"Didn't get our table back though the mac was correct");
// Now do the same thing with the wrong mac
computedMac = "012" + computedMac.substr(3);
var data4 = "[test-foo-domain 1.1][mac=" + computedMac + "]\n" +
set3data;
var wfr4 = new PROT_WireFormatReader(new TH_ThreadQueue(), tablesData);
wfr4.deserialize(data4, data4cb);
}
// Test the MAC stuff
function data4cb(tablesKnown, tablesData) {
G_Assert(z, !tablesKnown["test-foo-domain"],
"Deserialized though our mac was wrong");
G_Assert(z, !tablesData["test-foo-domain"],
"Deserialized though our mac was wrong");
testMalformedTables();
}
var wfr = new PROT_WireFormatReader(new TH_ThreadQueue());
wfr.deserialize(data1, data1cb);
G_Debug(z, "PASSED");
}
}

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

@ -121,18 +121,18 @@ PROT_XMLFetcher.prototype = {
// headers) when we try to read the response. Mask the exception
// by returning null response.
// TODO maybe masking this should be an option?
var responseText = null;
try {
G_Debug(this, "xml fetch status code: \"" +
fetcher._request.status + "\"");
if (fetcher._callback)
fetcher._callback(fetcher._request.responseText);
var responseText = fetcher._request.responseText;
} catch(e) {
G_Debug(this, "Caught exception trying to read xmlhttprequest " +
"status/response.");
G_Debug(this, e);
if (fetcher._callback)
fetcher._callback(null);
}
if (fetcher._callback)
fetcher._callback(responseText);
}
};

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

@ -1,7 +1,6 @@
toolkit.jar:
+ content/global/url-classifier/application.js (content/application.js)
+ content/global/url-classifier/enchash-decrypter.js (content/enchash-decrypter.js)
+ content/global/url-classifier/globalstore.js (content/globalstore.js)
+ content/global/url-classifier/listmanager.js (content/listmanager.js)
+ content/global/url-classifier/list-warden.js (content/list-warden.js)
+ content/global/url-classifier/map.js (content/map.js)

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

@ -16,7 +16,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
dump('*** loading subscript ' + libFile + '\n');
//dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);

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

@ -20,7 +20,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
dump('*** loading subscript ' + libFile + '\n');
//dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);

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

@ -16,7 +16,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
dump('*** loading subscript ' + libFile + '\n');
//dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);

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

@ -17,7 +17,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
dump('*** loading subscript ' + libFile + '\n');
//dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);

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

@ -21,7 +21,6 @@ const LIB_FILES = [
"chrome://global/content/url-classifier/moz/protocol4.js",
"chrome://global/content/url-classifier/application.js",
"chrome://global/content/url-classifier/globalstore.js",
"chrome://global/content/url-classifier/listmanager.js",
"chrome://global/content/url-classifier/url-crypto.js",
"chrome://global/content/url-classifier/url-crypto-key-manager.js", // dep url-crypto.js
@ -30,7 +29,7 @@ const LIB_FILES = [
];
for (var i = 0, libFile; libFile = LIB_FILES[i]; ++i) {
dump('*** loading subscript ' + libFile + '\n');
//dump('*** loading subscript ' + libFile + '\n');
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript(libFile);