gecko-dev/browser/base/content/browser-fullZoom.js

366 lines
13 KiB
JavaScript

/*
#ifdef 0
* ***** 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 Content Preferences (cpref).
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
* Dão Gottwald <dao@mozilla.com>
* Ehsan Akhgari <ehsan.akhgari@gmail.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 *****
#endif
*/
// One of the possible values for the mousewheel.* preferences.
// From nsEventStateManager.cpp.
const MOUSE_SCROLL_ZOOM = 3;
/**
* Controls the "full zoom" setting and its site-specific preferences.
*/
var FullZoom = {
// Identifies the setting in the content prefs database.
name: "browser.content.full-zoom",
// The global value (if any) for the setting. Lazily loaded from the service
// when first requested, then updated by the pref change listener as it changes.
// If there is no global value, then this should be undefined.
get globalValue() {
var globalValue = Services.contentPrefs.getPref(null, this.name);
if (typeof globalValue != "undefined")
globalValue = this._ensureValid(globalValue);
delete this.globalValue;
return this.globalValue = globalValue;
},
// browser.zoom.siteSpecific preference cache
_siteSpecificPref: undefined,
// browser.zoom.updateBackgroundTabs preference cache
updateBackgroundTabs: undefined,
// whether we are in private browsing mode
_inPrivateBrowsing: false,
get siteSpecific() {
return !this._inPrivateBrowsing && this._siteSpecificPref;
},
//**************************************************************************//
// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
Ci.nsIObserver,
Ci.nsIContentPrefObserver,
Ci.nsISupportsWeakReference,
Ci.nsISupports]),
//**************************************************************************//
// Initialization & Destruction
init: function FullZoom_init() {
// Listen for scrollwheel events so we can save scrollwheel-based changes.
window.addEventListener("DOMMouseScroll", this, false);
// Register ourselves with the service so we know when our pref changes.
Services.contentPrefs.addObserver(this.name, this);
// We disable site-specific preferences in Private Browsing mode, because the
// content preferences module is disabled
Services.obs.addObserver(this, "private-browsing", true);
// Retrieve the initial status of the Private Browsing mode.
this._inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService).
privateBrowsingEnabled;
this._siteSpecificPref =
gPrefService.getBoolPref("browser.zoom.siteSpecific");
this.updateBackgroundTabs =
gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
// Listen for changes to the browser.zoom branch so we can enable/disable
// updating background tabs and per-site saving and restoring of zoom levels.
gPrefService.addObserver("browser.zoom.", this, true);
},
destroy: function FullZoom_destroy() {
Services.obs.removeObserver(this, "private-browsing");
gPrefService.removeObserver("browser.zoom.", this);
Services.contentPrefs.removeObserver(this.name, this);
window.removeEventListener("DOMMouseScroll", this, false);
},
//**************************************************************************//
// Event Handlers
// nsIDOMEventListener
handleEvent: function FullZoom_handleEvent(event) {
switch (event.type) {
case "DOMMouseScroll":
this._handleMouseScrolled(event);
break;
}
},
_handleMouseScrolled: function FullZoom__handleMouseScrolled(event) {
// Construct the "mousewheel action" pref key corresponding to this event.
// Based on nsEventStateManager::GetBasePrefKeyForMouseWheel.
var pref = "mousewheel";
if (event.axis == event.HORIZONTAL_AXIS)
pref += ".horizscroll";
if (event.shiftKey)
pref += ".withshiftkey";
else if (event.ctrlKey)
pref += ".withcontrolkey";
else if (event.altKey)
pref += ".withaltkey";
else if (event.metaKey)
pref += ".withmetakey";
else
pref += ".withnokey";
pref += ".action";
// Don't do anything if this isn't a "zoom" scroll event.
var isZoomEvent = false;
try {
isZoomEvent = (gPrefService.getIntPref(pref) == MOUSE_SCROLL_ZOOM);
} catch (e) {}
if (!isZoomEvent)
return;
// XXX Lazily cache all the possible action prefs so we don't have to get
// them anew from the pref service for every scroll event? We'd have to
// make sure to observe them so we can update the cache when they change.
// We have to call _applySettingToPref in a timeout because we handle
// the event before the event state manager has a chance to apply the zoom
// during nsEventStateManager::PostHandleEvent.
window.setTimeout(function (self) { self._applySettingToPref() }, 0, this);
},
// nsIObserver
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "nsPref:changed":
switch (aData) {
case "browser.zoom.siteSpecific":
this._siteSpecificPref =
gPrefService.getBoolPref("browser.zoom.siteSpecific");
break;
case "browser.zoom.updateBackgroundTabs":
this.updateBackgroundTabs =
gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
break;
}
break;
case "private-browsing":
switch (aData) {
case "enter":
this._inPrivateBrowsing = true;
break;
case "exit":
this._inPrivateBrowsing = false;
break;
}
break;
}
},
// nsIContentPrefObserver
onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) {
if (aGroup == Services.contentPrefs.grouper.group(gBrowser.currentURI))
this._applyPrefToSetting(aValue);
else if (aGroup == null) {
this.globalValue = this._ensureValid(aValue);
// If the current page doesn't have a site-specific preference,
// then its zoom should be set to the new global preference now that
// the global preference has changed.
if (!Services.contentPrefs.hasPref(gBrowser.currentURI, this.name))
this._applyPrefToSetting();
}
},
onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) {
if (aGroup == Services.contentPrefs.grouper.group(gBrowser.currentURI))
this._applyPrefToSetting();
else if (aGroup == null) {
this.globalValue = undefined;
// If the current page doesn't have a site-specific preference,
// then its zoom should be set to the default preference now that
// the global preference has changed.
if (!Services.contentPrefs.hasPref(gBrowser.currentURI, this.name))
this._applyPrefToSetting();
}
},
// location change observer
/**
* Called when the location of a tab changes.
* When that happens, we need to update the current zoom level if appropriate.
*
* @param aURI
* A URI object representing the new location.
* @param aIsTabSwitch
* Whether this location change has happened because of a tab switch.
* @param aBrowser
* (optional) browser object displaying the document
*/
onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
if (!aURI || (aIsTabSwitch && !this.siteSpecific))
return;
// Avoid the cps roundtrip and apply the default/global pref.
if (aURI.spec == "about:blank") {
this._applyPrefToSetting(undefined, aBrowser);
return;
}
var self = this;
Services.contentPrefs.getPref(aURI, this.name, function (aResult) {
// Check that we're still where we expect to be in case this took a while.
let browser = aBrowser || gBrowser.selectedBrowser;
if (aURI.equals(browser.currentURI)) {
self._applyPrefToSetting(aResult, browser);
}
});
},
// update state of zoom type menu item
updateMenu: function FullZoom_updateMenu() {
var menuItem = document.getElementById("toggle_zoom");
menuItem.setAttribute("checked", !ZoomManager.useFullZoom);
},
//**************************************************************************//
// Setting & Pref Manipulation
reduce: function FullZoom_reduce() {
ZoomManager.reduce();
this._applySettingToPref();
},
enlarge: function FullZoom_enlarge() {
ZoomManager.enlarge();
this._applySettingToPref();
},
reset: function FullZoom_reset() {
if (typeof this.globalValue != "undefined")
ZoomManager.zoom = this.globalValue;
else
ZoomManager.reset();
this._removePref();
},
/**
* Set the zoom level for the current tab.
*
* Per nsPresContext::setFullZoom, we can set the zoom to its current value
* without significant impact on performance, as the setting is only applied
* if it differs from the current setting. In fact getting the zoom and then
* checking ourselves if it differs costs more.
*
* And perhaps we should always set the zoom even if it was more expensive,
* since DocumentViewerImpl::SetTextZoom claims that child documents can have
* a different text zoom (although it would be unusual), and it implies that
* those child text zooms should get updated when the parent zoom gets set,
* and perhaps the same is true for full zoom
* (although DocumentViewerImpl::SetFullZoom doesn't mention it).
*
* So when we apply new zoom values to the browser, we simply set the zoom.
* We don't check first to see if the new value is the same as the current
* one.
**/
_applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
if ((!this.siteSpecific && !this._inPrivateBrowsing) ||
gInPrintPreviewMode)
return;
var browser = aBrowser || (gBrowser && gBrowser.selectedBrowser);
try {
if (browser.contentDocument instanceof Ci.nsIImageDocument ||
this._inPrivateBrowsing)
ZoomManager.setZoomForBrowser(browser, 1);
else if (typeof aValue != "undefined")
ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
else if (typeof this.globalValue != "undefined")
ZoomManager.setZoomForBrowser(browser, this.globalValue);
else
ZoomManager.setZoomForBrowser(browser, 1);
}
catch(ex) {}
},
_applySettingToPref: function FullZoom__applySettingToPref() {
if (!this.siteSpecific || gInPrintPreviewMode ||
content.document instanceof Ci.nsIImageDocument)
return;
var zoomLevel = ZoomManager.zoom;
Services.contentPrefs.setPref(gBrowser.currentURI, this.name, zoomLevel);
},
_removePref: function FullZoom__removePref() {
if (!(content.document instanceof Ci.nsIImageDocument))
Services.contentPrefs.removePref(gBrowser.currentURI, this.name);
},
//**************************************************************************//
// Utilities
_ensureValid: function FullZoom__ensureValid(aValue) {
if (isNaN(aValue))
return 1;
if (aValue < ZoomManager.MIN)
return ZoomManager.MIN;
if (aValue > ZoomManager.MAX)
return ZoomManager.MAX;
return aValue;
}
};