mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-10 01:40:17 +01:00
784 lines
26 KiB
JavaScript
784 lines
26 KiB
JavaScript
|
/* ***** 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 = "phishdisplayer";
|
||
|
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.faqLinkId_ = "safebrowsing-palm-faq-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),
|
||
|
};
|
||
|
|
||
|
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 = this.getMeOutOfHereUrl_();
|
||
|
this.browser_.loadURI(url);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the url for "Get me out of here." This is the browser's default home
|
||
|
* page, or, about:blank.
|
||
|
* @return String url
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.getMeOutOfHereUrl_ = function() {
|
||
|
// Try to get their homepage from prefs.
|
||
|
var prefs = Cc["@mozilla.org/preferences-service;1"]
|
||
|
.getService(Ci.nsIPrefService).getDefaultBranch(null);
|
||
|
|
||
|
var url = "about:blank";
|
||
|
try {
|
||
|
url = prefs.getComplexValue("browser.startup.homepage",
|
||
|
Ci.nsIPrefLocalizedString).data;
|
||
|
} catch(e) {
|
||
|
G_Debug(this, "Couldn't get homepage pref: " + e);
|
||
|
}
|
||
|
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Invoked when the browser is resized
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.onBrowserResized_ = function(event) {
|
||
|
G_Debug(this, "Got resize for " + event.target);
|
||
|
|
||
|
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.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_);
|
||
|
|
||
|
// Add an event listener for when the browser resizes (e.g., user
|
||
|
// shows/hides the sidebar).
|
||
|
this.resizeHandler_ = BindToObject(this.onBrowserResized_, this);
|
||
|
this.browser_.addEventListener("resize",
|
||
|
this.resizeHandler_,
|
||
|
false);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @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.browser_.removeEventListener("resize",
|
||
|
this.resizeHandler_,
|
||
|
false);
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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");
|
||
|
if (!lockIcon)
|
||
|
return;
|
||
|
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");
|
||
|
if (!lockIcon)
|
||
|
return;
|
||
|
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_);
|
||
|
if (!urlbarIcon)
|
||
|
return;
|
||
|
urlbarIcon.setAttribute('level', 'warn');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Hides our urlbar icon
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.removeWarningInUrlbar_ = function() {
|
||
|
var urlbarIcon = this.doc_.getElementById(this.urlbarIconId_);
|
||
|
if (!urlbarIcon)
|
||
|
return;
|
||
|
urlbarIcon.setAttribute('level', 'safe');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
|
||
|
// Move message up a couple pixels so the tail overlaps it.
|
||
|
var messageY = tailY + tail.boxObject.height - 2;
|
||
|
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 + ")");
|
||
|
|
||
|
if (messageX < 0) {
|
||
|
// We're hanging off the left edge, switch to floating mode
|
||
|
tail.style.display = "none";
|
||
|
this.adjustLocationFloating_(message);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tail.style.top = tailY + "px";
|
||
|
tail.style.left = tailX + "px";
|
||
|
message.style.top = messageY + "px";
|
||
|
message.style.left = messageX + "px";
|
||
|
|
||
|
this.maybeAddScrollbars_();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Position the warning bubble with no reference element. In this case we
|
||
|
* just center the warning bubble at the top of the users window.
|
||
|
* @param message XULElement message bubble XUL container.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.adjustLocationFloating_ = function(message) {
|
||
|
// Compute X offset
|
||
|
var browserX = this.browser_.boxObject.x;
|
||
|
var browserXCenter = browserX + this.browser_.boxObject.width / 2;
|
||
|
var messageX = browserXCenter - (message.boxObject.width / 2);
|
||
|
|
||
|
// Compute Y offset (top of the browser window)
|
||
|
var messageY = this.browser_.boxObject.y;
|
||
|
|
||
|
// Position message
|
||
|
message.style.top = messageY + "px";
|
||
|
message.style.left = messageX + "px";
|
||
|
|
||
|
this.maybeAddScrollbars_();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a vertical scrollbar if we're falling out of the browser window.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.maybeAddScrollbars_ = function() {
|
||
|
var message = this.doc_.getElementById(this.messageId_);
|
||
|
|
||
|
var content = this.doc_.getElementById(this.messageContentId_);
|
||
|
var bottom = content.boxObject.y + content.boxObject.height;
|
||
|
var maxY = this.doc_.defaultView.innerHeight;
|
||
|
G_Debug(this, "bottom: " + bottom + ", maxY: " + maxY
|
||
|
+ ", new height: " + (maxY - content.boxObject.y));
|
||
|
if (bottom > maxY) {
|
||
|
var newHeight = maxY - content.boxObject.y;
|
||
|
if (newHeight < 1)
|
||
|
newHeight = 1;
|
||
|
|
||
|
content.style.height = newHeight + "px";
|
||
|
content.style.overflow = "auto";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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";
|
||
|
|
||
|
// set FAQ URL
|
||
|
var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
||
|
.getService(Components.interfaces.nsIURLFormatter);
|
||
|
var faqURL = formatter.formatURLPref("browser.safebrowsing.warning.infoURL");
|
||
|
var labelEl = this.doc_.getElementById(this.faqLinkId_);
|
||
|
labelEl.setAttribute("href", faqURL);
|
||
|
|
||
|
this.maybeAddScrollbars_();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If the warning bubble came up in error, this url goes to a form
|
||
|
* to notify the data provider.
|
||
|
* @return url String
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.getReportErrorURL_ = function() {
|
||
|
var badUrl = this.url_;
|
||
|
|
||
|
var url = gDataProvider.getReportErrorURL();
|
||
|
url += "&url=" + encodeURIComponent(badUrl);
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* URL for the user to report back to us. This is to provide the user
|
||
|
* with an action after being warned.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerBase.prototype.getReportGenericURL_ = function() {
|
||
|
var badUrl = this.url_;
|
||
|
|
||
|
var url = gDataProvider.getReportGenericURL();
|
||
|
url += "&url=" + encodeURIComponent(badUrl);
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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.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. First we make sure the overlay is loaded
|
||
|
* then call showMessageAfterOverlay_.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerCanvas.prototype.showMessage_ = function() {
|
||
|
G_Debug(this, "Showing message.");
|
||
|
|
||
|
// Load the overlay if we haven't already.
|
||
|
var dimmer = this.doc_.getElementById('safebrowsing-dim-area-canvas');
|
||
|
if (!dimmer) {
|
||
|
var onOverlayMerged = BindToObject(this.showMessageAfterOverlay_,
|
||
|
this);
|
||
|
var observer = new G_ObserverWrapper("xul-overlay-merged",
|
||
|
onOverlayMerged);
|
||
|
|
||
|
this.doc_.loadOverlay(
|
||
|
"chrome://browser/content/safebrowsing/warning-overlay.xul",
|
||
|
observer);
|
||
|
} else {
|
||
|
// The overlay is already loaded so we go ahead and call
|
||
|
// showMessageAfterOverlay_.
|
||
|
this.showMessageAfterOverlay_();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This does the actual work of showing the warning message.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerCanvas.prototype.showMessageAfterOverlay_ = function() {
|
||
|
this.messageShowing_ = true;
|
||
|
|
||
|
// Position the canvas overlay. Order here is significant, but don't ask me
|
||
|
// why for some of these. You need to:
|
||
|
// 1. get browser dimensions
|
||
|
// 2. add canvas to the document
|
||
|
// 3. unhide the dimmer (gray out overlay)
|
||
|
// 4. display to the canvas
|
||
|
// 5. unhide the warning message
|
||
|
// 6. update link targets in warning message
|
||
|
// 7. focus the warning message
|
||
|
|
||
|
// (1)
|
||
|
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 x = this.browser_.boxObject.x;
|
||
|
G_Debug(this, "browser x=" + w);
|
||
|
var y = this.browser_.boxObject.y;
|
||
|
G_Debug(this, "browser y=" + 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);
|
||
|
|
||
|
// (2)
|
||
|
// 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.style.left = x + 'px';
|
||
|
pageCanvas.style.top = y + 'px';
|
||
|
|
||
|
var dimarea = this.doc_.getElementById(this.dimAreaId_);
|
||
|
this.doc_.getElementById('main-window').insertBefore(pageCanvas,
|
||
|
dimarea);
|
||
|
|
||
|
// (3)
|
||
|
dimarea.style.left = x + 'px';
|
||
|
dimarea.style.top = y + 'px';
|
||
|
dimarea.style.width = w + 'px';
|
||
|
dimarea.style.height = h + 'px';
|
||
|
dimarea.hidden = false;
|
||
|
|
||
|
// (4)
|
||
|
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);
|
||
|
|
||
|
// (5)
|
||
|
this.showAndPositionWarning_();
|
||
|
|
||
|
// (6)
|
||
|
var link = this.doc_.getElementById('safebrowsing-palm-falsepositive-link');
|
||
|
link.href = this.getReportErrorURL_();
|
||
|
|
||
|
// (7)
|
||
|
this.doc_.getElementById(this.messageContentId_).focus();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Show and position the warning message. We position the waring message
|
||
|
* relative to the icon in the url bar, but if the element doesn't exist,
|
||
|
* (e.g., the user remove the url bar from her/his chrome), we anchor at the
|
||
|
* top of the window.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerCanvas.prototype.showAndPositionWarning_ = function() {
|
||
|
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";
|
||
|
|
||
|
// Determine if the refElement is visible.
|
||
|
if (this.isVisibleElement_(refElement)) {
|
||
|
// Show tail and position warning relative to refElement.
|
||
|
tail.hidden = false;
|
||
|
tail.style.display = "block";
|
||
|
this.adjustLocation_(message, tail, refElement);
|
||
|
} else {
|
||
|
// No ref element, position in the top center of window.
|
||
|
tail.hidden = true;
|
||
|
tail.style.display = "none";
|
||
|
this.adjustLocationFloating_(message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Boolean true if elt is a visible XUL element.
|
||
|
*/
|
||
|
PROT_PhishMsgDisplayerCanvas.prototype.isVisibleElement_ = function(elt) {
|
||
|
if (!elt)
|
||
|
return false;
|
||
|
|
||
|
// If it's on a collapsed/hidden toolbar, the x position is set to 0.
|
||
|
if (elt.boxObject.x == 0)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
|
||
|
// Hide the warning popup.
|
||
|
var message = this.doc_.getElementById(this.messageId_);
|
||
|
message.hidden = true;
|
||
|
message.style.display = "none";
|
||
|
var content = this.doc_.getElementById(this.messageContentId_);
|
||
|
content.style.height = "";
|
||
|
content.style.overflow = "";
|
||
|
|
||
|
var tail = this.doc_.getElementById(this.messageTailId_);
|
||
|
tail.hidden = true;
|
||
|
tail.style.display = "none";
|
||
|
|
||
|
// Remove the canvas element from the chrome document.
|
||
|
var pageCanvas = this.doc_.getElementById(this.pageCanvasId_);
|
||
|
pageCanvas.parentNode.removeChild(pageCanvas);
|
||
|
|
||
|
// Hide the dimmer.
|
||
|
var dimarea = this.doc_.getElementById(this.dimAreaId_);
|
||
|
dimarea.hidden = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
}
|