Bug 750903 - Land browser/metro/* r=mbrubeck

This commit is contained in:
Jim Mathies 2013-02-12 14:51:25 -06:00
Родитель 006d130536
Коммит b3a0b467c6
248 изменённых файлов: 37658 добавлений и 0 удалений

51
browser/metro/Makefile.in Normal file
Просмотреть файл

@ -0,0 +1,51 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/config.mk
DIRS = \
base \
components \
modules \
theme \
profile \
locales \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
DIRS += shell
endif
TEST_DIRS += base/tests
include $(topsrcdir)/config/rules.mk
#########################################
# application.ini
ifdef MOZILLA_OFFICIAL
DEFINES += -DMOZILLA_OFFICIAL
endif
GRE_MILESTONE := $(shell tail -n 1 $(topsrcdir)/config/milestone.txt 2>/dev/null || tail -1 $(topsrcdir)/config/milestone.txt)
GRE_BUILDID := $(shell cat $(DEPTH)/config/buildid)
DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DGRE_BUILDID=$(GRE_BUILDID)
# 'application.ini' breaks firefox build config. So we use something different.
metroapp.ini: metroapp.ini.in $(DEPTH)/config/buildid $(topsrcdir)/config/milestone.txt
$(RM) "metroapp.ini"
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
libs:: metroapp.ini
$(INSTALL) metroapp.ini $(FINAL_TARGET)
#########################################
GARBAGE += metroapp.ini

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

@ -0,0 +1,20 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/config.mk
DIST_SUBDIR=metro
DEFINES += -DAB_CD=$(MOZ_UI_LOCALE) \
-DPACKAGE=browser \
-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
$(NULL)
include $(topsrcdir)/config/rules.mk

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

@ -0,0 +1,124 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Responsible for zooming in to a given view rectangle
*/
const AnimatedZoom = {
startScale: null,
/** Starts an animated zoom to zoomRect. */
animateTo: function(aZoomRect) {
if (!aZoomRect)
return;
this.zoomTo = aZoomRect.clone();
if (this.animationDuration === undefined)
this.animationDuration = Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
Browser.forceChromeReflow();
this.start();
// Check if zooming animations were occuring before.
if (!this.zoomRect) {
this.updateTo(this.zoomFrom);
mozRequestAnimationFrame(this);
let event = document.createEvent("Events");
event.initEvent("AnimatedZoomBegin", true, true);
window.dispatchEvent(event);
}
},
start: function start() {
this.tab = Browser.selectedTab;
this.browser = this.tab.browser;
this.bcr = this.browser.getBoundingClientRect();
this.zoomFrom = this.zoomRect || this.getStartRect();
this.startScale = this.browser.scale;
this.beginTime = mozAnimationStartTime;
},
/** Get the visible rect, in device pixels relative to the content origin. */
getStartRect: function getStartRect() {
let browser = this.browser;
let scroll = browser.getRootView().getPosition();
return new Rect(scroll.x, scroll.y, this.bcr.width, this.bcr.height);
},
/** Update the visible rect, in device pixels relative to the content origin. */
updateTo: function(nextRect) {
// Stop animating if the browser has been destroyed
if (typeof this.browser.fuzzyZoom !== "function") {
this.reset();
return false;
}
let zoomRatio = this.bcr.width / nextRect.width;
let scale = this.startScale * zoomRatio;
let scrollX = nextRect.left * zoomRatio;
let scrollY = nextRect.top * zoomRatio;
this.browser.fuzzyZoom(scale, scrollX, scrollY);
this.zoomRect = nextRect;
return true;
},
/** Stop animation, zoom to point, and clean up. */
finish: function() {
if (!this.updateTo(this.zoomTo || this.zoomRect))
return;
// Check whether the zoom limits have changed since the animation started.
let browser = this.browser;
let finalScale = this.tab.clampZoomLevel(browser.scale);
if (browser.scale != finalScale)
browser.scale = finalScale; // scale= calls finishFuzzyZoom.
else
browser.finishFuzzyZoom();
this.reset();
browser._updateCSSViewport();
},
reset: function reset() {
this.beginTime = null;
this.zoomTo = null;
this.zoomFrom = null;
this.zoomRect = null;
this.startScale = null;
let event = document.createEvent("Events");
event.initEvent("AnimatedZoomEnd", true, true);
window.dispatchEvent(event);
},
isZooming: function isZooming() {
return this.beginTime != null;
},
sample: function(aTimeStamp) {
try {
let tdiff = aTimeStamp - this.beginTime;
let counter = tdiff / this.animationDuration;
if (counter < 1) {
// update browser to interpolated rectangle
let rect = this.zoomFrom.blend(this.zoomTo, counter);
if (this.updateTo(rect))
mozRequestAnimationFrame(this);
} else {
// last cycle already rendered final scaled image, now clean up
this.finish();
}
} catch(e) {
this.finish();
throw e;
}
}
};

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

@ -0,0 +1,91 @@
/*
* BrowserTouchHandler
*
* Receives touch events from our input event capturing in input.js
* and relays appropriate events to content. Also handles requests
* from content for UI.
*/
const BrowserTouchHandler = {
_debugEvents: false,
init: function init() {
// Misc. events we catch during the bubbling phase
document.addEventListener("PopupChanged", this, false);
document.addEventListener("CancelTouchSequence", this, false);
// Messages sent from content.js
messageManager.addMessageListener("Content:ContextMenu", this);
},
// Content forwarding the contextmenu command
onContentContextMenu: function onContentContextMenu(aMessage) {
let contextInfo = { name: aMessage.name,
json: aMessage.json,
target: aMessage.target };
// Touch input selection handling
if (!InputSourceHelper.isPrecise) {
if (SelectionHelperUI.isActive()) {
// Selection handler is active.
if (aMessage.json.types.indexOf("selected-text") != -1) {
// long tap on existing selection. The incoming message has the
// string data, so reset the selection handler and invoke the
// context menu.
SelectionHelperUI.closeEditSession();
} else {
// Weird, context menu request with no selected text and
// SelectionHelperUI is active? Might be a bug, warn. Fall
// through anyway, the context menu handler will look in the
// incoming message for content types it knows how to handle.
Util.dumpLn("long tap on empty selection with SelectionHelperUI active?");
SelectionHelperUI.closeEditSession();
}
} else if (SelectionHelperUI.canHandle(aMessage)) {
SelectionHelperUI.openEditSession(aMessage);
return;
}
}
// Check to see if we have context menu item(s) that apply to what
// was clicked on.
if (ContextMenuUI.showContextMenu(contextInfo)) {
let event = document.createEvent("Events");
event.initEvent("CancelTouchSequence", true, false);
document.dispatchEvent(event);
} else {
// Send the MozEdgeUIGesture to input.js to
// toggle the context ui.
let event = document.createEvent("Events");
event.initEvent("MozEdgeUIGesture", true, false);
window.dispatchEvent(event);
}
},
/*
* Events
*/
handleEvent: function handleEvent(aEvent) {
// ignore content events we generate
if (this._debugEvents)
Util.dumpLn("BrowserTouchHandler:", aEvent.type);
switch (aEvent.type) {
case "PopupChanged":
case "CancelTouchSequence":
if (!aEvent.detail)
ContextMenuUI.reset();
break;
}
},
receiveMessage: function receiveMessage(aMessage) {
if (this._debugEvents) Util.dumpLn("BrowserTouchHandler:", aMessage.name);
switch (aMessage.name) {
// Content forwarding the contextmenu command
case "Content:ContextMenu":
this.onContentContextMenu(aMessage);
break;
}
},
};

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

@ -0,0 +1,301 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* context menu command handlers
*/
var ContextCommands = {
_picker: null,
get clipboard() {
return Cc["@mozilla.org/widget/clipboardhelper;1"]
.getService(Ci.nsIClipboardHelper);
},
get docRef() {
return Browser.selectedBrowser.contentWindow.document;
},
/*
* Context menu handlers
*/
copy: function cc_copy() {
let target = ContextMenuUI.popupState.target;
if (target.localName == "browser") {
if (ContextMenuUI.popupState.string != "undefined") {
this.clipboard.copyString(ContextMenuUI.popupState.string,
this.docRef);
} else {
let x = ContextMenuUI.popupState.x;
let y = ContextMenuUI.popupState.y;
let json = {x: x, y: y, command: "copy" };
target.messageManager.sendAsyncMessage("Browser:ContextCommand", json);
}
} else {
target.editor.copy();
}
if (target)
target.focus();
},
paste: function cc_paste() {
let target = ContextMenuUI.popupState.target;
if (target.localName == "browser") {
let x = ContextMenuUI.popupState.x;
let y = ContextMenuUI.popupState.y;
let json = {x: x, y: y, command: "paste" };
target.messageManager.sendAsyncMessage("Browser:ContextCommand", json);
} else {
target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
target.focus();
}
},
pasteAndGo: function cc_pasteAndGo() {
let target = ContextMenuUI.popupState.target;
target.editor.selectAll();
target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
BrowserUI.goToURI();
},
selectAll: function cc_selectAll() {
let target = ContextMenuUI.popupState.target;
if (target.localName == "browser") {
let x = ContextMenuUI.popupState.x;
let y = ContextMenuUI.popupState.y;
let json = {x: x, y: y, command: "select-all" };
target.messageManager.sendAsyncMessage("Browser:ContextCommand", json);
} else {
target.editor.selectAll();
target.focus();
}
},
openInNewTab: function cc_openInNewTab() {
Browser.addTab(ContextMenuUI.popupState.linkURL, false, Browser.selectedTab);
ContextUI.peekTabs();
},
saveToWinLibrary: function cc_saveToWinLibrary(aType) {
let popupState = ContextMenuUI.popupState;
let browser = popupState.target;
// ContentAreaUtils internalSave relies on various desktop related prefs,
// values, and functionality. We want to be more direct by saving the
// image to the users Windows Library. If users want to specify the
// save location they can use the context menu option 'Save (type) To'.
let dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
let saveLocationPath = dirSvc.get(aType, Components.interfaces.nsIFile);
let fileInfo = new ContentAreaUtils.FileInfo();
ContentAreaUtils.initFileInfo(fileInfo, popupState.mediaURL,
browser.documentURI.originCharset,
null, null, null);
let filename =
ContentAreaUtils.getNormalizedLeafName(fileInfo.fileName, fileInfo.fileExt);
saveLocationPath.append(filename);
// Start the background save process
ContentAreaUtils.internalPersist({
sourceURI : fileInfo.uri,
sourceDocument : null,
sourceReferrer : browser.documentURI,
targetContentType : popupState.contentType,
targetFile : saveLocationPath,
sourceCacheKey : null,
sourcePostData : null,
bypassCache : false,
initiatingWindow : this.docRef.defaultView
});
},
saveVideo: function cc_saveVideo() {
this.saveToWinLibrary("Vids");
},
saveVideoTo: function cc_saveVideoTo() {
this.saveFileAs(ContextMenuUI.popupState);
},
saveImage: function cc_saveImage() {
this.saveToWinLibrary("Pict");
},
saveImageTo: function cc_saveImageTo() {
this.saveFileAs(ContextMenuUI.popupState);
},
copyLink: function cc_copyLink() {
this.clipboard.copyString(ContextMenuUI.popupState.linkURL,
this.docRef);
},
copyEmail: function cc_copyEmail() {
this.clipboard.copyString(ContextMenuUI.popupState.linkURL.substr(ContextMenuUI.popupState.linkURL.indexOf(':')+1),
this.docRef);
},
copyPhone: function cc_copyPhone() {
this.clipboard.copyString(ContextMenuUI.popupState.linkURL.substr(ContextMenuUI.popupState.linkURL.indexOf(':')+1),
this.docRef);
},
copyImageLocation: function cc_copyImageLocation() {
this.clipboard.copyString(ContextMenuUI.popupState.mediaURL,
this.docRef);
},
bookmarkLink: function cc_bookmarkLink() {
let state = ContextMenuUI.popupState;
let uri = Util.makeURI(state.linkURL);
let title = state.linkTitle || state.linkURL;
try {
Bookmarks.addForURI(uri, title);
} catch (e) {
return;
}
let message = Strings.browser.GetStringFromName("alertLinkBookmarked");
let toaster = Cc["@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService);
toaster.showAlertNotification(null, message, "", false, "", null);
},
sendCommand: function cc_playVideo(aCommand) {
let browser = ContextMenuUI.popupState.target;
browser.messageManager.sendAsyncMessage("Browser:ContextCommand", { command: aCommand });
},
editBookmark: function cc_editBookmark() {
let target = ContextMenuUI.popupState.target;
target.startEditing();
},
removeBookmark: function cc_removeBookmark() {
let target = ContextMenuUI.popupState.target;
target.remove();
},
shortcutBookmark: function cc_shortcutBookmark() {
let target = ContextMenuUI.popupState.target;
Util.createShortcut(target.getAttribute("title"), target.getAttribute("uri"), target.getAttribute("src"), "bookmark");
},
findInPage: function cc_findInPage() {
dump('ContextCommand: findInPage');
FindHelperUI.show();
},
viewOnDesktop: function cc_viewOnDesktop() {
dump('ContextCommand: viewOnDesktop');
Appbar.onViewOnDesktop();
},
/*
* Utilities
*/
/*
* isAccessibleDirectory
*
* Test to see if the directory exists and is writable.
*/
isAccessibleDirectory: function isAccessibleDirectory(aDirectory) {
return aDirectory && aDirectory.exists() && aDirectory.isDirectory() &&
aDirectory.isWritable();
},
/*
* saveFileAs
*
* Browse for a location and then save a file to the local system using
* standard download prefs.
*
* @param aFileUriString path to file
*/
saveFileAs: function saveFileAs(aPopupState) {
let srcUri = null;
let mediaURL = aPopupState.mediaURL;
try {
srcUri = Util.makeURI(mediaURL, null, null);
} catch (ex) {
Util.dumpLn("could not parse:", mediaURL);
return;
}
let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
let windowTitle = Strings.browser.GetStringFromName("browserForSaveLocation");
picker.init(window, windowTitle, Ci.nsIFilePicker.modeSave);
// prefered filename
let fileName = mediaURL.substring(mediaURL.lastIndexOf("/") + 1);
if (fileName.length)
picker.defaultString = fileName;
// prefered file extension
let fileExtension = mediaURL.substring(mediaURL.lastIndexOf(".") + 1);
if (fileExtension.length)
picker.defaultExtension = fileExtension;
picker.appendFilters(Ci.nsIFilePicker.filterImages);
// prefered save location
var dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
picker.displayDirectory = dnldMgr.userDownloadsDirectory;
try {
let lastDir = Services.prefs.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
if (this.isAccessibleDirectory(lastDir))
picker.displayDirectory = lastDir;
}
catch (e) { }
this._picker = picker;
this._pickerUrl = mediaURL;
this._pickerContentDisp = aPopupState.contentDisposition;
this._contentType = aPopupState.contentType;
picker.open(ContextCommands);
},
/*
* Filepicker callback
*/
done: function done(aSuccess) {
let picker = this._picker;
this._picker = null;
if (aSuccess == Ci.nsIFilePicker.returnCancel)
return;
let file = picker.file;
if (file == null)
return;
try {
if (file.exists())
file.remove(false);
} catch (e) {}
ContentAreaUtils.internalSave(this._pickerUrl, null, null,
this._pickerContentDisp,
this._contentType, false, "SaveImageTitle",
new AutoChosen(file, Util.makeURI(this._pickerUrl, null, null)),
getBrowser().documentURI, this.docRef, true, null);
var newDir = file.parent.QueryInterface(Ci.nsILocalFile);
Services.prefs.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, newDir);
},
};
function AutoChosen(aFileAutoChosen, aUriAutoChosen) {
this.file = aFileAutoChosen;
this.uri = aUriAutoChosen;
}

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

@ -0,0 +1,818 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var Cc = Components.classes;
var Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
var loginManager = {
/* ---------- private members ---------- */
get _formFillService() {
return this._formFillService =
Cc["@mozilla.org/satchel/form-fill-controller;1"].
getService(Ci.nsIFormFillController);
},
_nsLoginInfo : null, // Constructor for nsILoginInfo implementation
_debug : false, // mirrors signon.debug
_remember : true, // mirrors signon.rememberSignons preference
init : function () {
// Cache references to current |this| in utility objects
this._webProgressListener._domEventListener = this._domEventListener;
this._webProgressListener._pwmgr = this;
this._domEventListener._pwmgr = this;
this._observer._pwmgr = this;
// Form submit observer checks forms for new logins and pw changes.
Services.obs.addObserver(this._observer, "earlyformsubmit", false);
// Get constructor for nsILoginInfo
this._nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo);
// Preferences. Add observer so we get notified of changes.
Services.prefs.addObserver("signon.", this._observer, false);
// Get current preference values.
this._debug = Services.prefs.getBoolPref("signon.debug");
this._remember = Services.prefs.getBoolPref("signon.rememberSignons");
// WebProgressListener for getting notification of new doc loads.
var progress = Cc["@mozilla.org/docloaderservice;1"].
getService(Ci.nsIWebProgress);
progress.addProgressListener(this._webProgressListener,
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
},
/*
* log
*
* Internal function for logging debug messages to the Error Console window
*/
log : function (message) {
if (!this._debug)
return;
dump("PasswordUtils: " + message + "\n");
Services.console.logStringMessage("PasswordUtils: " + message);
},
/* fillForm
*
* Fill the form with login information if we can find it.
*/
fillForm: function (form) {
this._fillForm(form, true, true, false, null);
},
/*
* _fillform
*
* Fill the form with login information if we can find it.
* autofillForm denotes if we should fill the form in automatically
* ignoreAutocomplete denotes if we should ignore autocomplete=off attributes
* foundLogins is an array of nsILoginInfo
*/
_fillForm : function (form, autofillForm, ignoreAutocomplete,
clobberPassword, foundLogins) {
// Heuristically determine what the user/pass fields are
// We do this before checking to see if logins are stored,
// so that the user isn't prompted for a master password
// without need.
var [usernameField, passwordField, ignored] =
this._getFormFields(form, false);
// Need a valid password field to do anything.
if (passwordField == null)
return false;
// If the fields are disabled or read-only, there's nothing to do.
if (passwordField.disabled || passwordField.readOnly ||
usernameField && (usernameField.disabled ||
usernameField.readOnly)) {
this.log("not filling form, login fields disabled");
return false;
}
// Discard logins which have username/password values that don't
// fit into the fields (as specified by the maxlength attribute).
// The user couldn't enter these values anyway, and it helps
// with sites that have an extra PIN to be entered (bug 391514)
var maxUsernameLen = Number.MAX_VALUE;
var maxPasswordLen = Number.MAX_VALUE;
// If attribute wasn't set, default is -1.
if (usernameField && usernameField.maxLength >= 0)
maxUsernameLen = usernameField.maxLength;
if (passwordField.maxLength >= 0)
maxPasswordLen = passwordField.maxLength;
var logins = foundLogins.filter(function (l) {
var fit = (l.username.length <= maxUsernameLen &&
l.password.length <= maxPasswordLen);
if (!fit)
this.log("Ignored " + l.username + " login: won't fit");
return fit;
}, this);
// Nothing to do if we have no matching logins available.
if (logins.length == 0)
return false;
// The reason we didn't end up filling the form, if any. We include
// this in the formInfo object we send with the passwordmgr-found-logins
// notification. See the _notifyFoundLogins docs for possible values.
var didntFillReason = null;
// Attach autocomplete stuff to the username field, if we have
// one. This is normally used to select from multiple accounts,
// but even with one account we should refill if the user edits.
if (usernameField)
this._attachToInput(usernameField);
// Don't clobber an existing password.
if (passwordField.value && !clobberPassword) {
didntFillReason = "existingPassword";
this._notifyFoundLogins(didntFillReason, usernameField,
passwordField, foundLogins, null);
return false;
}
// If the form has an autocomplete=off attribute in play, don't
// fill in the login automatically. We check this after attaching
// the autocomplete stuff to the username field, so the user can
// still manually select a login to be filled in.
var isFormDisabled = false;
if (!ignoreAutocomplete &&
(this._isAutocompleteDisabled(form) ||
this._isAutocompleteDisabled(usernameField) ||
this._isAutocompleteDisabled(passwordField))) {
isFormDisabled = true;
this.log("form not filled, has autocomplete=off");
}
// Variable such that we reduce code duplication and can be sure we
// should be firing notifications if and only if we can fill the form.
var selectedLogin = null;
if (usernameField && usernameField.value) {
// If username was specified in the form, only fill in the
// password if we find a matching login.
var username = usernameField.value.toLowerCase();
let matchingLogins = logins.filter(function(l)
l.username.toLowerCase() == username);
if (matchingLogins.length) {
selectedLogin = matchingLogins[0];
} else {
didntFillReason = "existingUsername";
this.log("Password not filled. None of the stored " +
"logins match the username already present.");
}
} else if (logins.length == 1) {
selectedLogin = logins[0];
} else {
// We have multiple logins. Handle a special case here, for sites
// which have a normal user+pass login *and* a password-only login
// (eg, a PIN). Prefer the login that matches the type of the form
// (user+pass or pass-only) when there's exactly one that matches.
let matchingLogins;
if (usernameField)
matchingLogins = logins.filter(function(l) l.username);
else
matchingLogins = logins.filter(function(l) !l.username);
if (matchingLogins.length == 1) {
selectedLogin = matchingLogins[0];
} else {
didntFillReason = "multipleLogins";
this.log("Multiple logins for form, so not filling any.");
}
}
var didFillForm = false;
if (selectedLogin && autofillForm && !isFormDisabled) {
// Fill the form
if (usernameField)
usernameField.value = selectedLogin.username;
passwordField.value = selectedLogin.password;
didFillForm = true;
} else if (selectedLogin && !autofillForm) {
// For when autofillForm is false, but we still have the information
// to fill a form, we notify observers.
didntFillReason = "noAutofillForms";
Services.obs.notifyObservers(form, "passwordmgr-found-form", didntFillReason);
this.log("autofillForms=false but form can be filled; notified observers");
} else if (selectedLogin && isFormDisabled) {
// For when autocomplete is off, but we still have the information
// to fill a form, we notify observers.
didntFillReason = "autocompleteOff";
Services.obs.notifyObservers(form, "passwordmgr-found-form", didntFillReason);
this.log("autocomplete=off but form can be filled; notified observers");
}
this._notifyFoundLogins(didntFillReason, usernameField, passwordField,
foundLogins, selectedLogin);
return didFillForm;
},
/*
* _getPasswordOrigin
*
* Get the parts of the URL we want for identification.
*/
_getPasswordOrigin : function (uriString, allowJS) {
var realm = "";
try {
var uri = Services.io.newURI(uriString, null, null);
if (allowJS && uri.scheme == "javascript")
return "javascript:"
realm = uri.scheme + "://" + uri.host;
// If the URI explicitly specified a port, only include it when
// it's not the default. (We never want "http://foo.com:80")
var port = uri.port;
if (port != -1) {
var handler = Services.io.getProtocolHandler(uri.scheme);
if (port != handler.defaultPort)
realm += ":" + port;
}
} catch (e) {
// bug 159484 - disallow url types that don't support a hostPort.
// (although we handle "javascript:..." as a special case above.)
this.log("Couldn't parse origin for " + uriString);
realm = null;
}
return realm;
},
_getActionOrigin : function (form) {
var uriString = form.action;
// A blank or mission action submits to where it came from.
if (uriString == "")
uriString = form.baseURI; // ala bug 297761
return this._getPasswordOrigin(uriString, true);
},
/*
* _isAutoCompleteDisabled
*
* Returns true if the page requests autocomplete be disabled for the
* specified form input.
*/
_isAutocompleteDisabled : function (element) {
if (element && element.hasAttribute("autocomplete") &&
element.getAttribute("autocomplete").toLowerCase() == "off")
return true;
return false;
},
/*
* _getFormFields
*
* Returns the username and password fields found in the form.
* Can handle complex forms by trying to figure out what the
* relevant fields are.
*
* Returns: [usernameField, newPasswordField, oldPasswordField]
*
* usernameField may be null.
* newPasswordField will always be non-null.
* oldPasswordField may be null. If null, newPasswordField is just
* "theLoginField". If not null, the form is apparently a
* change-password field, with oldPasswordField containing the password
* that is being changed.
*/
_getFormFields : function (form, isSubmission) {
var usernameField = null;
// Locate the password field(s) in the form. Up to 3 supported.
// If there's no password field, there's nothing for us to do.
var pwFields = this._getPasswordFields(form, isSubmission);
if (!pwFields)
return [null, null, null];
// Locate the username field in the form by searching backwards
// from the first passwordfield, assume the first text field is the
// username. We might not find a username field if the user is
// already logged in to the site.
for (var i = pwFields[0].index - 1; i >= 0; i--) {
var element = form.elements[i];
var fieldType = (element.hasAttribute("type") ?
element.getAttribute("type").toLowerCase() :
element.type);
if (fieldType == "text" ||
fieldType == "email" ||
fieldType == "url" ||
fieldType == "tel" ||
fieldType == "number") {
usernameField = element;
break;
}
}
if (!usernameField)
this.log("(form -- no username field found)");
// If we're not submitting a form (it's a page load), there are no
// password field values for us to use for identifying fields. So,
// just assume the first password field is the one to be filled in.
if (!isSubmission || pwFields.length == 1)
return [usernameField, pwFields[0].element, null];
// Try to figure out WTF is in the form based on the password values.
var oldPasswordField, newPasswordField;
var pw1 = pwFields[0].element.value;
var pw2 = pwFields[1].element.value;
var pw3 = (pwFields[2] ? pwFields[2].element.value : null);
if (pwFields.length == 3) {
// Look for two identical passwords, that's the new password
if (pw1 == pw2 && pw2 == pw3) {
// All 3 passwords the same? Weird! Treat as if 1 pw field.
newPasswordField = pwFields[0].element;
oldPasswordField = null;
} else if (pw1 == pw2) {
newPasswordField = pwFields[0].element;
oldPasswordField = pwFields[2].element;
} else if (pw2 == pw3) {
oldPasswordField = pwFields[0].element;
newPasswordField = pwFields[2].element;
} else if (pw1 == pw3) {
// A bit odd, but could make sense with the right page layout.
newPasswordField = pwFields[0].element;
oldPasswordField = pwFields[1].element;
} else {
// We can't tell which of the 3 passwords should be saved.
this.log("(form ignored -- all 3 pw fields differ)");
return [null, null, null];
}
} else { // pwFields.length == 2
if (pw1 == pw2) {
// Treat as if 1 pw field
newPasswordField = pwFields[0].element;
oldPasswordField = null;
} else {
// Just assume that the 2nd password is the new password
oldPasswordField = pwFields[0].element;
newPasswordField = pwFields[1].element;
}
}
return [usernameField, newPasswordField, oldPasswordField];
},
/* ---------- Private methods ---------- */
/*
* _getPasswordFields
*
* Returns an array of password field elements for the specified form.
* If no pw fields are found, or if more than 3 are found, then null
* is returned.
*
* skipEmptyFields can be set to ignore password fields with no value.
*/
_getPasswordFields : function (form, skipEmptyFields) {
// Locate the password fields in the form.
var pwFields = [];
for (var i = 0; i < form.elements.length; i++) {
var element = form.elements[i];
if (!(element instanceof Ci.nsIDOMHTMLInputElement) ||
element.type != "password")
continue;
if (skipEmptyFields && !element.value)
continue;
pwFields[pwFields.length] = {
index : i,
element : element
};
}
// If too few or too many fields, bail out.
if (pwFields.length == 0) {
this.log("(form ignored -- no password fields.)");
return null;
} else if (pwFields.length > 3) {
this.log("(form ignored -- too many password fields. [got " +
pwFields.length + "])");
return null;
}
return pwFields;
},
/**
* Notify observers about an attempt to fill a form that resulted in some
* saved logins being found for the form.
*
* This does not get called if the login manager attempts to fill a form
* but does not find any saved logins. It does, however, get called when
* the login manager does find saved logins whether or not it actually
* fills the form with one of them.
*
* @param didntFillReason {String}
* the reason the login manager didn't fill the form, if any;
* if the value of this parameter is null, then the form was filled;
* otherwise, this parameter will be one of these values:
* existingUsername: the username field already contains a username
* that doesn't match any stored usernames
* existingPassword: the password field already contains a password
* autocompleteOff: autocomplete has been disabled for the form
* or its username or password fields
* multipleLogins: we have multiple logins for the form
* noAutofillForms: the autofillForms pref is set to false
*
* @param usernameField {HTMLInputElement}
* the username field detected by the login manager, if any;
* otherwise null
*
* @param passwordField {HTMLInputElement}
* the password field detected by the login manager
*
* @param foundLogins {Array}
* an array of nsILoginInfos that can be used to fill the form
*
* @param selectedLogin {nsILoginInfo}
* the nsILoginInfo that was/would be used to fill the form, if any;
* otherwise null; whether or not it was actually used depends on
* the value of the didntFillReason parameter
*/
_notifyFoundLogins : function (didntFillReason, usernameField,
passwordField, foundLogins, selectedLogin) {
// We need .setProperty(), which is a method on the original
// nsIWritablePropertyBag. Strangley enough, nsIWritablePropertyBag2
// doesn't inherit from that, so the additional QI is needed.
let formInfo = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag2).
QueryInterface(Ci.nsIWritablePropertyBag);
formInfo.setPropertyAsACString("didntFillReason", didntFillReason);
formInfo.setPropertyAsInterface("usernameField", usernameField);
formInfo.setPropertyAsInterface("passwordField", passwordField);
formInfo.setProperty("foundLogins", foundLogins.concat());
formInfo.setPropertyAsInterface("selectedLogin", selectedLogin);
Services.obs.notifyObservers(formInfo, "passwordmgr-found-logins", null);
},
/*
* _attachToInput
*
* Hooks up autocomplete support to a username field, to allow
* a user editing the field to select an existing login and have
* the password field filled in.
*/
_attachToInput : function (element) {
this.log("attaching autocomplete stuff");
element.addEventListener("blur",
this._domEventListener, false);
element.addEventListener("DOMAutoComplete",
this._domEventListener, false);
this._formFillService.markAsLoginManagerField(element);
},
/*
* _fillDocument
*
* Called when a page has loaded. For each form in the document,
* we ask the parent process to see if it can be filled with a stored login
* and fill them in with the results
*/
_fillDocument : function (doc) {
var forms = doc.forms;
if (!forms || forms.length == 0)
return;
this.log("_fillDocument processing " + forms.length +
" forms on " + doc.documentURI);
var autofillForm = !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView) &&
Services.prefs.getBoolPref("signon.autofillForms");
// actionOrigins is a list of each form's action origins for this
// document. The parent process needs this to find the passwords
// for each action origin.
var actionOrigins = [];
for (var i = 0; i < forms.length; i++) {
var form = forms[i];
let [, passwordField, ] = this._getFormFields(form, false);
if (passwordField) {
var actionOrigin = this._getActionOrigin(form);
actionOrigins.push(actionOrigin);
}
} // foreach form
if (!actionOrigins.length)
return;
var formOrigin = this._getPasswordOrigin(doc.documentURI);
var foundLogins = this._getPasswords(actionOrigins, formOrigin);
for (var i = 0; i < forms.length; i++) {
var form = forms[i];
var actionOrigin = this._getActionOrigin(form);
if (foundLogins[actionOrigin]) {
this.log("_fillDocument processing form[" + i + "]");
this._fillForm(form, autofillForm, false, false,
foundLogins[actionOrigin]);
}
} // foreach form
},
/*
* _getPasswords
*
* Retrieve passwords from parent process and prepare logins to be passed to
* _fillForm. Returns map from action origins to passwords.
*/
_getPasswords: function(actionOrigins, formOrigin) {
// foundLogins will be a map from action origins to passwords.
var message = sendSyncMessage("PasswordMgr:GetPasswords", {
actionOrigins: actionOrigins,
formOrigin: formOrigin
})[0];
// XXX need to somehow respond to the UI being busy
// not needed for Fennec yet
var foundLogins = message.foundLogins;
// Each password will be a JSON-unserialized object, but they need to be
// nsILoginInfo's.
for (var key in foundLogins) {
var logins = foundLogins[key];
for (var i = 0; i < logins.length; i++) {
var obj = logins[i];
logins[i] = new this._nsLoginInfo();
logins[i].init(obj.hostname, obj.formSubmitURL, obj.httpRealm,
obj.username, obj.password,
obj.usernameField, obj.passwordField);
}
}
return foundLogins;
},
/*
* _onFormSubmit
*
* Called by the our observer when notified of a form submission.
* [Note that this happens before any DOM onsubmit handlers are invoked.]
* Looks for a password change in the submitted form, so we can update
* our stored password.
*/
_onFormSubmit : function (form) {
var doc = form.ownerDocument;
var win = doc.defaultView;
if (PrivateBrowsingUtils.isWindowPrivate(win)) {
// We won't do anything in private browsing mode anyway,
// so there's no need to perform further checks.
this.log("(form submission ignored in private browsing mode)");
return;
}
// If password saving is disabled (globally or for host), bail out now.
if (!this._remember)
return;
var hostname = this._getPasswordOrigin(doc.documentURI);
var formSubmitURL = this._getActionOrigin(form);
// Get the appropriate fields from the form.
var [usernameField, newPasswordField, oldPasswordField] =
this._getFormFields(form, true);
// Need at least 1 valid password field to do anything.
if (newPasswordField == null)
return;
// Check for autocomplete=off attribute. We don't use it to prevent
// autofilling (for existing logins), but won't save logins when it's
// present.
// XXX spin out a bug that we don't update timeLastUsed in this case?
if (this._isAutocompleteDisabled(form) ||
this._isAutocompleteDisabled(usernameField) ||
this._isAutocompleteDisabled(newPasswordField) ||
this._isAutocompleteDisabled(oldPasswordField)) {
this.log("(form submission ignored -- autocomplete=off found)");
return;
}
sendSyncMessage("PasswordMgr:FormSubmitted", {
hostname: hostname,
formSubmitURL: formSubmitURL,
usernameField: usernameField ? usernameField.name : "",
usernameValue: usernameField ? usernameField.value : "",
passwordField: newPasswordField.name,
passwordValue: newPasswordField.value,
hasOldPasswordField: !!oldPasswordField
});
},
/* ---------- Utility objects ---------- */
/*
* _observer object
*
* Internal utility object, implements the nsIObserver interface.
* Used to receive notification for: form submission, preference changes.
*/
_observer : {
_pwmgr : null,
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsIFormSubmitObserver,
Ci.nsISupportsWeakReference]),
// nsFormSubmitObserver
notify : function (formElement, aWindow, actionURI) {
// Counterintuitively, form submit observers fire for content that
// may not be the content in this context.
if (aWindow.top != content)
return true;
this._pwmgr.log("observer notified for form submission.");
// We're invoked before the content's |onsubmit| handlers, so we
// can grab form data before it might be modified (see bug 257781).
try {
this._pwmgr._onFormSubmit(formElement);
} catch (e) {
this._pwmgr.log("Caught error in onFormSubmit: " + e);
}
return true; // Always return true, or form submit will be canceled.
},
observe : function (aSubject, aTopic, aData) {
this._pwmgr._debug = Services.prefs.getBoolPref("signon.debug");
this._pwmgr._remember = Services.prefs.getBoolPref("signon.rememberSignons");
}
},
/*
* _webProgressListener object
*
* Internal utility object, implements nsIWebProgressListener interface.
* This is attached to the document loader service, so we get
* notifications about all page loads.
*/
_webProgressListener : {
_pwmgr : null,
_domEventListener : null,
QueryInterface : XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
onStateChange : function (aWebProgress, aRequest,
aStateFlags, aStatus) {
// STATE_START is too early, doc is still the old page.
if (!(aStateFlags & Ci.nsIWebProgressListener.STATE_TRANSFERRING))
return;
if (!this._pwmgr._remember)
return;
var domWin = aWebProgress.DOMWindow;
var domDoc = domWin.document;
// Only process things which might have HTML forms.
if (!(domDoc instanceof Ci.nsIDOMHTMLDocument))
return;
if (this._pwmgr._debug) {
let requestName = "(null)";
if (aRequest) {
try {
requestName = aRequest.name;
} catch (ex if ex.result == Components.results.NS_ERROR_NOT_IMPLEMENTED) {
// do nothing - leave requestName = "(null)"
}
}
this._pwmgr.log("onStateChange accepted: req = " + requestName +
", flags = 0x" + aStateFlags.toString(16));
}
// Fastback doesn't fire DOMContentLoaded, so process forms now.
if (aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) {
this._pwmgr.log("onStateChange: restoring document");
return this._pwmgr._fillDocument(domDoc);
}
// Add event listener to process page when DOM is complete.
domDoc.addEventListener("DOMContentLoaded",
this._domEventListener, false);
return;
},
// stubs for the nsIWebProgressListener interfaces which we don't use.
onProgressChange : function() { throw "Unexpected onProgressChange"; },
onLocationChange : function() { throw "Unexpected onLocationChange"; },
onStatusChange : function() { throw "Unexpected onStatusChange"; },
onSecurityChange : function() { throw "Unexpected onSecurityChange"; }
},
/*
* _domEventListener object
*
* Internal utility object, implements nsIDOMEventListener
* Used to catch certain DOM events needed to properly implement form fill.
*/
_domEventListener : {
_pwmgr : null,
QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
Ci.nsISupportsWeakReference]),
handleEvent : function (event) {
if (!event.isTrusted)
return;
this._pwmgr.log("domEventListener: got event " + event.type);
switch (event.type) {
case "DOMAutoComplete":
case "blur":
var acInputField = event.target;
var acForm = acInputField.form;
// If the username is blank, bail out now -- we don't want
// _fillForm() to try filling in a login without a username
// to filter on (bug 471906).
if (!acInputField.value)
return;
// Make sure the username field _fillForm will use is the
// same field as the autocomplete was activated on. If
// not, the DOM has been altered and we'll just give up.
var [usernameField, passwordField, ignored] =
this._pwmgr._getFormFields(acForm, false);
if (usernameField == acInputField && passwordField) {
var actionOrigin = this._pwmgr._getActionOrigin(acForm);
var formOrigin = this._pwmgr._getPasswordOrigin(acForm.ownerDocument.documentURI);
var foundLogins = this._pwmgr._getPasswords([actionOrigin], formOrigin);
this._pwmgr._fillForm(acForm, true, true, true, foundLogins[actionOrigin]);
} else {
this._pwmgr.log("Oops, form changed before AC invoked");
}
return;
case "DOMContentLoaded":
// Only process when we need to
event.currentTarget.removeEventListener(event.type, this);
if (this._pwmgr._remember && event.target instanceof Ci.nsIDOMHTMLDocument)
this._pwmgr._fillDocument(event.target);
break;
default:
this._pwmgr.log("Oops! This event unexpected.");
return;
}
}
}
};
loginManager.init();

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

@ -0,0 +1,180 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var PageActions = {
_handlers: null,
init: function init() {
if (this._handlers)
return;
this._handlers = [];
document.getElementById("pageactions-container").addEventListener("click", this, true);
this.register("pageaction-reset", this.updatePagePermissions, this);
this.register("pageaction-password", this.updateForgetPassword, this);
#ifdef NS_PRINTING
this.register("pageaction-saveas", this.updatePageSaveAs, this);
#endif
this.register("pageaction-share", this.updateShare, this);
CharsetMenu.init();
},
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "click":
IdentityUI.hide();
break;
}
},
/**
* @param aId id of a pageaction element
* @param aCallback function that takes an element and returns true if it should be visible
* @param aThisObj (optional) scope object for aCallback
*/
register: function register(aId, aCallback, aThisObj) {
this.init();
this._handlers.push({id: aId, callback: aCallback, obj: aThisObj});
},
updateSiteMenu: function updateSiteMenu() {
this.init();
this._handlers.forEach(function(action) {
let node = document.getElementById(action.id);
if (node)
node.hidden = !action.callback.call(action.obj, node);
});
this._updateAttributes();
},
get _loginManager() {
delete this._loginManager;
return this._loginManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
},
// Permissions we track in Page Actions
_permissions: ["popup", "offline-app", "geolocation", "desktop-notification", "openWebappsManage"],
_forEachPermissions: function _forEachPermissions(aHost, aCallback) {
let pm = Services.perms;
for (let i = 0; i < this._permissions.length; i++) {
let type = this._permissions[i];
if (!pm.testPermission(aHost, type))
continue;
let perms = pm.enumerator;
while (perms.hasMoreElements()) {
let permission = perms.getNext().QueryInterface(Ci.nsIPermission);
if (permission.host == aHost.asciiHost && permission.type == type)
aCallback(type);
}
}
},
updatePagePermissions: function updatePagePermissions(aNode) {
let host = Browser.selectedBrowser.currentURI;
let permissions = [];
this._forEachPermissions(host, function(aType) {
permissions.push("pageactions." + aType);
});
if (!this._loginManager.getLoginSavingEnabled(host.prePath)) {
// If rememberSignons is false, then getLoginSavingEnabled returns false
// for all pages, so we should just ignore it (Bug 601163).
if (Services.prefs.getBoolPref("signon.rememberSignons"))
permissions.push("pageactions.password");
}
let descriptions = permissions.map(function(s) Strings.browser.GetStringFromName(s));
aNode.setAttribute("description", descriptions.join(", "));
return (permissions.length > 0);
},
updateForgetPassword: function updateForgetPassword(aNode) {
let host = Browser.selectedBrowser.currentURI;
let logins = this._loginManager.findLogins({}, host.prePath, "", "");
return logins.some(function(login) login.hostname == host.prePath);
},
forgetPassword: function forgetPassword(aEvent) {
let host = Browser.selectedBrowser.currentURI;
let lm = this._loginManager;
lm.findLogins({}, host.prePath, "", "").forEach(function(login) {
if (login.hostname == host.prePath)
lm.removeLogin(login);
});
this.hideItem(aEvent.target);
aEvent.stopPropagation(); // Don't hide the site menu.
},
clearPagePermissions: function clearPagePermissions(aEvent) {
let pm = Services.perms;
let host = Browser.selectedBrowser.currentURI;
this._forEachPermissions(host, function(aType) {
pm.remove(host.asciiHost, aType);
// reset the 'remember' counter for permissions that support it
if (["geolocation", "desktop-notification"].indexOf(aType) != -1)
Services.contentPrefs.setPref(host.asciiHost, aType + ".request.remember", 0);
});
let lm = this._loginManager;
if (!lm.getLoginSavingEnabled(host.prePath))
lm.setLoginSavingEnabled(host.prePath, true);
this.hideItem(aEvent.target);
aEvent.stopPropagation(); // Don't hide the site menu.
},
pinSite : function pinCurrentSite() {
if (Browser.selectedBrowser.currentURI.spec.length == 0) {
return;
}
Browser.pinSite();
},
unpinSite : function unpinCurrentSite() {
if (Browser.selectedBrowser.currentURI.spec.length == 0) {
return;
}
Browser.unpinSite();
},
updatePageSaveAs: function updatePageSaveAs(aNode) {
// Check for local XUL content
let contentWindow = Browser.selectedBrowser.contentWindow;
return !(contentWindow && contentWindow.document instanceof XULDocument);
},
updateShare: function updateShare(aNode) {
return Util.isShareableScheme(Browser.selectedBrowser.currentURI.scheme);
},
hideItem: function hideItem(aNode) {
aNode.hidden = true;
this._updateAttributes();
},
_updateAttributes: function _updateAttributes() {
let container = document.getElementById("pageactions-container");
let visibleNodes = container.querySelectorAll("pageaction:not([hidden=true])");
let visibleCount = visibleNodes.length;
if (visibleCount == 0)
return;
for (let i = 0; i < visibleCount; i++)
visibleNodes[i].classList.remove("odd-last-child");
visibleNodes[visibleCount - 1].classList.add("last-child");
if (visibleCount % 2)
visibleNodes[visibleCount - 1].classList.add("odd");
}
};

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

@ -0,0 +1,144 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
Components.utils.import("resource://services-sync/main.js");
/**
* Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
* fills it with the user's synced tabs.
*
* @param aSet Control implementing nsIDOMXULSelectControlElement.
* @param aSetUIAccess The UI element that should be hidden when Sync is
* disabled. Must sanely support 'hidden' attribute.
* You may only have one UI access point at this time.
*/
function RemoteTabsView(aSet, aSetUIAccess) {
this._set = aSet;
this._set.controller = this;
this._uiAccessElement = aSetUIAccess;
// Sync uses special voodoo observers.
// If you want to change this code, talk to the fx-si team
Weave.Svc.Obs.add("weave:service:setup-complete", this);
Weave.Svc.Obs.add("weave:service:sync:finish", this);
Weave.Svc.Obs.add("weave:service:start-over", this);
if (this.isSyncEnabled() ) {
this.populateTabs();
this.populateGrid();
this.setUIAccessVisible(true);
}
else {
this.setUIAccessVisible(false);
}
}
RemoteTabsView.prototype = {
_set: null,
_uiAccessElement: null,
handleItemClick: function tabview_handleItemClick(aItem) {
let url = aItem.getAttribute("value");
BrowserUI.goToURI(url);
},
observe: function(subject, topic, data) {
switch (topic) {
case "weave:service:setup-complete":
this.populateTabs();
this.setUIAccessVisible(true);
break;
case "weave:service:sync:finish":
this.populateGrid();
break;
case "weave:service:start-over":
this.setUIAccessVisible(false);
break;
}
},
setUIAccessVisible: function setUIAccessVisible(aVisible) {
this._uiAccessElement.hidden = !aVisible;
},
populateGrid: function populateGrid() {
let tabsEngine = Weave.Service.engineManager.get("tabs");
let list = this._set;
let seenURLs = new Set();
for (let [guid, client] in Iterator(tabsEngine.getAllClients())) {
client.tabs.forEach(function({title, urlHistory, icon}) {
let url = urlHistory[0];
if (tabsEngine.locallyOpenTabMatchesURL(url) || seenURLs.has(url)) {
return;
}
seenURLs.add(url);
// If we wish to group tabs by client, we should be looking for records
// of {type:client, clientName, class:{mobile, desktop}} and will
// need to readd logic to reset seenURLs for each client.
let item = this._set.appendItem((title || url), url);
item.setAttribute("iconURI", Weave.Utils.getIcon(icon));
}, this);
}
},
populateTabs: function populateTabs() {
Weave.Service.scheduler.scheduleNextSync(0);
},
destruct: function destruct() {
Weave.Svc.Obs.remove("weave:service:setup-complete", this);
Weave.Svc.Obs.remove("weave:engine:sync:finish", this);
Weave.Svc.Obs.remove("weave:service:logout:start-over", this);
},
isSyncEnabled: function isSyncEnabled() {
return (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED);
}
};
let RemoteTabsStartView = {
_view: null,
get _grid() { return document.getElementById("start-remotetabs-grid"); },
init: function init() {
let vbox = document.getElementById("start-remotetabs");
this._view = new RemoteTabsView(this._grid, vbox);
},
uninit: function uninit() {
this._view.destruct();
},
show: function show() {
this._grid.arrangeItems();
}
};
let RemoteTabsPanelView = {
_view: null,
get _grid() { return document.getElementById("remotetabs-list"); },
get visible() { return PanelUI.isPaneVisible("remotetabs-container"); },
init: function init() {
//decks are fragile, don't hide the tab panel(bad things happen), hide link.
let menuEntry = document.getElementById("menuitem-remotetabs");
this._view = new RemoteTabsView(this._grid, menuEntry);
},
show: function show() {
this._grid.arrangeItems();
},
uninit: function uninit() {
this._view.destruct();
}
};

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

@ -0,0 +1,201 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
let prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
// singleton to provide data-level functionality to the views
let TopSites = {
pinSite: function(aId, aSlotIndex) {
Util.dumpLn("TopSites.pinSite: " + aId + ", (TODO)");
// FIXME: implementation needed
return true; // operation was successful
},
unpinSite: function(aId) {
Util.dumpLn("TopSites.unpinSite: " + aId + ", (TODO)");
// FIXME: implementation needed
return true; // operation was successful
},
hideSite: function(aId) {
Util.dumpLn("TopSites.hideSite: " + aId + ", (TODO)");
// FIXME: implementation needed
return true; // operation was successful
},
restoreSite: function(aId) {
Util.dumpLn("TopSites.restoreSite: " + aId + ", (TODO)");
// FIXME: implementation needed
return true; // operation was successful
}
};
function TopSitesView(aGrid, maxSites) {
this._set = aGrid;
this._set.controller = this;
this._topSitesMax = maxSites;
// handle selectionchange DOM events from the grid/tile group
this._set.addEventListener("context-action", this, false);
}
TopSitesView.prototype = {
_set:null,
_topSitesMax: null,
handleItemClick: function tabview_handleItemClick(aItem) {
let url = aItem.getAttribute("value");
BrowserUI.goToURI(url);
},
doActionOnSelectedTiles: function(aActionName) {
let tileGroup = this._set;
let selectedTiles = tileGroup.selectedItems;
switch (aActionName){
case "delete":
Array.forEach(selectedTiles, function(aNode) {
let id = aNode.getAttribute("data-itemid");
// add some class to transition element before deletion?
if (TopSites.hideSite(id)) {
// success
aNode.contextActions.delete('delete');
aNode.contextActions.add('restore');
}
// TODO: we'll use some callback/event to remove the item or re-draw the grid
});
break;
case "pin":
Array.forEach(selectedTiles, function(aNode) {
let id = aNode.getAttribute("data-itemid");
if (TopSites.pinSite(id)) {
// success
aNode.contextActions.delete('pin');
aNode.contextActions.add('unpin');
}
// TODO: we'll use some callback/event to add some class to
// indicate element is pinned?
});
break;
case "unpin":
Array.forEach(selectedTiles, function(aNode) {
let id = aNode.getAttribute("data-itemid");
if (TopSites.unpinSite(id)) {
// success
aNode.contextActions.delete('unpin');
aNode.contextActions.add('pin');
}
// TODO: we'll use some callback/event to add some class to
// indicate element is pinned (or just redraw grid)
});
break;
// default: no action
}
},
handleEvent: function(aEvent) {
switch (aEvent.type){
case "context-action":
this.doActionOnSelectedTiles(aEvent.action);
break;
}
},
populateGrid: function populateGrid() {
let query = gHistSvc.getNewQuery();
let options = gHistSvc.getNewQueryOptions();
options.excludeQueries = true;
options.queryType = options.QUERY_TYPE_HISTORY;
options.maxResults = this._topSitesMax;
options.resultType = options.RESULTS_AS_URI;
options.sortingMode = options.SORT_BY_FRECENCY_DESCENDING;
let result = gHistSvc.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let childCount = rootNode.childCount;
// use this property as the data-itemid attribute on the tiles
// which identifies the site
let identifier = 'uri';
function isPinned(aNode) {
// placeholder condition,
// FIXME: do the actual lookup/check
return (aNode.uri.indexOf('google') > -1);
}
for (let i = 0; i < childCount; i++) {
let node = rootNode.getChild(i);
let uri = node.uri;
let title = node.title || uri;
let supportedActions = ['delete'];
// placeholder condition - check field/property for this site
if (isPinned(node)) {
supportedActions.push('unpin');
} else {
supportedActions.push('pin');
}
let item = this._set.appendItem(title, uri);
item.setAttribute("iconURI", node.icon);
item.setAttribute("data-itemid", node[identifier]);
// here is where we could add verbs based on pinned etc. state
item.setAttribute("data-contextactions", supportedActions.join(','));
}
rootNode.containerOpen = false;
},
isFirstRun: function isFirstRun() {
return prefs.getBoolPref("browser.firstrun.show.localepicker");
},
destruct: function destruct() {
// remove the observers here
}
};
let TopSitesStartView = {
_view: null,
get _grid() { return document.getElementById("start-topsites-grid"); },
init: function init() {
this._view = new TopSitesView(this._grid, 9);
if (this._view.isFirstRun()) {
let topsitesVbox = document.getElementById("start-topsites");
topsitesVbox.setAttribute("hidden", "true");
}
this._view.populateGrid();
},
uninit: function uninit() {
this._view.destruct();
},
show: function show() {
this._grid.arrangeItems(3, 3);
},
};
let TopSitesSnappedView = {
get _grid() { return document.getElementById("snapped-topsite-grid"); },
show: function show() {
this._grid.arrangeItems(1, 9);
},
init: function() {
this._view = new TopSitesView(this._grid, 9);
if (this._view.isFirstRun()) {
let topsitesVbox = document.getElementById("snapped-topsites");
topsitesVbox.setAttribute("hidden", "true");
}
this._view.populateGrid();
},
uninit: function uninit() {
this._view.destruct();
},
};

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

@ -0,0 +1,412 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let Util = {
/*
* General purpose utilities
*/
getWindowUtils: function getWindowUtils(aWindow) {
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
},
// Recursively find all documents, including root document.
getAllDocuments: function getAllDocuments(doc, resultSoFar) {
resultSoFar = resultSoFar || [doc];
if (!doc.defaultView)
return resultSoFar;
let frames = doc.defaultView.frames;
if (!frames)
return resultSoFar;
let i;
let currentDoc;
for (i = 0; i < frames.length; i++) {
currentDoc = frames[i].document;
resultSoFar.push(currentDoc);
this.getAllDocuments(currentDoc, resultSoFar);
}
return resultSoFar;
},
// Put the Mozilla networking code into a state that will kick the
// auto-connection process.
forceOnline: function forceOnline() {
Services.io.offline = false;
},
/*
* Timing utilties
*/
// Executes aFunc after other events have been processed.
executeSoon: function executeSoon(aFunc) {
Services.tm.mainThread.dispatch({
run: function() {
aFunc();
}
}, Ci.nsIThread.DISPATCH_NORMAL);
},
/*
* Console printing utilities
*/
dumpf: function dumpf(str) {
let args = arguments;
let i = 1;
dump(str.replace(/%s/g, function() {
if (i >= args.length) {
throw "dumps received too many placeholders and not enough arguments";
}
return args[i++].toString();
}));
},
// Like dump, but each arg is handled and there's an automatic newline
dumpLn: function dumpLn() {
for (let i = 0; i < arguments.length; i++)
dump(arguments[i] + " ");
dump("\n");
},
dumpElement: function dumpElement(aElement) {
this.dumpLn(aElement.id);
},
dumpElementTree: function dumpElementTree(aElement) {
let node = aElement;
while (node) {
this.dumpLn("node:", node, "id:", node.id, "class:", node.classList);
node = node.parentNode;
}
},
/*
* Element utilities
*/
highlightElement: function highlightElement(aElement) {
if (aElement == null) {
this.dumpLn("aElement is null");
return;
}
aElement.style.border = "2px solid red";
},
getHrefForElement: function getHrefForElement(target) {
let link = null;
while (target) {
if (target instanceof Ci.nsIDOMHTMLAnchorElement ||
target instanceof Ci.nsIDOMHTMLAreaElement ||
target instanceof Ci.nsIDOMHTMLLinkElement) {
if (target.hasAttribute("href"))
link = target;
}
target = target.parentNode;
}
if (link && link.hasAttribute("href"))
return link.href;
else
return null;
},
/*
* Rect and nsIDOMRect utilities
*/
pointWithinRect: function pointWithinRect(aX, aY, aRect) {
return (aRect.left < aX && aRect.top < aY &&
aRect.right > aX && aRect.bottom > aY);
},
pointWithinDOMRect: function pointWithinDOMRect(aX, aY, aRect) {
if (!aRect.width || !aRect.height)
return false;
return this.pointWithinRect(aX, aY, aRect);
},
isEmptyDOMRect: function isEmptyDOMRect(aRect) {
if ((aRect.bottom - aRect.top) <= 0 &&
(aRect.right - aRect.left) <= 0)
return true;
return false;
},
// Dumps the details of a dom rect to the console
dumpDOMRect: function dumpDOMRect(aMsg, aRect) {
try {
Util.dumpLn(aMsg,
"left:" + Math.round(aRect.left) + ",",
"top:" + Math.round(aRect.top) + ",",
"right:" + Math.round(aRect.right) + ",",
"bottom:" + Math.round(aRect.bottom) + ",",
"width:" + Math.round(aRect.right - aRect.left) + ",",
"height:" + Math.round(aRect.bottom - aRect.top) );
} catch (ex) {
Util.dumpLn("dumpDOMRect:", ex.message);
}
},
/*
* URIs and schemes
*/
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
},
makeURLAbsolute: function makeURLAbsolute(base, url) {
// Note: makeURI() will throw if url is not a valid URI
return this.makeURI(url, null, this.makeURI(base)).spec;
},
isLocalScheme: function isLocalScheme(aURL) {
return ((aURL.indexOf("about:") == 0 &&
aURL != "about:blank" &&
aURL != "about:empty" &&
aURL != "about:start") ||
aURL.indexOf("chrome:") == 0);
},
isOpenableScheme: function isShareableScheme(aProtocol) {
let dontOpen = /^(mailto|javascript|news|snews)$/;
return (aProtocol && !dontOpen.test(aProtocol));
},
isShareableScheme: function isShareableScheme(aProtocol) {
let dontShare = /^(chrome|about|file|javascript|resource)$/;
return (aProtocol && !dontShare.test(aProtocol));
},
// Don't display anything in the urlbar for these special URIs.
isURLEmpty: function isURLEmpty(aURL) {
return (!aURL ||
aURL == "about:blank" ||
aURL == "about:empty" ||
aURL == "about:home" ||
aURL == "about:start");
},
// Don't remember these pages in the session store.
isURLMemorable: function isURLMemorable(aURL) {
return !(aURL == "about:blank" ||
aURL == "about:empty" ||
aURL == "about:start");
},
/*
* Math utilities
*/
clamp: function(num, min, max) {
return Math.max(min, Math.min(max, num));
},
/*
* Screen and layout utilities
*/
get displayDPI() {
delete this.displayDPI;
return this.displayDPI = this.getWindowUtils(window).displayDPI;
},
isPortrait: function isPortrait() {
return (window.innerWidth <= window.innerHeight);
},
LOCALE_DIR_RTL: -1,
LOCALE_DIR_LTR: 1,
get localeDir() {
// determine browser dir first to know which direction to snap to
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
return chromeReg.isLocaleRTL("global") ? this.LOCALE_DIR_RTL : this.LOCALE_DIR_LTR;
},
/*
* Process utilities
*/
isParentProcess: function isInParentProcess() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
},
/*
* Event utilities
*/
modifierMaskFromEvent: function modifierMaskFromEvent(aEvent) {
return (aEvent.altKey ? Ci.nsIDOMEvent.ALT_MASK : 0) |
(aEvent.ctrlKey ? Ci.nsIDOMEvent.CONTROL_MASK : 0) |
(aEvent.shiftKey ? Ci.nsIDOMEvent.SHIFT_MASK : 0) |
(aEvent.metaKey ? Ci.nsIDOMEvent.META_MASK : 0);
},
/*
* Download utilities
*/
insertDownload: function insertDownload(aSrcUri, aFile) {
let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
let db = dm.DBConnection;
let stmt = db.createStatement(
"INSERT INTO moz_downloads (name, source, target, startTime, endTime, state, referrer) " +
"VALUES (:name, :source, :target, :startTime, :endTime, :state, :referrer)"
);
stmt.params.name = aFile.leafName;
stmt.params.source = aSrcUri.spec;
stmt.params.target = aFile.path;
stmt.params.startTime = Date.now() * 1000;
stmt.params.endTime = Date.now() * 1000;
stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED;
stmt.params.referrer = aSrcUri.spec;
stmt.execute();
stmt.finalize();
let newItemId = db.lastInsertRowID;
let download = dm.getDownload(newItemId);
//dm.resumeDownload(download);
//Services.obs.notifyObservers(download, "dl-start", null);
},
/*
* Local system utilities
*/
createShortcut: function Util_createShortcut(aTitle, aURL, aIconURL, aType) {
// The background images are 72px, but Android will resize as needed.
// Bigger is better than too small.
const kIconSize = 72;
const kOverlaySize = 32;
const kOffset = 20;
// We have to fallback to something
aTitle = aTitle || aURL;
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.setAttribute("style", "display: none");
function _createShortcut() {
let icon = canvas.toDataURL("image/png", "");
canvas = null;
try {
let shell = Cc["@mozilla.org/browser/shell-service;1"].createInstance(Ci.nsIShellService);
shell.createShortcut(aTitle, aURL, icon, aType);
} catch(e) {
Cu.reportError(e);
}
}
// Load the main background image first
let image = new Image();
image.onload = function() {
canvas.width = canvas.height = kIconSize;
let ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, kIconSize, kIconSize);
// If we have a favicon, lets draw it next
if (aIconURL) {
let favicon = new Image();
favicon.onload = function() {
// Center the favicon and overlay it on the background
ctx.drawImage(favicon, kOffset, kOffset, kOverlaySize, kOverlaySize);
_createShortcut();
}
favicon.onerror = function() {
Cu.reportError("CreateShortcut: favicon image load error");
}
favicon.src = aIconURL;
} else {
_createShortcut();
}
}
image.onerror = function() {
Cu.reportError("CreateShortcut: background image load error");
}
// Pick the right background
image.src = aIconURL ? "chrome://browser/skin/images/homescreen-blank-hdpi.png"
: "chrome://browser/skin/images/homescreen-default-hdpi.png";
},
};
/*
* Timeout
*
* Helper class to nsITimer that adds a little more pizazz. Callback can be an
* object with a notify method or a function.
*/
Util.Timeout = function(aCallback) {
this._callback = aCallback;
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._type = null;
};
Util.Timeout.prototype = {
// Timer callback. Don't call this manually.
notify: function notify() {
if (this._type == this._timer.TYPE_ONE_SHOT)
this._type = null;
if (this._callback.notify)
this._callback.notify();
else
this._callback.apply(null);
},
// Helper function for once and interval.
_start: function _start(aDelay, aType, aCallback) {
if (aCallback)
this._callback = aCallback;
this.clear();
this._timer.initWithCallback(this, aDelay, aType);
this._type = aType;
return this;
},
// Do the callback once. Cancels other timeouts on this object.
once: function once(aDelay, aCallback) {
return this._start(aDelay, this._timer.TYPE_ONE_SHOT, aCallback);
},
// Do the callback every aDelay msecs. Cancels other timeouts on this object.
interval: function interval(aDelay, aCallback) {
return this._start(aDelay, this._timer.TYPE_REPEATING_SLACK, aCallback);
},
// Clear any pending timeouts.
clear: function clear() {
if (this.isPending()) {
this._timer.cancel();
this._type = null;
}
return this;
},
// If there is a pending timeout, call it and cancel the timeout.
flush: function flush() {
if (this.isPending()) {
this.notify();
this.clear();
}
return this;
},
// Return true if we are waiting for a callback.
isPending: function isPending() {
return this._type !== null;
}
};

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

@ -0,0 +1,209 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Progress heartbeat timer duration (ms)
const kHeartbeatDuration = 1000;
// Start and end progress screen css margins as percentages
const kProgressMarginStart = 30;
const kProgressMarginEnd = 70;
const WebProgress = {
_progressActive: false,
init: function init() {
messageManager.addMessageListener("Content:StateChange", this);
messageManager.addMessageListener("Content:LocationChange", this);
messageManager.addMessageListener("Content:SecurityChange", this);
Elements.progress.addEventListener("transitionend", this._progressTransEnd, true);
return this;
},
receiveMessage: function receiveMessage(aMessage) {
let json = aMessage.json;
let tab = Browser.getTabForBrowser(aMessage.target);
switch (aMessage.name) {
case "Content:StateChange": {
if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START)
this._windowStart(json, tab);
else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP)
this._windowStop(json, tab);
}
if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START)
this._networkStart(json, tab);
else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP)
this._networkStop(json, tab);
}
this._progressStep();
break;
}
case "Content:LocationChange": {
this._locationChange(json, tab);
this._progressStep();
break;
}
case "Content:SecurityChange": {
this._securityChange(json, tab);
this._progressStep();
break;
}
}
},
_securityChange: function _securityChange(aJson, aTab) {
// Don't need to do anything if the data we use to update the UI hasn't changed
if (aTab.state == aJson.state && !aTab.hostChanged)
return;
aTab.hostChanged = false;
aTab.state = aJson.state;
if (aTab == Browser.selectedTab) {
IdentityUI.checkIdentity();
}
},
_locationChange: function _locationChange(aJson, aTab) {
let spec = aJson.location;
let location = spec.split("#")[0]; // Ignore fragment identifier changes.
if (aTab == Browser.selectedTab)
BrowserUI.updateURI();
let locationHasChanged = (location != aTab.browser.lastLocation);
if (locationHasChanged) {
Browser.getNotificationBox(aTab.browser).removeTransientNotifications();
aTab.resetZoomLevel();
aTab.hostChanged = true;
aTab.browser.lastLocation = location;
aTab.browser.userTypedValue = "";
aTab.browser.appIcon = { href: null, size:-1 };
#ifdef MOZ_CRASHREPORTER
if (CrashReporter.enabled)
CrashReporter.annotateCrashReport("URL", spec);
#endif
this._waitForLoad(aTab);
}
let event = document.createEvent("UIEvents");
event.initUIEvent("URLChanged", true, false, window, locationHasChanged);
aTab.browser.dispatchEvent(event);
},
_waitForLoad: function _waitForLoad(aTab) {
let browser = aTab.browser;
aTab._firstPaint = false;
browser.messageManager.addMessageListener("Browser:FirstPaint", function firstPaintListener(aMessage) {
browser.messageManager.removeMessageListener(aMessage.name, arguments.callee);
aTab._firstPaint = true;
aTab.scrolledAreaChanged(true);
aTab.updateThumbnailSource();
});
},
_networkStart: function _networkStart(aJson, aTab) {
aTab.startLoading();
if (aTab == Browser.selectedTab) {
BrowserUI.update(TOOLBARSTATE_LOADING);
// We should at least show something in the URLBar until
// the load has progressed further along
if (aTab.browser.currentURI.spec == "about:blank")
BrowserUI.updateURI({ captionOnly: true });
}
},
_networkStop: function _networkStop(aJson, aTab) {
aTab.endLoading();
if (aTab == Browser.selectedTab) {
BrowserUI.update(TOOLBARSTATE_LOADED);
}
},
_windowStart: function _windowStart(aJson, aTab) {
this._progressStart(aJson, aTab);
},
_windowStop: function _windowStop(aJson, aTab) {
this._progressStop(aJson, aTab);
},
_progressStart: function _progressStart(aJson, aTab) {
// We will get multiple calls from _windowStart, so
// only process once.
if (this._progressActive)
return;
this._progressActive = true;
// 'Whoosh' in
this._progressCount = kProgressMarginStart;
Elements.progress.style.width = this._progressCount + "%";
Elements.progress.removeAttribute("fade");
// Create a pulse timer to keep things moving even if we don't
// collect any state changes.
setTimeout(function() {
WebProgress._progressStepTimer();
}, kHeartbeatDuration, this);
},
_stepProgressCount: function _stepProgressCount() {
// Step toward the end margin in smaller slices as we get closer
let left = kProgressMarginEnd - this._progressCount;
let step = left * .05;
this._progressCount += Math.ceil(step);
// Don't go past the 'whoosh out' margin.
if (this._progressCount > kProgressMarginEnd) {
this._progressCount = kProgressMarginEnd;
}
},
_progressStep: function _progressStep() {
if (!this._progressActive)
return;
this._stepProgressCount();
Elements.progress.style.width = this._progressCount + "%";
},
_progressStepTimer: function _progressStepTimer() {
if (!this._progressActive)
return;
this._progressStep();
setTimeout(function() {
WebProgress._progressStepTimer();
}, kHeartbeatDuration, this);
},
_progressStop: function _progressStop(aJson, aTab) {
this._progressActive = false;
// 'Whoosh out' and fade
Elements.progress.style.width = "100%";
Elements.progress.setAttribute("fade", true);
},
_progressTransEnd: function _progressTransEnd(data) {
if (!Elements.progress.hasAttribute("fade"))
return;
// Close out fade finished, reset
if (data.propertyName == "opacity") {
Elements.progress.style.width = "0px";
}
},
};

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

@ -0,0 +1,230 @@
var Appbar = {
get appbar() { return document.getElementById('appbar'); },
get consoleButton() { return document.getElementById('console-button'); },
get jsShellButton() { return document.getElementById('jsshell-button'); },
get zoomInButton() { return document.getElementById('zoomin-button'); },
get zoomOutButton() { return document.getElementById('zoomout-button'); },
get starButton() { return document.getElementById('star-button'); },
get pinButton() { return document.getElementById('pin-button'); },
get moreButton() { return document.getElementById('more-button'); },
// track selected/active richgrid/tilegroup - the context for contextual action buttons
activeTileset: null,
init: function Appbar_init() {
window.addEventListener('MozContextUIShow', this, false);
window.addEventListener('MozPrecisePointer', this, false);
window.addEventListener('MozImprecisePointer', this, false);
this._updateDebugButtons();
this._updateZoomButtons();
// tilegroup selection events for all modules get bubbled up
window.addEventListener("selectionchange", this, false);
},
handleEvent: function Appbar_handleEvent(aEvent) {
switch (aEvent.type) {
case 'MozContextUIShow':
this._updatePinButton();
this._updateStarButton();
break;
case 'MozPrecisePointer':
case 'MozImprecisePointer':
this._updateZoomButtons();
break;
case "selectionchange":
let nodeName = aEvent.target.nodeName;
if ('richgrid' === nodeName) {
this._onTileSelectionChanged(aEvent);
}
break;
}
},
onDownloadButton: function() {
PanelUI.show("downloads-container");
ContextUI.dismiss();
},
onZoomOutButton: function() {
Browser.zoom(1);
},
onZoomInButton: function() {
Browser.zoom(-1);
},
onPinButton: function() {
if (this.pinButton.checked) {
Browser.pinSite();
} else {
Browser.unpinSite();
}
},
onStarButton: function(aValue) {
if (aValue === undefined) {
aValue = this.starButton.checked;
}
if (aValue) {
Browser.starSite(function () {
Appbar._updateStarButton();
});
} else {
Browser.unstarSite(function () {
Appbar._updateStarButton();
});
}
},
onMoreButton: function(aEvent) {
var typesArray = ["find-in-page"];
try {
// If we have a valid http or https URI then show the view on desktop
// menu item.
var uri = Services.io.newURI(Browser.selectedBrowser.currentURI.spec,
null, null);
if (uri.schemeIs('http') || uri.schemeIs('https')) {
typesArray.push("view-on-desktop");
}
} catch(ex) {
}
var x = this.moreButton.getBoundingClientRect().left;
var y = this.appbar.getBoundingClientRect().top;
ContextMenuUI.showContextMenu({
json: {
types: typesArray,
string: '',
xPos: x,
yPos: y,
forcePosition: true,
leftAligned: true,
bottomAligned: true
}
});
},
onViewOnDesktop: function() {
try {
// Make sure we have a valid URI so Windows doesn't prompt
// with an unrecognized command, select default program window
var uri = Services.io.newURI(Browser.selectedBrowser.currentURI.spec,
null, null);
if (uri.schemeIs('http') || uri.schemeIs('https')) {
MetroUtils.launchInDesktop(Browser.selectedBrowser.currentURI.spec, "");
}
} catch(ex) {
}
},
onConsoleButton: function() {
PanelUI.show("console-container");
},
onJSShellButton: function() {
// XXX for debugging, this only works when running on the desktop.
if (!MetroUtils.immersive)
window.openDialog("chrome://browser/content/shell.xul", "_blank",
"all=no,scrollbars=yes,resizable=yes,dialog=no");
},
dispatchContextualAction: function(aActionName){
let activeTileset = this.activeTileset;
if (activeTileset) {
// fire event on the richgrid, others can listen
// but we keep coupling loose so grid doesn't need to know about appbar
let event = document.createEvent("Events");
event.action = aActionName;
event.initEvent("context-action", true, false);
activeTileset.dispatchEvent(event);
// done with this selection, explicitly clear it
activeTileset.clearSelection();
}
this.appbar.dismiss();
},
showContextualActions: function(aVerbs){
let doc = document;
// button element id to action verb lookup
let buttonsMap = new Map();
for (let verb of aVerbs) {
let id = verb + "-selected-button";
let buttonNode = doc.getElementById(id);
if (buttonNode) {
buttonsMap.set(id, verb);
} else {
Util.dumpLn("Appbar.showContextualActions: no button for " + verb);
}
}
// hide/show buttons as appropriate
let buttons = doc.querySelectorAll("#contextualactions-tray > toolbarbutton");
for (let btnNode of buttons) {
if (buttonsMap.has(btnNode.id)){
btnNode.hidden = false;
} else {
btnNode.hidden = true;
}
};
if (buttonsMap.size) {
// there are buttons to show
// TODO: show the contextual actions tray?
} else {
// 0 actions to show;
// TODO: hide the contextual actions tray entirely?
}
},
_onTileSelectionChanged: function _onTileSelectionChanged(aEvent){
let activeTileset = aEvent.target;
// deselect tiles in other tile groups
if (this.activeTileset && this.activeTileset !== activeTileset) {
this.activeTileset.clearSelection();
}
// keep track of which view is the target/scope for the contextual actions
this.activeTileset = activeTileset;
// ask the view for the list verbs/action-names it thinks are
// appropriate for the tiles selected
let contextActions = activeTileset.contextActions;
let verbs = [v for (v of contextActions)];
// could transition in old, new buttons?
this.showContextualActions(verbs);
if (verbs.length) {
this.appbar.show();
} else {
this.appbar.dismiss();
}
},
_updatePinButton: function() {
this.pinButton.checked = Browser.isSitePinned();
},
_updateStarButton: function() {
Browser.isSiteStarredAsync(function (isStarred) {
this.starButton.checked = isStarred;
}.bind(this));
},
_updateDebugButtons: function() {
this.consoleButton.disabled = !ConsolePanelView.enabled;
this.jsShellButton.disabled = MetroUtils.immersive;
},
_updateZoomButtons: function() {
let zoomDisabled = !InputSourceHelper.isPrecise;
this.zoomOutButton.disabled = this.zoomInButton.disabled = zoomDisabled;
}
};

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

@ -0,0 +1,89 @@
<?xml version="1.0"?>
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="appbarBinding">
<content>
<xul:toolbar anonid="toolbar"><children/></xul:toolbar>
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
window.addEventListener('MozContextUIShow', this);
window.addEventListener('MozContextUIDismiss', this);
window.addEventListener('MozAppbarDismiss', this);
]]>
</constructor>
<destructor>
<![CDATA[
window.removeEventListener('MozContextUIShow', this);
window.removeEventListener('MozContextUIDismiss', this);
window.removeEventListener('MozAppbarDismiss', this);
]]>
</destructor>
<field name="sticky">false</field>
<field name="_toolbar" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "toolbar");</field>
<property name="isShowing" readonly="true">
<getter>
<![CDATA[
return this.getAttribute("visible") == "true";
]]>
</getter>
</property>
<method name="dismiss">
<body>
<![CDATA[
this.removeAttribute("visible");
]]>
</body>
</method>
<method name="show">
<body>
<![CDATA[
this.setAttribute("visible", "true");
]]>
</body>
</method>
<method name="toggle">
<body>
<![CDATA[
if (this.getAttribute("visible") === "true") {
this.dismiss();
} else {
this.show();
}
]]>
</body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body>
<![CDATA[
switch (aEvent.type) {
case 'MozContextUIShow':
this.show();
break;
case 'MozAppbarDismiss':
case 'MozContextUIDismiss':
this.dismiss();
break;
}
]]>
</body>
</method>
</implementation>
<handlers>
<!-- Work around for bug 835175 -->
<handler event="click">false;</handler>
</handlers>
</binding>
</bindings>

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

@ -0,0 +1,274 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="arrowbox" extends="xul:box">
<content orient="vertical">
<xul:box anonid="container" class="panel-arrowcontainer" flex="1">
<xul:box anonid="arrowbox" class="panel-arrowbox" dir="ltr">
<xul:image anonid="arrow" class="panel-arrow" xbl:inherits="type"/>
</xul:box>
<xul:scrollbox anonid="arrowcontent" class="panel-arrowcontent" flex="1">
<xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack,flex">
<children/>
</xul:box>
</xul:scrollbox>
</xul:box>
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
window.addEventListener("resize", this._eventHandler, false);
]]>
</constructor>
<destructor>
<![CDATA[
window.removeEventListener("resize", this._eventHandler, false);
]]>
</destructor>
<field name="anonScrollBox" readonly="true"><![CDATA[
// Expose the anyonymous scrollbox so ScrollUtils.getScrollboxFromElement can find it.
document.getAnonymousElementByAttribute(this, "anonid", "arrowcontent");
]]></field>
<property name="offset" onget="return parseInt(this.getAttribute('offset')) || 0;"
onset="this.setAttribute('offset', val); return val;"/>
<method name="_updateArrow">
<parameter name="popupRect"/>
<parameter name="targetRect"/>
<parameter name="horizPos"/>
<parameter name="vertPos"/>
<body>
<![CDATA[
let arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
if (!popupRect || !targetRect) {
arrow.hidden = true;
return;
}
let container = document.getAnonymousElementByAttribute(this, "anonid", "container");
let content = document.getAnonymousElementByAttribute(this, "anonid", "arrowcontent");
let arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
// If the content of the arrowbox if taller than the available
// screen space, force a maximum height
this.style.minHeight = "";
content.style.overflow = "visible";
const kBottomMargin = 64;
let contentRect = content.firstChild.getBoundingClientRect();
if ((contentRect.height + contentRect.top + kBottomMargin) > window.innerHeight) {
content.style.overflow = "hidden";
this.style.minHeight = (window.innerHeight - parseInt(this.top) - kBottomMargin) + "px";
}
let HALF_ARROW_WIDTH = 16;
let anchorClass = "";
let hideArrow = false;
if (horizPos == 0) {
container.orient = "vertical";
arrowbox.orient = "";
if (vertPos == 0) {
hideArrow = true;
} else {
let anchorPosX = 0.5;
// check for hasAttribute because, in some cases, anchorNode is actually a rect
if (this.anchorNode && this.anchorNode.hasAttribute && this.anchorNode.hasAttribute("anchorPosX"))
anchorPosX = parseFloat(this.anchorNode.getAttribute("anchorPosX")) || 0.5;
arrowbox.style.marginLeft = ((targetRect.left - popupRect.left) + (targetRect.width * anchorPosX) - HALF_ARROW_WIDTH) + "px";
if (vertPos == 1) {
container.dir = "normal";
anchorClass = "top";
} else if (vertPos == -1) {
container.dir = "reverse";
anchorClass = "bottom";
}
}
} else if (vertPos == 0) {
container.orient = "";
arrowbox.orient = "vertical";
let anchorPosY = 0.5;
// check for hasAttribute because, in some cases, anchorNode is actually a rect
if (this.anchorNode && this.anchorNode.hasAttribute && this.anchorNode.hasAttribute("anchorPosY"))
anchorPosY = parseFloat(this.anchorNode.getAttribute("anchorPosY")) || 0.5;
arrowbox.style.marginTop = ((targetRect.top - popupRect.top) + (targetRect.height * anchorPosY) - HALF_ARROW_WIDTH) + "px";
if (horizPos == 1) {
container.dir = "ltr";
anchorClass = "left";
} else if (horizPos == -1) {
container.dir = "rtl";
anchorClass = "right";
}
} else {
hideArrow = true;
}
arrow.hidden = hideArrow;
arrow.setAttribute("side", anchorClass);
]]>
</body>
</method>
<field name="anchorNode">null</field>
<method name="anchorTo">
<parameter name="aAnchorNode"/>
<parameter name="aPosition"/>
<body>
<![CDATA[
if (!aAnchorNode) {
this._updateArrow(null, null, 0, 0);
return;
}
this.anchorNode = aAnchorNode;
this.position = aPosition;
let anchorRect = aAnchorNode.getBoundingClientRect();
let popupRect = new Rect(0,0,0,0);
for (let i = 0; i < this.childNodes.length; i++) {
popupRect.expandToContain(Rect.fromRect(this.childNodes[i].getBoundingClientRect()));
}
let offset = this.offset;
let horizPos = 0;
let vertPos = 0;
if (aPosition) {
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
let isRtl = chromeReg.isLocaleRTL("global");
let left = 0;
let top = 0;
switch (aPosition) {
case "before_start":
left = isRtl ? anchorRect.right - popupRect.width : anchorRect.left;
top = anchorRect.top + offset - popupRect.height;
vertPos = -1;
break;
case "before_end":
left = isRtl ? anchorRect.left : anchorRect.right - popupRect.width;
top = anchorRect.top + offset - popupRect.height;
vertPos = -1;
break;
case "after_start":
left = isRtl ? anchorRect.right - popupRect.width : anchorRect.left;
top = anchorRect.bottom - offset;
vertPos = 1;
break;
case "after_end":
left = isRtl ? anchorRect.left : anchorRect.right - popupRect.width;
top = anchorRect.bottom - offset;
vertPos = 1;
break;
case "start_before":
left = isRtl ? anchorRect.right : anchorRect.left - popupRect.width - offset;
top = anchorRect.top;
horizPos = -1;
break;
case "start_after":
left = isRtl ? anchorRect.right : anchorRect.left - popupRect.width - offset;
top = anchorRect.bottom - popupRect.height;
horizPos = -1;
break;
case "end_before":
left = isRtl ? anchorRect.left - popupRect.width - offset : anchorRect.right;
top = anchorRect.top;
horizPos = 1;
break;
case "end_after":
left = isRtl ? anchorRect.left - popupRect.width - offset : anchorRect.right;
top = anchorRect.bottom - popupRect.height;
horizPos = 1;
break;
case "overlap":
left = isRtl ? anchorRect.right - popupRect.width + offset : anchorRect.left + offset ;
top = anchorRect.top + offset ;
break;
}
if (top == 0) top = 1;
if (left == 0) left = 1;
if (left + popupRect.width > window.innerWidth)
left = window.innerWidth - popupRect.width;
else if (left < 0)
left = 1;
popupRect.left = left;
this.setAttribute("left", left);
popupRect.top = top;
this.setAttribute("top", top);
} else {
horizPos = (Math.round(popupRect.right) <= Math.round(anchorRect.left + offset)) ? -1 :
(Math.round(popupRect.left) >= Math.round(anchorRect.right - offset)) ? 1 : 0;
vertPos = (Math.round(popupRect.bottom) <= Math.round(anchorRect.top + offset)) ? -1 :
(Math.round(popupRect.top) >= Math.round(anchorRect.bottom - offset)) ? 1 : 0;
}
this._updateArrow(popupRect, anchorRect, horizPos, vertPos);
]]>
</body>
</method>
<method name="pointLeftAt">
<parameter name="targetNode"/>
<body>
<![CDATA[
if (!targetNode) {
this._updateArrow(null, null, 0, 0);
return;
}
let popupRect = this.getBoundingClientRect();
let targetRect = targetNode.getBoundingClientRect();
this._updateArrow(popupRect, targetRect, 1, 0);
]]>
</body>
</method>
<method name="pointRightAt">
<parameter name="targetNode"/>
<body>
<![CDATA[
if (!targetNode) {
this._updateArrow(null, null, 0, 0);
return;
}
let popupRect = this.getBoundingClientRect();
let targetRect = targetNode.getBoundingClientRect();
this._updateArrow(popupRect, targetRect, -1, 0);
]]>
</body>
</method>
<field name="_eventHandler"><![CDATA[
({
self: this,
handleEvent: function handleEvent(aEvent) {
// We need to reset the margins because the previous values could
// cause the arrowbox to size incorrectly.
let self = this.self;
switch (aEvent.type) {
case "resize":
// Do nothing if there's no anchorNode
if (!self.anchorNode)
break;
let arrowbox = document.getAnonymousElementByAttribute(self, "anonid", "arrowbox");
arrowbox.style.marginLeft = "0px";
arrowbox.style.marginTop = "0px";
self.anchorTo(self.anchorNode, self.position);
break;
}
}
})
]]></field>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,445 @@
<?xml version="1.0" encoding="Windows-1252" ?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="autocomplete" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation>
<constructor>
<![CDATA[
this.minResultsForPopup = 0;
this.popup._input = this;
]]>
</constructor>
<method name="openPopup">
<body>
<![CDATA[
this.popup.openAutocompletePopup(this, null);
]]>
</body>
</method>
<method name="closePopup">
<body>
<![CDATA[
this.popup.closePopup(this, null);
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="dblclick" phase="capturing">
<![CDATA[
let selectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
if (selectAll)
this.select();
]]>
</handler>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, true);
]]>
</handler>
<handler event="keypress" phase="capturing" keycode="VK_RETURN">
<![CDATA[
event.preventDefault();
event.stopPropagation();
this.popup.handleCompletion();
]]>
</handler>
</handlers>
</binding>
<binding id="autocomplete-popup">
<content orient="horizontal">
<xul:vbox id="results-vbox" class="meta-section viewable-height">
<xul:label class="meta-section-title" value="&autocompleteResultsHeader.label;"/>
<richgrid id="results-richgrid" anonid="results" seltype="single" flex="1"/>
</xul:vbox>
<xul:vbox id="searches-vbox" class="meta-section viewable-height">
<xul:label class="meta-section-title" value="&autocompleteSearchesHeader.label;"/>
<richgrid id="searches-richgrid" anonid="searches" seltype="single" flex="1"/>
</xul:vbox>
</content>
<implementation implements="nsIAutoCompletePopup, nsIObserver">
<constructor>
<![CDATA[
Services.obs.addObserver(this, "browser-search-engine-modified", false);
this.updateSearchEngines();
this._results.controller = this;
this._searches.controller = this;
]]>
</constructor>
<destructor>
<![CDATA[
Services.obs.removeObserver(this, "browser-search-engine-modified");
]]>
</destructor>
<method name="handleItemClick">
<parameter name="aItem"/>
<body>
<![CDATA[
if (aItem.control == this._searches) {
let engineName = aItem.getAttribute("value");
BrowserUI.doOpenSearch(engineName);
} else {
let url = aItem.getAttribute("value");
Browser.loadURI(url);
}
]]>
</body>
</method>
<!-- nsIAutocompleteInput -->
<field name="_input">null</field>
<field name="_popupOpen">false</field>
<property name="overrideValue" readonly="true" onget="return null;"/>
<property name="selectedItem">
<getter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedItem : null;
]]>
</getter>
<setter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedItem : null;
]]>
</setter>
</property>
<property name="selectedIndex">
<getter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
]]>
</getter>
<setter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
]]>
</setter>
</property>
<property name="input" readonly="true" onget="return this._input;"/>
<property name="popupOpen" readonly="true" onget="return this._popupOpen;"/>
<property name="_matchCount" readonly="true" onget="return this.input.controller.matchCount;"/>
<method name="openAutocompletePopup">
<parameter name="aInput"/>
<parameter name="aElement"/>
<body>
<![CDATA[
if (this._popupOpen)
return;
ContextUI.dismissAppbar();
this._input = aInput;
this._popupOpen = true;
this._grid = this._results;
this.clearSelection();
this.invalidate();
this._fire("autocompletestart");
]]>
</body>
</method>
<method name="gridBoundCallback">
<body>
<![CDATA[
this.updateResults();
]]>
</body>
</method>
<method name="closePopup">
<body>
<![CDATA[
if (!this._popupOpen)
return;
this.input.controller.stopSearch();
this._popupOpen = false;
this._fire("autocompleteend");
]]>
</body>
</method>
<method name="invalidate">
<body>
<![CDATA[
if (!this._popupOpen)
return;
this.updateResults();
this.updateSearchEngineSubtitles();
]]>
</body>
</method>
<method name="selectBy">
<parameter name="aReverse"/>
<parameter name="aPage"/>
<body>
<![CDATA[
let handleOnSelect = this._handleOnSelect;
this._handleOnSelect = false;
// TODO <jwilde>: Pressing page up/down should jump between rows,
// not just items in the grid
// Move between grids if we're at the edge of one
if ((this._grid.isSelectionAtEnd && !aReverse) ||
(this._grid.isSelectionAtStart && aReverse)) {
let index = aReverse ? this._otherGrid.itemCount - 1 : 0;
this._otherGrid.selectedIndex = index;
} else {
this._grid.offsetSelection(aReverse ? -1 : 1);
}
this._handleOnSelect = handleOnSelect;
]]>
</body>
</method>
<!-- nsIObserver -->
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
if (aTopic != "browser-search-engine-modified")
return;
switch (aData) {
case "engine-added":
case "engine-removed":
case "engine-changed":
this.updateSearchEngines();
break;
case "engine-current":
// Not relevant
break;
}
]]>
</body>
</method>
<!-- Interface for updating various components of the popup. -->
<method name="updateResults">
<body>
<![CDATA[
if (!this._isGridBound(this._results))
return;
if (!this.input)
return;
let controller = this.input.controller;
let lastMatch = this._matchCount - 1;
let iterCount = Math.max(this._results.itemCount, this._matchCount);
// Swap out existing items for new search hit results
for (let i = 0; i < iterCount; i++) {
if (i > lastMatch) {
let lastItem = this._results.itemCount - 1;
this._results.removeItemAt(lastItem);
continue;
}
let value = controller.getValueAt(i);
let label = controller.getCommentAt(i) || value;
let iconURI = controller.getImageAt(i);
let item = this._results.getItemAtIndex(i);
if (item == null) {
item = this._results.appendItem(label, value);
item.setAttribute("autocomplete", "true");
} else {
item.setAttribute("label", label);
item.setAttribute("value", value);
}
item.setAttribute("iconURI", iconURI);
}
this._results.arrangeItems();
]]>
</body>
</method>
<method name="updateSearchEngines">
<body><![CDATA[
Services.search.init(this._onSearchServiceInit.bind(this));
]]></body>
</method>
<method name="_onSearchServiceInit">
<body>
<![CDATA[
if (!this._isGridBound(this._searches))
return;
this._engines = Services.search.getVisibleEngines();
while (this._searches.itemCount > 0)
this._searches.removeItemAt(0);
this._engines.forEach(function (anEngine) {
let item = this._searches.appendItem(anEngine.name, anEngine.name);
item.setAttribute("autocomplete", "true");
item.setAttribute("search", "true");
let iconURI = anEngine.iconURI ? anEngine.iconURI.spec : "";
item.setAttribute("iconURI", iconURI);
}.bind(this));
this._searches.arrangeItems();
]]>
</body>
</method>
<method name="updateSearchEngineSubtitles">
<body>
<![CDATA[
if (!this._isGridBound(this._searches))
return;
let searchString = this.input.controller.searchString;
let label = Strings.browser.formatStringFromName("opensearch.search", [searchString], 1);
for (let i = 0, len = this._searches.itemCount; i < len; i++) {
let item = this._searches.getItemAtIndex(i);
item.setAttribute("label", label);
}
this._searches.arrangeItems();
]]>
</body>
</method>
<!-- Interface for handling actions across grids -->
<method name="handleCompletion">
<body>
<![CDATA[
if (this._grid == this._results) {
this.input.controller.handleEnter(false);
return;
}
if (this._grid == this._searches) {
let engine = this._engines[this._grid.selectedIndex];
BrowserUI.doOpenSearch(engine.name);
this.closePopup();
return;
}
]]>
</body>
</method>
<method name="clearSelection">
<body>
<![CDATA[
if (this._isGridBound(this._results))
this._results.clearSelection();
if (this._isGridBound(this._searches))
this._searches.clearSelection();
]]>
</body>
</method>
<!-- Helpers -->
<field name="_engines">[]</field>
<field name="_handleOnSelect">true</field>
<field name="_grid">null</field>
<property name="_results" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'results');"/>
<property name="_searches" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'searches');"/>
<property name="_otherGrid" readonly="true">
<getter>
<![CDATA[
return (this._grid == this._results) ? this._searches : this._results;
]]>
</getter>
</property>
<method name="_isGridBound">
<parameter name="aGrid"/>
<body>
<![CDATA[
return aGrid.itemCount != undefined;
]]>
</body>
</method>
<method name="_fire">
<parameter name="aName"/>
<body>
<![CDATA[
let event = document.createEvent("Events");
event.initEvent(aName, true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="select">
<![CDATA[
let grid = event.originalTarget;
if (grid != this._grid) {
if (this._grid.clearSelection)
this._grid.clearSelection();
this._grid = grid;
}
if (this._handleOnSelect && this._results.selectedItem) {
BrowserUI.goToURI(
this._results.selectedItem.getAttribute("value"));
this.closePopup();
} else if (this._handleOnSelect) {
this.handleCompletion();
}
]]>
</handler>
<handler event="contentgenerated">
<![CDATA[
let grid = event.originalTarget;
if (grid == this._searches)
this.updateSearchEngines();
]]>
</handler>
</handlers>
</binding>
</bindings>

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

@ -0,0 +1,407 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<!-- ==================================================================
Events: When these elements are in chrome, they are not obscured
by the touch input overlay. When they are in content they may be
obscured depending on the type of input we receive.
chrome: regular mouse events, moz gesture events, tap events.
content: touch input - tap events and synthetic mouse events
generated by content code. mouse input - regular mouse events,
moz gesture events, tap events.
================================================================== -->
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="richlistbox-batch" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
<handlers>
<handler event="scroll">
<![CDATA[
// if there no more items to insert, just return early
if (this._items.length == 0)
return;
if (this._contentScrollHeight == -1) {
let scrollheight = {};
this.scrollBoxObject.getScrolledSize({}, scrollheight);
this._contentScrollHeight = scrollheight.value;
}
let y = {};
this.scrollBoxObject.getPosition({}, y);
let scrollRatio = (y.value + this._childrenHeight) / this._contentScrollHeight;
// If we're scrolled 80% to the bottom of the list, append the next
// set of items
if (scrollRatio > 0.8)
this._insertItems();
]]>
</handler>
</handlers>
<implementation>
<!-- Number of elements to add to the list initially. If there are more
than this many elements to display, only add them to the list once
the user has scrolled towards them. This is a performance
optimization to avoid locking up while attempting to append hundreds
of nodes to our richlistbox.
-->
<property name="batchSize" readonly="true" onget="return this.getAttribute('batch')"/>
<field name="_childrenHeight">this.scrollBoxObject.height;</field>
<field name="_items">[]</field>
<method name="setItems">
<parameter name="aItems"/>
<body><![CDATA[
this._items = aItems;
this._insertItems();
]]></body>
</method>
<method name="_insertItems">
<body><![CDATA[
let items = this._items.splice(0, this.batchSize);
if (!items.length)
return; // no items to insert
let count = items.length;
for (let i = 0; i<count; i++)
this.appendChild(items[i]);
// make sure we recalculate the scrollHeight of the content
this._contentScrollHeight = -1;
]]></body>
</method>
</implementation>
</binding>
<binding id="richlistbox-contextmenu" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
<handlers>
<handler event="click" phase="bubble">
<![CDATA[
ContextMenuUI.hide();
]]>
</handler>
</handlers>
</binding>
<binding id="richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<handlers>
<handler event="mousedown" phase="capturing">
<![CDATA[
// We'll get this if the user is interacting via the mouse
let domUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"].
getService(Ci.inIDOMUtils);
domUtils.setContentState(this, 0x00000001);
]]>
</handler>
<handler event="click" phase="capturing">
<![CDATA[
// allow normal mouse event processing
if (!InputSourceHelper || InputSourceHelper.isPrecise)
return;
// trap this here, we'll rely on tap events
// event.stopPropagation();
]]>
</handler>
<handler event="touchstart" phase="capturing">
<![CDATA[
// touch input event
let domUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"].
getService(Ci.inIDOMUtils);
domUtils.setContentState(this, 0x00000001);
]]>
</handler>
</handlers>
</binding>
<binding id="content-navigator">
<content class="window-width content-navigator-box" orient="horizontal" pack="start">
<children includes="textbox"/>
<xul:button anonid="previous-button" class="content-navigator-item previous-button" xbl:inherits="command=previous"/>
<xul:button anonid="next-button" class="content-navigator-item next-button" xbl:inherits="command=next"/>
<xul:spacer flex="1"/>
<xul:button anonid="close-button" class="content-navigator-item close-button" xbl:inherits="command=close"/>
</content>
<implementation>
<field name="_previousButton">
document.getAnonymousElementByAttribute(this, "anonid", "previous-button");
</field>
<field name="_nextButton">
document.getAnonymousElementByAttribute(this, "anonid", "next-button");
</field>
<field name="_closeButton">
document.getAnonymousElementByAttribute(this, "anonid", "close-button");
</field>
<method name="contentHasChanged">
<body><![CDATA[
if (!this.isActive)
return;
// There is a race condition with getBoundingClientRect and when the
// box is displayed, the padding is ignored in the size calculation.
// A nested timeouts below are used to workaround this problem.
this.getBoundingClientRect();
setTimeout(function(self) {
setTimeout(function(self) {
let height = Math.floor(self.getBoundingClientRect().height);
self.top = window.innerHeight - height;
}, 0, self);
}, 0, this);
]]></body>
</method>
<property name="isActive" onget="return !!this.model;"/>
<field name="model">null</field>
<method name="show">
<parameter name="aModel" />
<body><![CDATA[
// call the hide callback of the current object if any
if (this.model && this.model.type != aModel.type)
this.model.hide();
this.setAttribute("type", aModel.type);
this.setAttribute("next", aModel.commands.next);
this.setAttribute("previous", aModel.commands.previous);
this.setAttribute("close", aModel.commands.close);
// buttons attributes sync with commands doesn not look updated when
// we dynamically switch the "command" attribute so we need to ensure
// the disabled state of the buttons is right when switching commands
this._previousButton.disabled = document.getElementById(aModel.commands.previous).getAttribute("disabled") == "true";
this._nextButton.disabled = document.getElementById(aModel.commands.next).getAttribute("disabled") == "true";
this.model = aModel;
this.contentHasChanged();
]]></body>
</method>
<method name="hide">
<parameter name="aModel" />
<body><![CDATA[
this.removeAttribute("next");
this.removeAttribute("previous");
this.removeAttribute("close");
this.removeAttribute("type");
this.contentHasChanged();
this.model = null;
]]></body>
</method>
</implementation>
</binding>
<binding id="menulist" display="xul:box" extends="chrome://global/content/bindings/menulist.xml#menulist">
<handlers>
<handler event="mousedown" phase="capturing">
<![CDATA[
// Stop the normal menupopup from appearing
event.stopPropagation();
]]>
</handler>
<handler event="click" button="0">
<![CDATA[
if (this.disabled || this.itemCount == 0)
return;
this.focus();
MenuControlUI.show(this);
]]>
</handler>
<handler event="command" phase="capturing">
<![CDATA[
// The dropmark (button) fires a command event too. Don't forward that.
// Just forward the menuitem command events, which the toolkit version does.
if (event.target.parentNode.parentNode != this)
event.stopPropagation();
]]>
</handler>
</handlers>
</binding>
<binding id="chrome-select-option">
<content orient="horizontal" flex="1">
<xul:image class="chrome-select-option-image" anonid="check"/>
<xul:label anonid="label" xbl:inherits="value=label"/>
</content>
<implementation>
<property name="selected">
<getter>
<![CDATA[
return this.hasAttribute("selected");
]]>
</getter>
<setter>
<![CDATA[
if (val)
this.setAttribute("selected", "true");
else
this.removeAttribute("selected");
return val;
]]>
</setter>
</property>
</implementation>
</binding>
<binding id="select-button" extends="xul:box">
<content>
<svg:svg width="11px" version="1.1" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -moz-calc(50% - 2px); left: 4px;">
<svg:polyline points="1 1 5 6 9 1" stroke="#414141" stroke-width="2" stroke-linecap="round" fill="transparent" stroke-linejoin="round"/>
</svg:svg>
</content>
</binding>
<binding id="textbox" extends="chrome://global/content/bindings/textbox.xml#textbox">
<handlers>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, false);
]]>
</handler>
</handlers>
</binding>
<binding id="textarea" extends="chrome://global/content/bindings/textbox.xml#textarea">
<handlers>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, false);
]]>
</handler>
</handlers>
</binding>
<binding id="timed-textbox" extends="chrome://global/content/bindings/textbox.xml#timed-textbox">
<handlers>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, false);
]]>
</handler>
</handlers>
</binding>
<binding id="search-textbox" extends="chrome://global/content/bindings/textbox.xml#search-textbox">
<implementation>
<field name="_searchClear">
<![CDATA[
document.getAnonymousElementByAttribute(this, "class", "textbox-search-clear");
]]>
</field>
</implementation>
<handlers>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, false);
]]>
</handler>
<handler event="text" phase="bubbling"><![CDATA[
// Listen for composition update, some VKB that does suggestions does not
// update directly the content of the field but in this case we want to
// search as soon as something is entered in the field
let evt = document.createEvent("Event");
evt.initEvent("input", true, false);
this.dispatchEvent(evt);
]]></handler>
<handler event="click" phase="bubbling"><![CDATA[
// bug 629661. To reset the autosuggestions mechanism of Android, the
// textfield need to reset the IME state
if (event.originalTarget == this._searchClear) {
setTimeout(function(self) {
try {
let imeEditor = self.inputField.QueryInterface(Ci.nsIDOMNSEditableElement)
.editor
.QueryInterface(Ci.nsIEditorIMESupport);
if (imeEditor.composing)
imeEditor.forceCompositionEnd();
} catch(e) {}
}, 0, this);
}
]]></handler>
</handlers>
</binding>
<binding id="numberbox" extends="chrome://global/content/bindings/numberbox.xml#numberbox">
<handlers>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, false);
]]>
</handler>
</handlers>
</binding>
<binding id="input-box" extends="xul:box">
<implementation>
<method name="showContextMenu">
<parameter name="aTextbox"/>
<parameter name="aEvent"/>
<parameter name="aIgnoreReadOnly"/>
<body><![CDATA[
let selectionStart = aTextbox.selectionStart;
let selectionEnd = aTextbox.selectionEnd;
let json = { types: ["input-text"], string: "" };
if (selectionStart != selectionEnd) {
json.types.push("copy");
json.string = aTextbox.value.slice(selectionStart, selectionEnd);
} else if (aTextbox.value) {
json.types.push("copy-all");
json.string = aTextbox.value;
}
if (selectionStart > 0 || selectionEnd < aTextbox.textLength)
json.types.push("select-all");
let clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].
getService(Ci.nsIClipboard);
let flavors = ["text/unicode"];
let hasData = clipboard.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard);
if (hasData && (!aTextbox.readOnly || aIgnoreReadOnly)) {
json.types.push("paste");
if (aTextbox.type == "url") {
json.types.push("paste-url");
}
}
json.xPos = aEvent.clientX;
json.yPos = aEvent.clientY;
json.source = aEvent.mozInputSource;
ContextMenuUI.showContextMenu({ target: aTextbox, json: json });
]]></body>
</method>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,796 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
let WebProgressListener = {
_lastLocation: null,
_firstPaint: false,
init: function() {
let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
Ci.nsIWebProgress.NOTIFY_SECURITY |
Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, flags);
},
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
if (content != aWebProgress.DOMWindow)
return;
sendAsyncMessage("Content:StateChange", {
contentWindowId: this.contentWindowId,
stateFlags: aStateFlags,
status: aStatus
});
},
onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
if (content != aWebProgress.DOMWindow)
return;
let spec = aLocationURI ? aLocationURI.spec : "";
let location = spec.split("#")[0];
let charset = content.document.characterSet;
sendAsyncMessage("Content:LocationChange", {
contentWindowId: this.contentWindowId,
documentURI: aWebProgress.DOMWindow.document.documentURIObject.spec,
location: spec,
canGoBack: docShell.canGoBack,
canGoForward: docShell.canGoForward,
charset: charset.toString()
});
this._firstPaint = false;
let self = this;
// Keep track of hash changes
this.hashChanged = (location == this._lastLocation);
this._lastLocation = location;
// When a new page is loaded fire a message for the first paint
addEventListener("MozAfterPaint", function(aEvent) {
removeEventListener("MozAfterPaint", arguments.callee, true);
self._firstPaint = true;
let scrollOffset = ContentScroll.getScrollOffset(content);
sendAsyncMessage("Browser:FirstPaint", scrollOffset);
}, true);
},
onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
},
onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
if (content != aWebProgress.DOMWindow)
return;
let serialization = SecurityUI.getSSLStatusAsString();
sendAsyncMessage("Content:SecurityChange", {
contentWindowId: this.contentWindowId,
SSLStatusAsString: serialization,
state: aState
});
},
get contentWindowId() {
return content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
},
QueryInterface: function QueryInterface(aIID) {
if (aIID.equals(Ci.nsIWebProgressListener) ||
aIID.equals(Ci.nsISupportsWeakReference) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
};
WebProgressListener.init();
let SecurityUI = {
getSSLStatusAsString: function() {
let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
if (status) {
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
status.QueryInterface(Ci.nsISerializable);
return serhelper.serializeToString(status);
}
return null;
}
};
let WebNavigation = {
_webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation),
_timer: null,
init: function() {
addMessageListener("WebNavigation:GoBack", this);
addMessageListener("WebNavigation:GoForward", this);
addMessageListener("WebNavigation:GotoIndex", this);
addMessageListener("WebNavigation:LoadURI", this);
addMessageListener("WebNavigation:Reload", this);
addMessageListener("WebNavigation:Stop", this);
},
receiveMessage: function(message) {
switch (message.name) {
case "WebNavigation:GoBack":
this.goBack();
break;
case "WebNavigation:GoForward":
this.goForward();
break;
case "WebNavigation:GotoIndex":
this.gotoIndex(message);
break;
case "WebNavigation:LoadURI":
this.loadURI(message);
break;
case "WebNavigation:Reload":
this.reload(message);
break;
case "WebNavigation:Stop":
this.stop(message);
break;
}
},
goBack: function() {
if (this._webNavigation.canGoBack)
this._webNavigation.goBack();
},
goForward: function() {
if (this._webNavigation.canGoForward)
this._webNavigation.goForward();
},
gotoIndex: function(message) {
this._webNavigation.gotoIndex(message.index);
},
loadURI: function(message) {
let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
this._webNavigation.loadURI(message.json.uri, flags, null, null, null);
let tabData = message.json;
if (tabData.entries) {
// We are going to load from history so kill the current load. We do not
// want the load added to the history anyway. We reload after resetting history
this._webNavigation.stop(this._webNavigation.STOP_ALL);
this._restoreHistory(tabData, 0);
}
},
reload: function(message) {
let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
this._webNavigation.reload(flags);
},
stop: function(message) {
let flags = message.json.flags || this._webNavigation.STOP_ALL;
this._webNavigation.stop(flags);
},
_restoreHistory: function _restoreHistory(aTabData, aCount) {
// We need to wait for the sessionHistory to be initialized and there
// is no good way to do this. We'll try a wait loop like desktop
try {
if (!this._webNavigation.sessionHistory)
throw new Error();
} catch (ex) {
if (aCount < 10) {
let self = this;
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._timer.initWithCallback(function(aTimer) {
self._timer = null;
self._restoreHistory(aTabData, aCount + 1);
}, 100, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
}
let history = this._webNavigation.sessionHistory;
if (history.count > 0)
history.PurgeHistory(history.count);
history.QueryInterface(Ci.nsISHistoryInternal);
// helper hashes for ensuring unique frame IDs and unique document
// identifiers.
let idMap = { used: {} };
let docIdentMap = {};
for (let i = 0; i < aTabData.entries.length; i++) {
if (!aTabData.entries[i].url)
continue;
history.addEntry(this._deserializeHistoryEntry(aTabData.entries[i], idMap, docIdentMap), true);
}
// We need to force set the active history item and cause it to reload since
// we stop the load above
let activeIndex = (aTabData.index || aTabData.entries.length) - 1;
history.getEntryAtIndex(activeIndex, true);
history.QueryInterface(Ci.nsISHistory).reloadCurrentEntry();
},
_deserializeHistoryEntry: function _deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) {
let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry);
shEntry.setURI(Services.io.newURI(aEntry.url, null, null));
shEntry.setTitle(aEntry.title || aEntry.url);
if (aEntry.subframe)
shEntry.setIsSubFrame(aEntry.subframe || false);
shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
if (aEntry.contentType)
shEntry.contentType = aEntry.contentType;
if (aEntry.referrer)
shEntry.referrerURI = Services.io.newURI(aEntry.referrer, null, null);
if (aEntry.cacheKey) {
let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32);
cacheKey.data = aEntry.cacheKey;
shEntry.cacheKey = cacheKey;
}
if (aEntry.ID) {
// get a new unique ID for this frame (since the one from the last
// start might already be in use)
let id = aIdMap[aEntry.ID] || 0;
if (!id) {
for (id = Date.now(); id in aIdMap.used; id++);
aIdMap[aEntry.ID] = id;
aIdMap.used[id] = true;
}
shEntry.ID = id;
}
if (aEntry.docshellID)
shEntry.docshellID = aEntry.docshellID;
if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
shEntry.stateData =
Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion);
}
if (aEntry.scroll) {
let scrollPos = aEntry.scroll.split(",");
scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
}
let childDocIdents = {};
if (aEntry.docIdentifier) {
// If we have a serialized document identifier, try to find an SHEntry
// which matches that doc identifier and adopt that SHEntry's
// BFCacheEntry. If we don't find a match, insert shEntry as the match
// for the document identifier.
let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
if (!matchingEntry) {
matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
aDocIdentMap[aEntry.docIdentifier] = matchingEntry;
} else {
shEntry.adoptBFCacheEntry(matchingEntry);
childDocIdents = matchingEntry.childDocIdents;
}
}
if (aEntry.owner_b64) {
let ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
let binaryData = atob(aEntry.owner_b64);
ownerInput.setData(binaryData, binaryData.length);
let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIObjectInputStream);
binaryStream.setInputStream(ownerInput);
try { // Catch possible deserialization exceptions
shEntry.owner = binaryStream.readObject(true);
} catch (ex) { dump(ex); }
}
if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
for (let i = 0; i < aEntry.children.length; i++) {
if (!aEntry.children[i].url)
continue;
// We're getting sessionrestore.js files with a cycle in the
// doc-identifier graph, likely due to bug 698656. (That is, we have
// an entry where doc identifier A is an ancestor of doc identifier B,
// and another entry where doc identifier B is an ancestor of A.)
//
// If we were to respect these doc identifiers, we'd create a cycle in
// the SHEntries themselves, which causes the docshell to loop forever
// when it looks for the root SHEntry.
//
// So as a hack to fix this, we restrict the scope of a doc identifier
// to be a node's siblings and cousins, and pass childDocIdents, not
// aDocIdents, to _deserializeHistoryEntry. That is, we say that two
// SHEntries with the same doc identifier have the same document iff
// they have the same parent or their parents have the same document.
shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap, childDocIdents), i);
}
}
return shEntry;
},
sendHistory: function sendHistory() {
// We need to package up the session history and send it to the sessionstore
let entries = [];
let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
for (let i = 0; i < history.count; i++) {
let entry = this._serializeHistoryEntry(history.getEntryAtIndex(i, false));
entries.push(entry);
}
let index = history.index + 1;
sendAsyncMessage("Content:SessionHistory", { entries: entries, index: index });
},
_serializeHistoryEntry: function _serializeHistoryEntry(aEntry) {
let entry = { url: aEntry.URI.spec };
if (aEntry.title && aEntry.title != entry.url)
entry.title = aEntry.title;
if (!(aEntry instanceof Ci.nsISHEntry))
return entry;
let cacheKey = aEntry.cacheKey;
if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 && cacheKey.data != 0)
entry.cacheKey = cacheKey.data;
entry.ID = aEntry.ID;
entry.docshellID = aEntry.docshellID;
if (aEntry.referrerURI)
entry.referrer = aEntry.referrerURI.spec;
if (aEntry.contentType)
entry.contentType = aEntry.contentType;
let x = {}, y = {};
aEntry.getScrollPosition(x, y);
if (x.value != 0 || y.value != 0)
entry.scroll = x.value + "," + y.value;
if (aEntry.owner) {
try {
let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIObjectOutputStream);
let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
pipe.init(false, false, 0, 0xffffffff, null);
binaryStream.setOutputStream(pipe.outputStream);
binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true);
binaryStream.close();
// Now we want to read the data from the pipe's input end and encode it.
let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
scriptableStream.setInputStream(pipe.inputStream);
let ownerBytes = scriptableStream.readByteArray(scriptableStream.available());
// We can stop doing base64 encoding once our serialization into JSON
// is guaranteed to handle all chars in strings, including embedded
// nulls.
entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
} catch (e) { dump(e); }
}
entry.docIdentifier = aEntry.BFCacheEntry.ID;
if (aEntry.stateData != null) {
entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
entry.structuredCloneVersion = aEntry.stateData.formatVersion;
}
if (!(aEntry instanceof Ci.nsISHContainer))
return entry;
if (aEntry.childCount > 0) {
entry.children = [];
for (let i = 0; i < aEntry.childCount; i++) {
let child = aEntry.GetChildAt(i);
if (child)
entry.children.push(this._serializeHistoryEntry(child));
else // to maintain the correct frame order, insert a dummy entry
entry.children.push({ url: "about:blank" });
// don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
delete entry.children;
break;
}
}
}
return entry;
}
};
WebNavigation.init();
let DOMEvents = {
init: function() {
addEventListener("DOMContentLoaded", this, false);
addEventListener("DOMTitleChanged", this, false);
addEventListener("DOMLinkAdded", this, false);
addEventListener("DOMWillOpenModalDialog", this, false);
addEventListener("DOMModalDialogClosed", this, true);
addEventListener("DOMWindowClose", this, false);
addEventListener("DOMPopupBlocked", this, false);
addEventListener("pageshow", this, false);
addEventListener("pagehide", this, false);
},
handleEvent: function(aEvent) {
let document = content.document;
switch (aEvent.type) {
case "DOMContentLoaded":
if (document.documentURIObject.spec == "about:blank")
return;
sendAsyncMessage("DOMContentLoaded", { });
// Send the session history now too
WebNavigation.sendHistory();
break;
case "pageshow":
case "pagehide": {
if (aEvent.target.defaultView != content)
break;
let util = aEvent.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let json = {
contentWindowWidth: content.innerWidth,
contentWindowHeight: content.innerHeight,
windowId: util.outerWindowID,
persisted: aEvent.persisted
};
// Clear onload focus to prevent the VKB to be shown unexpectingly
// but only if the location has really changed and not only the
// fragment identifier
let contentWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
if (!WebProgressListener.hashChanged && contentWindowID == util.currentInnerWindowID) {
let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
focusManager.clearFocus(content);
}
sendAsyncMessage(aEvent.type, json);
break;
}
case "DOMPopupBlocked": {
let util = aEvent.requestingWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let json = {
windowId: util.outerWindowID,
popupWindowURI: {
spec: aEvent.popupWindowURI.spec,
charset: aEvent.popupWindowURI.originCharset
},
popupWindowFeatures: aEvent.popupWindowFeatures,
popupWindowName: aEvent.popupWindowName
};
sendAsyncMessage("DOMPopupBlocked", json);
break;
}
case "DOMTitleChanged":
sendAsyncMessage("DOMTitleChanged", { title: document.title });
break;
case "DOMLinkAdded":
let target = aEvent.originalTarget;
if (!target.href || target.disabled)
return;
let json = {
windowId: target.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
href: target.href,
charset: document.characterSet,
title: target.title,
rel: target.rel,
type: target.type
};
// rel=icon can also have a sizes attribute
if (target.hasAttribute("sizes"))
json.sizes = target.getAttribute("sizes");
sendAsyncMessage("DOMLinkAdded", json);
break;
case "DOMWillOpenModalDialog":
case "DOMModalDialogClosed":
case "DOMWindowClose":
let retvals = sendSyncMessage(aEvent.type, { });
for (let i in retvals) {
if (retvals[i].preventDefault) {
aEvent.preventDefault();
break;
}
}
break;
}
}
};
DOMEvents.init();
let ContentScroll = {
_scrollOffset: { x: 0, y: 0 },
init: function() {
addMessageListener("Content:SetCacheViewport", this);
addMessageListener("Content:SetWindowSize", this);
addEventListener("scroll", this, false);
addEventListener("pagehide", this, false);
addEventListener("MozScrolledAreaChanged", this, false);
},
getScrollOffset: function(aWindow) {
let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let scrollX = {}, scrollY = {};
cwu.getScrollXY(false, scrollX, scrollY);
return { x: scrollX.value, y: scrollY.value };
},
getScrollOffsetForElement: function(aElement) {
if (aElement.parentNode == aElement.ownerDocument)
return this.getScrollOffset(aElement.ownerDocument.defaultView);
return { x: aElement.scrollLeft, y: aElement.scrollTop };
},
setScrollOffsetForElement: function(aElement, aLeft, aTop) {
if (aElement.parentNode == aElement.ownerDocument) {
aElement.ownerDocument.defaultView.scrollTo(aLeft, aTop);
} else {
aElement.scrollLeft = aLeft;
aElement.scrollTop = aTop;
}
},
receiveMessage: function(aMessage) {
let json = aMessage.json;
switch (aMessage.name) {
case "Content:SetCacheViewport": {
// Set resolution for root view
let rootCwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
if (json.id == 1) {
rootCwu.setResolution(json.scale, json.scale);
if (!WebProgressListener._firstPaint)
break;
}
let displayport = new Rect(json.x, json.y, json.w, json.h);
if (displayport.isEmpty())
break;
// Map ID to element
let element = null;
try {
element = rootCwu.findElementWithViewId(json.id);
} catch(e) {
// This could give NS_ERROR_NOT_AVAILABLE. In that case, the
// presshell is not available because the page is reloading.
}
if (!element)
break;
let binding = element.ownerDocument.getBindingParent(element);
if (binding instanceof Ci.nsIDOMHTMLInputElement && binding.mozIsTextField(false))
break;
// Set the scroll offset for this element if specified
if (json.scrollX >= 0 && json.scrollY >= 0) {
this.setScrollOffsetForElement(element, json.scrollX, json.scrollY)
if (json.id == 1)
this._scrollOffset = this.getScrollOffset(content);
}
// Set displayport. We want to set this after setting the scroll offset, because
// it is calculated based on the scroll offset.
let scrollOffset = this.getScrollOffsetForElement(element);
let x = displayport.x - scrollOffset.x;
let y = displayport.y - scrollOffset.y;
if (json.id == 1) {
x = Math.round(x * json.scale) / json.scale;
y = Math.round(y * json.scale) / json.scale;
}
let win = element.ownerDocument.defaultView;
let winCwu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
winCwu.setDisplayPortForElement(x, y, displayport.width, displayport.height, element);
break;
}
case "Content:SetWindowSize": {
let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
cwu.setCSSViewport(json.width, json.height);
break;
}
}
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "pagehide":
this._scrollOffset = { x: 0, y: 0 };
break;
case "scroll": {
let doc = aEvent.target;
if (doc != content.document)
break;
this.sendScroll();
break;
}
case "MozScrolledAreaChanged": {
let doc = aEvent.originalTarget;
if (content != doc.defaultView) // We are only interested in root scroll pane changes
return;
sendAsyncMessage("MozScrolledAreaChanged", {
width: aEvent.width,
height: aEvent.height,
left: aEvent.x + content.scrollX
});
// Send event only after painting to make sure content views in the parent process have
// been updated.
addEventListener("MozAfterPaint", function afterPaint() {
removeEventListener("MozAfterPaint", afterPaint, false);
sendAsyncMessage("Content:UpdateDisplayPort");
}, false);
break;
}
}
},
sendScroll: function sendScroll() {
let scrollOffset = this.getScrollOffset(content);
if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y)
return;
this._scrollOffset = scrollOffset;
sendAsyncMessage("scroll", scrollOffset);
}
};
ContentScroll.init();
let ContentActive = {
init: function() {
addMessageListener("Content:Activate", this);
addMessageListener("Content:Deactivate", this);
},
receiveMessage: function(aMessage) {
let json = aMessage.json;
switch (aMessage.name) {
case "Content:Deactivate":
docShell.isActive = false;
let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
if (json.keepviewport)
break;
cwu.setDisplayPortForElement(0, 0, 0, 0, content.document.documentElement);
break;
case "Content:Activate":
docShell.isActive = true;
break;
}
}
};
ContentActive.init();
/**
* Helper class for IndexedDB, child part. Listens using
* the observer service for events regarding IndexedDB
* prompts, and sends messages to the parent to actually
* show the prompts.
*/
let IndexedDB = {
_permissionsPrompt: "indexedDB-permissions-prompt",
_permissionsResponse: "indexedDB-permissions-response",
_quotaPrompt: "indexedDB-quota-prompt",
_quotaResponse: "indexedDB-quota-response",
_quotaCancel: "indexedDB-quota-cancel",
waitingObservers: [],
init: function IndexedDBPromptHelper_init() {
let os = Services.obs;
os.addObserver(this, this._permissionsPrompt, false);
os.addObserver(this, this._quotaPrompt, false);
os.addObserver(this, this._quotaCancel, false);
addMessageListener("IndexedDB:Response", this);
},
observe: function IndexedDBPromptHelper_observe(aSubject, aTopic, aData) {
if (aTopic != this._permissionsPrompt && aTopic != this._quotaPrompt && aTopic != this._quotaCancel) {
throw new Error("Unexpected topic!");
}
let requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
let observer = requestor.getInterface(Ci.nsIObserver);
let contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
let contentDocument = contentWindow.document;
if (aTopic == this._quotaCancel) {
observer.observe(null, this._quotaResponse, Ci.nsIPermissionManager.UNKNOWN_ACTION);
return;
}
// Remote to parent
sendAsyncMessage("IndexedDB:Prompt", {
topic: aTopic,
host: contentDocument.documentURIObject.asciiHost,
location: contentDocument.location.toString(),
data: aData,
observerId: this.addWaitingObserver(observer)
});
},
receiveMessage: function(aMessage) {
let payload = aMessage.json;
switch (aMessage.name) {
case "IndexedDB:Response":
let observer = this.getAndRemoveWaitingObserver(payload.observerId);
observer.observe(null, payload.responseTopic, payload.permission);
}
},
addWaitingObserver: function(aObserver) {
let observerId = 0;
while (observerId in this.waitingObservers)
observerId++;
this.waitingObservers[observerId] = aObserver;
return observerId;
},
getAndRemoveWaitingObserver: function(aObserverId) {
let observer = this.waitingObservers[aObserverId];
delete this.waitingObservers[aObserverId];
return observer;
}
};
IndexedDB.init();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,61 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="error" extends="chrome://browser/content/bindings/bindings.xml#richlistitem">
<content orient="vertical">
<xul:hbox class="console-row-internal-box" flex="1">
<xul:vbox class="console-row-content" flex="1">
<xul:hbox class="console-row-msg" align="start">
<xul:label class="label title" xbl:inherits="value=typetext"/>
<xul:description class="console-error-msg title" xbl:inherits="xbl:text=msg" flex="1"/>
</xul:hbox>
<xul:hbox class="console-row-file" xbl:inherits="hidden=hideSource">
<xul:label class="label title" value="&consoleErrFile.label;"/>
<xul:label class="title" xbl:inherits="value=href" crop="right"/>
<xul:spacer flex="1"/>
<xul:hbox class="lineNumberRow" xbl:inherits="line">
<xul:label class="label title" value="&consoleErrLine.label;"/>
<xul:label class="label title" xbl:inherits="value=line"/>
</xul:hbox>
</xul:hbox>
<xul:vbox class="console-row-code" xbl:inherits="hidden=hideCode">
<xul:label class="monospace console-code" xbl:inherits="value=code" crop="end"/>
<xul:hbox xbl:inherits="hidden=hideCaret">
<xul:label class="monospace console-dots title" xbl:inherits="value=errorDots"/>
<xul:label class="monospace console-caret title" xbl:inherits="value=errorCaret"/>
<xul:spacer flex="1"/>
</xul:hbox>
</xul:vbox>
</xul:vbox>
</xul:hbox>
</content>
</binding>
<binding id="message" extends="chrome://browser/content/bindings/bindings.xml#richlistitem">
<content>
<xul:hbox class="console-internal-box" flex="1">
<xul:vbox class="console-row-content" flex="1">
<xul:vbox class="console-row-msg" flex="1">
<xul:description class="console-msg-text title" xbl:inherits="xbl:text=msg"/>
</xul:vbox>
</xul:vbox>
</xul:hbox>
</content>
</binding>
</bindings>

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

@ -0,0 +1,109 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="dialog">
<content xbl:inherits="orient, closebutton" flex="1">
<children/>
</content>
<implementation implements="nsIDOMEventListener">
<field name="arguments"/>
<field name="parent"/>
<property name="_scrollbox" readonly="true" onget="return this.getElementsByTagName('scrollbox')[0];"/>
<constructor><![CDATA[
this._closed = false;
if (this.hasAttribute("script")) {
try {
Services.scriptloader.loadSubScript(this.getAttribute("script"), this);
} catch(e) {
throw("Dialog : Unable to load script : " + this.getAttribute("script") + "\n");
}
}
window.addEventListener("unload", this, true);
let scrollbox = this._scrollbox;
if (scrollbox) {
window.addEventListener("resize", this, true);
scrollbox.addEventListener("overflow", this, true);
}
setTimeout(this.load.bind(this), 0);
]]></constructor>
<method name="handleEvent">
<parameter name="aEvent"/>
<body><![CDATA[
switch(aEvent.type) {
case "unload":
if (aEvent.originalTarget == document)
this._removeDialog();
break;
case "resize":
case "overflow":
let scrollbox = this._scrollbox;
let style = document.defaultView.getComputedStyle(scrollbox, null);
let newHeight = Math.ceil(scrollbox.firstChild.getBoundingClientRect().height) +
parseInt(style.marginTop) +
parseInt(style.marginBottom);
scrollbox.style.minHeight = Math.min(window.innerHeight / 2, newHeight) + "px";
break;
}
]]></body>
</method>
<method name="load">
<body><![CDATA[
if (this.hasAttribute("onload")) {
let func = new Function(this.getAttribute("onload"));
func.call(this);
}
]]></body>
</method>
<method name="close">
<body><![CDATA[
if (this.hasAttribute("onclose")) {
let func = new Function(this.getAttribute("onclose"));
func.call(this);
}
this._removeDialog();
]]></body>
</method>
<method name="_removeDialog">
<body><![CDATA[
window.removeEventListener("unload", this, true);
let scrollbox = this._scrollbox;
if (scrollbox) {
window.removeEventListener("resize", this, true);
scrollbox.removeEventListener("overflow", this, true);
}
this.parentNode.parentNode.removeChild(this.parentNode);
this._closed = true;
// emit DOMModalDialogClosed event
let event = document.createEvent("Events");
event.initEvent("DOMModalDialogClosed", true, false);
let dispatcher = this.parent || getBrowser();
dispatcher.dispatchEvent(event);
]]></body>
</method>
<method name="waitForClose">
<body><![CDATA[
while (!this._closed)
Services.tm.currentThread.processNextEvent(true);
]]></body>
</method>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,208 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="download-base" extends="chrome://browser/content/bindings/grid.xml#richgrid-item">
<content>
<xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox class="richgrid-icon-container">
<xul:box class="richgrid-icon-box"><xul:image xbl:inherits="src=iconURI"/></xul:box>
<xul:box flex="1" />
</xul:hbox>
<xul:description xbl:inherits="value=label" crop="end"/>
</xul:vbox>
<children/>
</content>
<implementation>
<field name="nsIDLMgr">Components.interfaces.nsIDownloadManager</field>
<property name="paused">
<getter>
<![CDATA[
return parseInt(this.getAttribute("state")) == this.nsIDLMgr.DOWNLOAD_PAUSED;
]]>
</getter>
</property>
<property name="openable">
<getter>
<![CDATA[
return parseInt(this.getAttribute("state")) == this.nsIDLMgr.DOWNLOAD_FINISHED;
]]>
</getter>
</property>
<property name="inProgress">
<getter>
<![CDATA[
var state = parseInt(this.getAttribute("state"));
return state == this.nsIDLMgr.DOWNLOAD_NOTSTARTED ||
state == this.nsIDLMgr.DOWNLOAD_QUEUED ||
state == this.nsIDLMgr.DOWNLOAD_DOWNLOADING ||
state == this.nsIDLMgr.DOWNLOAD_PAUSED ||
state == this.nsIDLMgr.DOWNLOAD_SCANNING;
]]>
</getter>
</property>
<property name="removable">
<getter>
<![CDATA[
var state = parseInt(this.getAttribute("state"));
return state == this.nsIDLMgr.DOWNLOAD_FINISHED ||
state == this.nsIDLMgr.DOWNLOAD_CANCELED ||
state == this.nsIDLMgr.DOWNLOAD_BLOCKED_PARENTAL ||
state == this.nsIDLMgr.DOWNLOAD_BLOCKED_POLICY ||
state == this.nsIDLMgr.DOWNLOAD_DIRTY ||
state == this.nsIDLMgr.DOWNLOAD_FAILED;
]]>
</getter>
</property>
</implementation>
</binding>
<binding id="download-not-started" extends="#download-base">
<content orient="horizontal" align="start">
<xul:image validate="always" xbl:inherits="src=iconURL"/>
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
<xul:label class="normal" xbl:inherits="value=datetime"/>
</xul:hbox>
<xul:hbox>
<xul:label class="normal" xbl:inherits="value=status"/>
</xul:hbox>
<xul:hbox class="show-on-select" align="center">
<xul:button anonid="showpage-button" label="&downloadShowPage.label;"
oncommand="Downloads.showPage(document.getBindingParent(this));"/>
<xul:spacer flex="1"/>
</xul:hbox>
</xul:vbox>
</content>
<implementation>
<constructor>
<![CDATA[
let referrer = this.hasAttribute("referrer");
if (!referrer)
document.getAnonymousElementByAttribute(this, "anonid", "showpage-button").setAttribute("disabled", "true");
]]>
</constructor>
</implementation>
</binding>
<binding id="download-downloading" extends="#download-base">
<content orient="horizontal" align="start">
<xul:image validate="always" xbl:inherits="src=iconURL"/>
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
<xul:label class="normal" xbl:inherits="value=datetime"/>
</xul:hbox>
<xul:hbox align="center">
<xul:progressmeter anonid="progressmeter" mode="normal" value="0" flex="1" xbl:inherits="value=progress,mode=progressmode"/>
<xul:button class="download-pause" label="&downloadPause.label;"
oncommand="Downloads.pauseDownload(document.getBindingParent(this));"/>
<xul:button class="download-cancel" label="&downloadCancel.label;"
oncommand="Downloads.cancelDownload(document.getBindingParent(this));"/>
</xul:hbox>
<xul:label class="normal" xbl:inherits="value=status" crop="end"/>
</xul:vbox>
</content>
</binding>
<binding id="download-paused" extends="#download-base">
<content orient="horizontal" align="start">
<xul:image validate="always" xbl:inherits="src=iconURL"/>
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
<xul:label class="normal" xbl:inherits="value=datetime"/>
</xul:hbox>
<xul:hbox align="center">
<xul:progressmeter anonid="progressmeter" mode="normal" value="0" flex="1" xbl:inherits="value=progress,mode=progressmode"/>
<xul:button class="download-resume" label="&downloadResume.label;"
oncommand="Downloads.resumeDownload(document.getBindingParent(this));"/>
<xul:button class="download-cancel" label="&downloadCancel.label;"
oncommand="Downloads.cancelDownload(document.getBindingParent(this));"/>
</xul:hbox>
<xul:label class="normal" xbl:inherits="value=status" crop="end"/>
</xul:vbox>
</content>
</binding>
<binding id="download-retry" extends="#download-base">
<content orient="horizontal" align="start">
<xul:image validate="always" xbl:inherits="src=iconURL"/>
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
<xul:label class="normal" xbl:inherits="value=datetime"/>
</xul:hbox>
<xul:hbox>
<xul:label class="normal" xbl:inherits="value=status" crop="end" flex="1"/>
<xul:label class="hide-on-select download-retry-failed normal" value="&downloadFailed.label;" xbl:inherits="state" />
<xul:button class="show-on-select download-retry" label="&downloadRetry.label;"
oncommand="Downloads.retryDownload(document.getBindingParent(this));"/>
<xul:button class="show-on-select download-remove" label="&downloadRemove.label;"
oncommand="Downloads.removeDownload(document.getBindingParent(this));"/>
</xul:hbox>
</xul:vbox>
</content>
</binding>
<binding id="download-done" extends="#download-base">
<content orient="horizontal" align="start">
<xul:image validate="always" xbl:inherits="src=iconURL"/>
<xul:vbox flex="1" anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name" crop="center" flex="1"/>
<xul:label class="normal" xbl:inherits="value=datetime"/>
</xul:hbox>
<xul:hbox>
<xul:label class="normal" xbl:inherits="value=status"/>
</xul:hbox>
<xul:hbox class="show-on-select" align="center">
<xul:button anonid="showpage-button" label="&downloadShowPage.label;"
oncommand="Downloads.showPage(document.getBindingParent(this));"/>
<xul:spacer flex="1"/>
<xul:button anonid="open-button" label="&downloadOpen2.label;"
oncommand="Downloads.openDownload(document.getBindingParent(this));"/>
<xul:button anonid="remove-button" label="&downloadRemove.label;"
oncommand="Downloads.removeDownload(document.getBindingParent(this));"/>
</xul:hbox>
</xul:vbox>
</content>
<implementation>
<constructor>
<![CDATA[
let referrer = this.hasAttribute("referrer");
if (!referrer)
document.getAnonymousElementByAttribute(this, "anonid", "showpage-button").setAttribute("disabled", "true");
let file = Downloads._getLocalFile(this.getAttribute("target"));
let mimeService = Components.classes["@mozilla.org/mime;1"].getService(Components.interfaces.nsIMIMEService);
let mimeType;
try {
mimeType = mimeService.getTypeFromFile(file);
}
catch(e) {}
if (!file.exists() || !mimeType)
document.getAnonymousElementByAttribute(this, "anonid", "open-button").setAttribute("disabled", "true");
]]>
</constructor>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,95 @@
<?xml version="1.0"?>
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="flyoutpanelBinding" extends="xul:box">
<resources>
<stylesheet src="chrome://browser/skin/flyoutpanel.css"/>
</resources>
<content>
<xul:vbox class="flyoutpanel-wrapper">
<xul:hbox class="flyoutpanel-header" align="top">
<xul:toolbarbutton class="flyout-close-button"
command="cmd_flyout_back"
xbl:inherits="command"/>
<xul:label class="flyout-header-label" xbl:inherits="value=headertext"/>
</xul:hbox>
<xul:scrollbox class="flyoutpanel-contents" flex="1" orient="vertical">
<children/>
</xul:scrollbox>
</xul:vbox>
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
window.addEventListener('MozContextUIShow', this);
window.addEventListener('MozContextUIDismiss', this);
window.addEventListener('MozAppbarDismiss', this);
]]>
</constructor>
<destructor>
<![CDATA[
window.removeEventListener('MozContextUIShow', this);
window.removeEventListener('MozContextUIDismiss', this);
window.removeEventListener('MozAppbarDismiss', this);
]]>
</destructor>
<property name="isVisible" readonly="true">
<getter>
<![CDATA[
return this.hasAttribute("visible");
]]>
</getter>
</property>
<method name="hide">
<body>
<![CDATA[
this.removeAttribute("visible");
DialogUI.popPopup(this);
]]>
</body>
</method>
<method name="show">
<body>
<![CDATA[
this.setAttribute("visible", "true");
DialogUI.pushPopup(this, this);
]]>
</body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body>
<![CDATA[
switch (aEvent.type) {
case 'MozContextUIShow':
case 'MozAppbarDismiss':
case 'MozContextUIDismiss':
this.hide();
break;
}
]]>
</body>
</method>
<field name="anonScrollBox" readonly="true"><![CDATA[
// Expose the anyonymous scrollbox so ScrollUtils.getScrollboxFromElement can find it.
document.getAnonymousElementByAttribute(this, "class", "flyoutpanel-contents");
]]></field>
</implementation>
<handlers>
<!-- Work around for bug 835175 -->
<handler event="click">false;</handler>
</handlers>
</binding>
</bindings>

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

@ -0,0 +1,556 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="richgrid"
extends="chrome://global/content/bindings/general.xml#basecontrol">
<content>
<html:div id="grid-div" anonid="grid" class="richgrid-grid">
<children/>
</html:div>
</content>
<implementation implements="nsIDOMXULSelectControlElement">
<property name="_grid" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'grid');"/>
<field name="controller">null</field>
<!-- nsIDOMXULMultiSelectControlElement (not fully implemented) -->
<method name="clearSelection">
<body>
<![CDATA[
// 'selection' and 'selected' are confusingly overloaded here
// as richgrid is adopting multi-select behavior, but select/selected are already being
// used to describe triggering the default action of a tile
if (this._selectedItem){
this._selectedItem.selected = false;
this._selectedItem = null;
}
for (let childItem of this.selectedItems) {
childItem.selected = false;
}
// reset context actions
this._contextActions = null;
]]>
</body>
</method>
<method name="toggleItemSelection">
<parameter name="anItem"/>
<body>
<![CDATA[
anItem.selected = !anItem.selected;
this._fireOnSelectionChange();
]]>
</body>
</method>
<method name="selectItem">
<parameter name="anItem"/>
<body>
<![CDATA[
this.clearSelection();
this._selectedItem = anItem;
this._selectedItem.selected = true;
this._fireOnSelect();
]]>
</body>
</method>
<method name="handleItemClick">
<parameter name="aItem"/>
<body>
<![CDATA[
if (this.controller)
this.controller.handleItemClick(aItem);
]]>
</body>
</method>
<method name="handleItemContextMenu">
<parameter name="aItem"/>
<parameter name="aEvent"/>
<body>
<![CDATA[
// we'll republish this as a selectionchange event on the grid
aEvent.stopPropagation();
this.toggleItemSelection(aItem);
]]>
</body>
</method>
<field name="_contextActions">null</field>
<property name="contextActions">
<getter>
<![CDATA[
// return the subset of verbs that apply to all selected tiles
// use cached list, it'll get cleared out when selection changes
if (this._contextActions) {
return this._contextActions;
}
let tileNodes = this.selectedItems;
if (!tileNodes.length) {
return new Set();
}
function cloneSet(aSet) {
let clone = new Set();
if (aSet) {
for (let item of aSet) {
clone.add(item);
}
}
return clone;
}
// given one or more sets of values,
// return a set with only those values present in each
let initialItem = tileNodes[0];
let verbSet = cloneSet(initialItem.contextActions);
for (let i=1; i<tileNodes.length; i++){
let set = tileNodes[i].contextActions;
for (let item of verbSet) {
if (!set.has(item)){
verbSet.delete(item);
}
}
}
this._contextActions = verbSet;
// returns Set
return this._contextActions;
]]>
</getter>
</property>
<!-- nsIDOMXULSelectControlElement -->
<property name="itemCount" readonly="true" onget="return this.children.length;"/>
<field name="_selectedItem">null</field>
<property name="selectedItem" onget="return this._selectedItem;">
<setter>
<![CDATA[
this.selectItem(val);
]]>
</setter>
</property>
<!-- partial implementation of multiple selection interface -->
<property name="selectedItems">
<getter>
<![CDATA[
return this.querySelectorAll("richgriditem[selected='true']");
]]>
</getter>
</property>
<property name="selectedIndex">
<getter>
<![CDATA[
return this.getIndexOfItem(this._selectedItem);
]]>
</getter>
<setter>
<![CDATA[
if (val >= 0) {
let selected = this.getItemAtIndex(val);
this.selectItem(selected);
} else {
this.clearSelection();
}
]]>
</setter>
</property>
<method name="appendItem">
<parameter name="aLabel"/>
<parameter name="aValue"/>
<body>
<![CDATA[
let addition = this._createItemElement(aLabel, aValue);
this.appendChild(addition);
this.arrangeItems();
return addition;
]]>
</body>
</method>
<method name="insertItemAt">
<parameter name="anIndex"/>
<parameter name="aLabel"/>
<parameter name="aValue"/>
<body>
<![CDATA[
let existing = this.getItemAtIndex(anIndex);
let addition = this._createItemElement(aLabel, aValue);
if (existing) {
this.insertBefore(addition, existing);
} else {
this.appendChild(addition);
}
this.arrangeItems();
return addition;
]]>
</body>
</method>
<method name="removeItemAt">
<parameter name="anIndex"/>
<body>
<![CDATA[
let removal = this.getItemAtIndex(anIndex);
this.removeChild(removal);
this.arrangeItems();
return removal;
]]>
</body>
</method>
<method name="getIndexOfItem">
<parameter name="anItem"/>
<body>
<![CDATA[
if (!anItem)
return -1;
return Array.prototype.indexOf.call(this.children, anItem);
]]>
</body>
</method>
<method name="getItemAtIndex">
<parameter name="anIndex"/>
<body>
<![CDATA[
if (!this._isIndexInBounds(anIndex))
return null;
return this.children.item(anIndex);
]]>
</body>
</method>
<!-- Interface for offsetting selection and checking bounds -->
<property name="isSelectionAtStart" readonly="true"
onget="return this.selectedIndex == 0;"/>
<property name="isSelectionAtEnd" readonly="true"
onget="return this.selectedIndex == (this.itemCount - 1);"/>
<property name="isSelectionInStartRow" readonly="true">
<getter>
<![CDATA[
return this.selectedIndex < this.columnCount;
]]>
</getter>
</property>
<property name="isSelectionInEndRow" readonly="true">
<getter>
<![CDATA[
let lowerBound = (this.rowCount - 1) * this.columnCount;
let higherBound = this.rowCount * this.columnCount;
return this.selectedIndex >= lowerBound &&
this.selectedIndex < higherBound;
]]>
</getter>
</property>
<method name="offsetSelection">
<parameter name="aOffset"/>
<body>
<![CDATA[
let newIndex = this.selectedIndex + aOffset;
if (this._isIndexInBounds(newIndex))
this.selectedIndex = newIndex;
]]>
</body>
</method>
<method name="offsetSelectionByRow">
<parameter name="aRowOffset"/>
<body>
<![CDATA[
let newIndex = this.selectedIndex + (this.columnCount * aRowOffset);
if (this._isIndexInBounds(newIndex))
this.selectedIndex -= this.columnCount;
]]>
</body>
</method>
<!-- Interface for grid layout management -->
<field name="_rowCount">0</field>
<property name="rowCount" readonly="true" onget="return this._rowCount;"/>
<field name="_columnCount">0</field>
<property name="columnCount" readonly="true" onget="return this._columnCount;"/>
<field name="_scheduledArrangeItemsTries">0</field>
<!-- define a height where we consider an item not yet rendered
10 is the height of the empty item (padding/border etc. only) -->
<field name="_itemHeightRenderThreshold">10</field>
<method name="arrangeItems">
<parameter name="aNumRows"/>
<parameter name="aNumColumns"/>
<body>
<![CDATA[
if (this.itemCount <= 0)
return;
let item = this.getItemAtIndex(0);
if (item == null)
return;
let gridItemRect = item.getBoundingClientRect();
// cap the number of times we reschedule calling arrangeItems
let maxRetries = 5;
// delay as necessary until the item has a proper height
if (gridItemRect.height <= this._itemHeightRenderThreshold) {
if (this._scheduledArrangeItemsTimerId) {
// retry of arrangeItems already scheduled
return;
}
// track how many times we've attempted arrangeItems
this._scheduledArrangeItemsTries++;
if (maxRetries > this._scheduledArrangeItemsTries) {
// schedule re-try of arrangeItems at the next tick
this._scheduledArrangeItemsTimerId = setTimeout(this.arrangeItems.bind(this), 0);
return;
}
}
// items ready to arrange (or retries max exceeded)
// reset the flags
if (this._scheduledArrangeItemsTimerId) {
clearTimeout(this._scheduledArrangeItemsTimerId);
delete this._scheduledArrangeItemsTimerId;
}
if (this._scheduledArrangeItemsTries) {
this._scheduledArrangeItemsTries = 0;
}
// Autocomplete is a binding within a binding, so we have to step
// up an additional parentNode.
let container = null;
if (this.parentNode.id == "results-vbox" ||
this.parentNode.id == "searches-vbox")
container = this.parentNode.parentNode.getBoundingClientRect();
else
container = this.parentNode.getBoundingClientRect();
// If we don't have valid dimensions we can't arrange yet
if (!container.height || !gridItemRect.height)
return;
// We favor overflowing horizontally, not vertically
let maxRowCount = Math.floor(container.height / gridItemRect.height) - 1;
if (aNumRows) {
this._rowCount = aNumRows;
} else {
this._rowCount = Math.min(this.itemCount, maxRowCount);
}
if (aNumColumns) {
this._columnCount = aNumColumns;
} else {
this._columnCount = Math.ceil(this.itemCount / this._rowCount);
}
this._grid.style.width = (this._columnCount * gridItemRect.width) + "px";
]]>
</body>
</method>
<!-- Inteface to suppress selection events -->
<field name="_suppressOnSelect"/>
<property name="suppressOnSelect"
onget="return this.getAttribute('suppressonselect') == 'true';"
onset="this.setAttribute('suppressonselect', val);"/>
<!-- Internal methods -->
<constructor>
<![CDATA[
if (this.controller && this.controller.gridBoundCallback != undefined)
this.controller.gridBoundCallback();
// XXX This event was never actually implemented (bug 223411).
var event = document.createEvent("Events");
event.initEvent("contentgenerated", true, true);
this.dispatchEvent(event);
]]>
</constructor>
<method name="_isIndexInBounds">
<parameter name="anIndex"/>
<body>
<![CDATA[
return anIndex >= 0 && anIndex < this.itemCount;
]]>
</body>
</method>
<method name="_createItemElement">
<parameter name="aLabel"/>
<parameter name="aValue"/>
<body>
<![CDATA[
let item = this.ownerDocument.createElement("richgriditem");
item.control = this;
item.setAttribute("label", aLabel);
if (aValue)
item.setAttribute("value", aValue);
// copy over the richgrid's data-contextactions as each child is created
if (this.hasAttribute("data-contextactions")) {
item.setAttribute("data-contextactions", this.getAttribute("data-contextactions"));
}
return item;
]]>
</body>
</method>
<method name="_fireOnSelect">
<body>
<![CDATA[
if (this.suppressOnSelect || this._suppressOnSelect)
return;
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="_fireOnSelectionChange">
<body>
<![CDATA[
// flush out selection-related cached properties so they get recalc'd next time
this._contextActions = null;
// fire an event?
if (this.suppressOnSelect || this._suppressOnSelect)
return;
var event = document.createEvent("Events");
event.initEvent("selectionchange", true, true);
this.dispatchEvent(event);
]]>
</body>
</method>
</implementation>
</binding>
<binding id="richgrid-item">
<content>
<xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content">
<xul:hbox class="richgrid-icon-container">
<xul:box class="richgrid-icon-box"><xul:image xbl:inherits="src=iconURI"/></xul:box>
<xul:box flex="1" />
</xul:hbox>
<xul:description xbl:inherits="value=label" crop="end"/>
</xul:vbox>
</content>
<implementation>
<property name="_box" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item');"/>
<property name="color" onset="this._color = val; this.setColor();" onget="return this._color;"/>
<property name="selected"
onget="return this.getAttribute('selected') == 'true';"
onset="this.setAttribute('selected', val);"/>
<constructor>
<![CDATA[
// Bindings don't get bound until the item is displayed,
// so we have to reset the background color when we get
// created.
this.setColor();
]]>
</constructor>
<property name="control">
<getter><![CDATA[
var parent = this.parentNode;
while (parent) {
if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
return parent;
parent = parent.parentNode;
}
return null;
]]></getter>
</property>
<method name="setColor">
<body>
<![CDATA[
if (this.color != undefined) {
this._box.parentNode.setAttribute("customColorPresent", "true");
this._box.style.backgroundColor = this.color;
} else {
this._box.parentNode.removeAttribute("customColorPresent");
}
]]>
</body>
</method>
<field name="_contextActions">null</field>
<property name="contextActions">
<getter>
<![CDATA[
if(!this._contextActions) {
this._contextActions = new Set();
let actionSet = this._contextActions;
let actions = this.getAttribute("data-contextactions");
if (actions) {
actions.split(/[,\s]+/).forEach(function(verb){
actionSet.add(verb);
});
}
}
return this._contextActions;
]]>
</getter>
</property>
</implementation>
<handlers>
<handler event="click" button="0">
<![CDATA[
// left-click/touch handler
this.control.handleItemClick(this, event);
]]>
</handler>
<handler event="contextmenu">
<![CDATA[
// fires for right-click, long-click and (keyboard) contextmenu input
// TODO: handle cross-slide event when it becomes available,
// .. using contextmenu is a stop-gap measure to allow us to
// toggle the selected state of tiles in a grid
this.control.handleItemContextMenu(this, event);
]]>
</handler>
</handlers>
</binding>
</bindings>

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

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<bindings xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="pageaction" display="xul:hbox">
<content>
<xul:hbox align="center">
<xul:image xbl:inherits="src=image" class="pageaction-image"/>
</xul:hbox>
<xul:vbox pack="center" flex="1">
<xul:label class="pageaction-title" xbl:inherits="value=title" crop="end"/>
<xul:label class="pageaction-desc" value="" xbl:inherits="value=description" crop="end"/>
</xul:vbox>
</content>
</binding>
</bindings>

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

@ -0,0 +1,126 @@
<?xml version="1.0"?>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="selection-binding">
<content>
<html:div flex="1" class="selection-overlay-inner window-width window-height" anonid="selection-overlay-inner">
<xul:stack>
<html:div anonid="selection-overlay-debug" class="window-width window-height"/>
<xul:toolbarbutton id="selectionhandle-start" label="^" left="0" top="10" hidden="false"/>
<xul:toolbarbutton id="selectionhandle-end" label="^" left="100" top="10" hidden="false"/>
</xul:stack>
</html:div>
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
]]>
</constructor>
<destructor>
<![CDATA[
]]>
</destructor>
<field name="_selectionOverlay" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "selection-overlay-inner").parentNode;</field>
<field name="_selectionDebugOverlay" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "selection-overlay-debug");</field>
<property name="enabled">
<setter>
<![CDATA[
if (val) {
this._selectionOverlay.setAttribute("class", "selection-overlay window-width window-height");
} else {
this._selectionOverlay.setAttribute("class", "selection-overlay-hidden");
while(this._selectionDebugOverlay.hasChildNodes()) this._selectionDebugOverlay.removeChild(this._selectionDebugOverlay.firstChild);
}
return val;
]]>
</setter>
<getter>
<![CDATA[
return (this._selectionOverlay.getAttribute("class") == "selection-overlay");
]]>
</getter>
</property>
<property name="displayDebugLayer">
<setter>
<![CDATA[
if (val) {
this._selectionDebugOverlay.style.display = "block";
this._debugLayerVisible = true;
} else {
this._selectionDebugOverlay.style.display = "none";
this._debugLayerVisible = false;
}
return this._debugLayerVisible;
]]>
</setter>
<getter>
<![CDATA[
if (this._debugLayerVisible == "undefined")
this._debugLayerVisible = false;
return this._debugLayerVisible;
]]>
</getter>
</property>
<method name="init">
<body>
<![CDATA[
this.enabled = true;
]]>
</body>
</method>
<method name="shutdown">
<body>
<![CDATA[
this.enabled = false;
]]>
</body>
</method>
<method name="addDebugRect">
<parameter name="aLeft"/>
<parameter name="aTop"/>
<parameter name="aRight"/>
<parameter name="aBottom"/>
<parameter name="aColor"/>
<parameter name="aFill"/>
<parameter name="aId"/>
<body>
<![CDATA[
let e = document.createElement("div");
this._selectionDebugOverlay.appendChild(e);
e.style.position = "fixed";
e.style.left = aLeft +"px";
e.style.top = aTop +"px";
e.style.right = aRight +"px";
e.style.bottom = aBottom +"px";
e.style.maxWidth = (aRight - aLeft) +"px";
e.style.width = (aRight - aLeft) +"px";
e.style.maxHeight = (aBottom - aTop) +"px";
e.style.height = (aBottom - aTop) +"px";
if (aFill == undefined) {
e.style.backgroundColor = aColor;
} else {
if (aFill) {
e.style.backgroundColor = aColor;
} else {
e.style.border = "2px solid " + aColor;
}
}
e.style.opacity = "0.5";
]]>
</body>
</method>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,173 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="documenttab">
<content observes="bcast_urlbarState">
<xul:stack class="documenttab-container">
<xul:box anonid="thumbnail" class="documenttab-thumbnail" />
<xul:image anonid="favicon" class="documenttab-favicon"
observes="bcast_urlbarState" width="26" height="26"/>
<xul:label anonid="title" class="documenttab-title" bottom="0" start="0" end="0" crop="end"/>
<xul:box anonid="selection" class="documenttab-crop"/>
<xul:box anonid="selection" class="documenttab-selection"/>
<xul:button anonid="close" class="documenttab-close" observes="bcast_urlbarState" end="0" top="0"
onclick="event.stopPropagation(); document.getBindingParent(this)._onClose()"/>
</xul:stack>
</content>
<handlers>
<handler event="click" clickcount="1" action="this._onClick()"/>
<handler event="dblclick" action="this._onDoubleClick(); event.stopPropagation();"/>
</handlers>
<implementation>
<field name="_thumbnail" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "thumbnail");</field>
<field name="_close" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "close");</field>
<field name="_title" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "title");</field>
<field name="_favicon" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "favicon");</field>
<field name="_container" readonly="true">this.parentNode;</field>
<method name="_onClick">
<body>
<![CDATA[
this._container.selectedTab = this;
let selectFn = new Function("event", this._container.parentNode.getAttribute("onselect"));
selectFn.call(this);
]]>
</body>
</method>
<method name="_onDoubleClick">
<body>
<![CDATA[
this._container.selectedTab = this;
let selectFn = new Function("event", this._container.parentNode.getAttribute("ondbltap"));
selectFn.call(this);
]]>
</body>
</method>
<method name="_onClose">
<body>
<![CDATA[
let callbackFunc = this._container.parentNode.getAttribute("onclosetab");
let closeFn = new Function("event", callbackFunc);
closeFn.call(this);
]]>
</body>
</method>
<method name="updateTitle">
<parameter name="title"/>
<body>
<![CDATA[
this._title.value = title;
]]>
</body>
</method>
<method name="updateFavicon">
<parameter name="src"/>
<body>
<![CDATA[
this._favicon.src = src;
]]>
</body>
</method>
<method name="updateThumbnailSource">
<parameter name="browser"/>
<body>
<![CDATA[
this._thumbnail.style.backgroundImage = "-moz-element(#" + browser.id + ")";
]]>
</body>
</method>
</implementation>
</binding>
<binding id="tablist">
<content>
<xul:arrowscrollbox anonid="tabs-scrollbox" class="tabs-scrollbox" flex="1" orient="horizontal"
clicktoscroll="true" />
</content>
<handlers>
<handler event="dblclick" action="this._onDoubleClick();"/>
</handlers>
<implementation>
<field name="strip">document.getAnonymousElementByAttribute(this, "anonid", "tabs-scrollbox");</field>
<field name="_selectedTab">null</field>
<!-- Used by the chrome input handler -->
<property name="anonScrollBox"
readonly="true"
onget="return this.strip;"/>
<property name="selectedTab" onget="return this._selectedTab;">
<setter>
<![CDATA[
if (this._selectedTab)
this._selectedTab.removeAttribute("selected");
if (val)
val.setAttribute("selected", "true");
this._selectedTab = val;
this.strip.ensureElementIsVisible(val);
]]>
</setter>
</property>
<method name="addTab">
<body>
<![CDATA[
let tab = document.createElement("documenttab");
this.strip.appendChild(tab);
return tab;
]]>
</body>
</method>
<method name="removeTab">
<parameter name="aTab"/>
<body>
<![CDATA[
this.strip.removeChild(aTab);
]]>
</body>
</method>
<method name="_onDoubleTap">
<body>
<![CDATA[
new Function("event", this.getAttribute("ondoubletap")).call();
]]>
</body>
</method>
<method name="_onDoubleClick">
<body>
<![CDATA[
// ignore mouse events if we're interacting with touch input
if (!InputSourceHelper.isPrecise)
return;
new Function("event", this.getAttribute("ondoubletap")).call();
]]>
</body>
</method>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,144 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % checkboxDTD SYSTEM "chrome://browser/locale/checkbox.dtd">
%checkboxDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="checkbox-toggleswitch" display="xul:box" extends="chrome://global/content/bindings/checkbox.xml#checkbox-baseline">
<content>
<xul:hbox>
<xul:description anonid="onlabel" class="onlabel" value="&checkbox.yes.label;" xbl:inherits="value=onlabel"/>
<xul:description anonid="offlabel" class="offlabel" value="&checkbox.no.label;" xbl:inherits="value=offlabel"/>
<xul:radiogroup anonid="group" xbl:inherits="disabled">
<xul:radio anonid="on" class="checkbox-radio-on"/>
<xul:radio anonid="off" class="checkbox-radio-off"/>
</xul:radiogroup>
</xul:hbox>
</content>
<implementation>
<constructor><![CDATA[
this.setChecked(this.checked);
]]></constructor>
<field name="_group">
document.getAnonymousElementByAttribute(this, "anonid", "group");
</field>
<field name="_on">
document.getAnonymousElementByAttribute(this, "anonid", "on");
</field>
<field name="_onlabel">
document.getAnonymousElementByAttribute(this, "anonid", "onlabel");
</field>
<field name="_off">
document.getAnonymousElementByAttribute(this, "anonid", "off");
</field>
<field name="_offlabel">
document.getAnonymousElementByAttribute(this, "anonid", "offlabel");
</field>
<property name="onlabel"
onget="return this._onlabel.value"
onset="this._offlabel.value=val"/>
<property name="offlabel"
onget="return this._offlabel.value"
onset="this._offlabel.value=val"/>
<method name="setChecked">
<parameter name="aValue"/>
<body>
<![CDATA[
let change = (aValue != this.checked);
if (aValue) {
this.setAttribute("checked", "true");
this._group.selectedItem = this._on;
} else {
this.removeAttribute("checked");
this._group.selectedItem = this._off;
}
if (change) {
this.fireEvent("CheckboxStateChange");
this.fireEvent("command");
}
return aValue;
]]>
</body>
</method>
<method name="fireEvent">
<parameter name="aName"/>
<body>
<![CDATA[
var event = document.createEvent("Events");
event.initEvent(aName, true, true);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="_onClickOrTap">
<parameter name="aEvent"/>
<body>
<![CDATA[
aEvent.stopPropagation();
this.setChecked(!this.checked);
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="click" button="0" phase="capturing">
<![CDATA[
this._onClickOrTap(event);
]]>
</handler>
</handlers>
</binding>
<binding id="setting-fulltoggle-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
<implementation>
<constructor>
<![CDATA[
this.input.classList.add("toggleswitch");
]]>
</constructor>
</implementation>
</binding>
<binding id="setting-fulltoggle-boolint" extends="chrome://mozapps/content/extensions/setting.xml#setting-boolint">
<implementation>
<constructor>
<![CDATA[
this.input.classList.add("toggleswitch");
]]>
</constructor>
</implementation>
</binding>
<binding id="setting-fulltoggle-localized-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-localized-bool">
<implementation>
<constructor>
<![CDATA[
this.input.classList.add("toggleswitch");
]]>
</constructor>
</implementation>
</binding>
</bindings>

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

@ -0,0 +1,364 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Utility singleton for manipulating bookmarks.
*/
var Bookmarks = {
get metroRoot() {
return PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {})[0];
},
logging: false,
log: function(msg) {
if (this.logging) {
Services.console.logStringMessage(msg);
}
},
/*
* fixupColorFormat - convert a decimal color value to a valid
* css color string of the format '#123456'
*/
fixupColorFormat: function bv_fixupColorFormat(aColor) {
let color = aColor.toString(16);
while (color.length < 6)
color = "0" + color;
return "#" + color;
},
getFaveIconPrimaryColor: function bh_getFaveIconPrimaryColor(aBookmarkId) {
if (PlacesUtils.annotations.itemHasAnnotation(aBookmarkId, 'metro/faveIconColor'))
return PlacesUtils.annotations.getItemAnnotation(aBookmarkId, 'metro/faveIconColor');
return "";
},
setFaveIconPrimaryColor: function bh_setFaveIconPrimaryColor(aBookmarkId, aColorStr) {
var anno = [{ name: "metro/faveIconColor",
type: Ci.nsIAnnotationService.TYPE_STRING,
flags: 0,
value: aColorStr,
expires: Ci.nsIAnnotationService.EXPIRE_NEVER }];
PlacesUtils.setAnnotationsForItem(aBookmarkId, anno);
},
addForURI: function bh_addForURI(aURI, aTitle, callback) {
this.isURIBookmarked(aURI, function (isBookmarked) {
if (isBookmarked)
return;
let bookmarkTitle = aTitle || aURI.spec;
let bookmarkService = PlacesUtils.bookmarks;
let bookmarkId = bookmarkService.insertBookmark(Bookmarks.metroRoot,
aURI,
bookmarkService.DEFAULT_INDEX,
bookmarkTitle);
// XXX Used for browser-chrome tests
let event = document.createEvent("Events");
event.initEvent("BookmarkCreated", true, false);
window.dispatchEvent(event);
if (callback)
callback(bookmarkId);
});
},
isURIBookmarked: function bh_isURIBookmarked(aURI, callback) {
if (!callback)
return;
PlacesUtils.asyncGetBookmarkIds(aURI, function(aItemIds) {
callback(aItemIds && aItemIds.length > 0);
}, this);
},
removeForURI: function bh_removeForURI(aURI, callback) {
// XXX blargle xpconnect! might not matter, but a method on
// nsINavBookmarksService that takes an array of items to
// delete would be faster. better yet, a method that takes a URI!
PlacesUtils.asyncGetBookmarkIds(aURI, function(aItemIds) {
aItemIds.forEach(PlacesUtils.bookmarks.removeItem);
if (callback)
callback(aURI, aItemIds);
// XXX Used for browser-chrome tests
let event = document.createEvent("Events");
event.initEvent("BookmarkRemoved", true, false);
window.dispatchEvent(event);
});
}
};
/**
* Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
* fills it with the user's bookmarks.
*
* @param aSet Control implementing nsIDOMXULSelectControlElement.
* @param {Number} aLimit Maximum number of items to show in the view.
* @param aRoot Bookmark root to show in the view.
*/
function BookmarksView(aSet, aLimit, aRoot) {
this._set = aSet;
this._set.controller = this;
this._limit = aLimit;
this._changes = new BookmarkChangeListener(this);
PlacesUtils.bookmarks.addObserver(this._changes, false);
// This also implicitly calls `getBookmarks`
this.root = aRoot;
}
BookmarksView.prototype = {
_limit: null,
_set: null,
_changes: null,
_root: null,
_sort: 0, // Natural bookmark order.
get sort() {
return this._sort;
},
set sort(aSort) {
this._sort = aSort;
this.getBookmarks();
},
get root() {
return this._root;
},
set root(aRoot) {
this._root = aRoot;
this.getBookmarks();
},
handleItemClick: function bv_handleItemClick(aItem) {
let url = aItem.getAttribute("value");
BrowserUI.goToURI(url);
},
_getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) {
return this._set.querySelector("richgriditem[bookmarkId='" + aBookmarkId + "']");
},
_getBookmarkIdForItem: function bv__getBookmarkForItem(aItem) {
return aItem.getAttribute("bookmarkId");
},
_updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
for (let name in aAttrs)
anItem.setAttribute(name, aAttrs[name]);
},
getBookmarks: function bv_getBookmarks() {
let options = gHistSvc.getNewQueryOptions();
options.queryType = options.QUERY_TYPE_BOOKMARKS;
options.excludeQueries = true; // Don't include "smart folders"
options.maxResults = this._limit;
options.sortingMode = this._sort;
let query = gHistSvc.getNewQuery();
query.setFolders([Bookmarks.metroRoot], 1);
let result = gHistSvc.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let childCount = rootNode.childCount;
for (let i = 0; i < childCount; i++) {
let node = rootNode.getChild(i);
// Ignore folders, separators, undefined item types, etc.
if (node.type != node.RESULT_TYPE_URI &&
node.type != node.RESULT_TYPE_VISIT &&
node.type != node.RESULT_TYPE_FULL_VISIT)
continue;
this.addBookmark(node.itemId);
}
rootNode.containerOpen = false;
},
inCurrentView: function bv_inCurrentView(aParentId, aIndex, aItemType) {
if (this._root && aParentId != this._root)
return false;
if (this._limit && aIndex >= this._limit)
return false;
if (aItemType != PlacesUtils.bookmarks.TYPE_BOOKMARK)
return false;
return true;
},
clearBookmarks: function bv_clearBookmarks() {
while (this._set.itemCount > 0)
this._set.removeItemAt(0);
},
addBookmark: function bv_addBookmark(aBookmarkId) {
let bookmarks = PlacesUtils.bookmarks;
let index = bookmarks.getItemIndex(aBookmarkId);
let uri = bookmarks.getBookmarkURI(aBookmarkId);
let title = bookmarks.getItemTitle(aBookmarkId) || uri.spec;
let item = this._set.insertItemAt(index, title, uri.spec);
item.setAttribute("bookmarkId", aBookmarkId);
this._updateFavicon(aBookmarkId, item, uri);
},
_updateFavicon: function _updateFavicon(aBookmarkId, aItem, aUri) {
PlacesUtils.favicons.getFaviconURLForPage(aUri, this._gotIcon.bind(this, aBookmarkId, aItem));
},
_gotIcon: function _gotIcon(aBookmarkId, aItem, aIconUri) {
aItem.setAttribute("iconURI", aIconUri ? aIconUri.spec : "");
let color = Bookmarks.getFaveIconPrimaryColor(aBookmarkId);
if (color) {
aItem.color = color;
return;
}
let url = Services.io.newURI(aIconUri.spec.replace("moz-anno:favicon:",""), "", null)
let ca = Components.classes["@mozilla.org/places/colorAnalyzer;1"]
.getService(Components.interfaces.mozIColorAnalyzer);
ca.findRepresentativeColor(url, function (success, color) {
let colorStr = Bookmarks.fixupColorFormat(color);
Bookmarks.setFaveIconPrimaryColor(aBookmarkId, colorStr);
aItem.color = colorStr;
}, this);
},
updateBookmark: function bv_updateBookmark(aBookmarkId) {
let item = this._getItemForBookmarkId(aBookmarkId);
if (!item)
return;
let bookmarks = PlacesUtils.bookmarks;
let oldIndex = this._set.getIndexOfItem(item);
let index = bookmarks.getItemIndex(aBookmarkId);
if (oldIndex != index) {
this.removeBookmark(aBookmarkId);
this.addBookmark(aBookmarkId);
return;
}
let uri = bookmarks.getBookmarkURI(aBookmarkId);
let title = bookmarks.getItemTitle(aBookmarkId) || uri.spec;
item.setAttribute("value", uri.spec);
item.setAttribute("label", title);
this._updateFavicon(aBookmarkId, item, uri);
},
removeBookmark: function bv_removeBookmark(aBookmarkId) {
let item = this._getItemForBookmarkId(aBookmarkId);
let index = this._set.getIndexOfItem(item);
this._set.removeItemAt(index);
},
destruct: function bv_destruct() {
PlacesUtils.bookmarks.removeObserver(this._changes);
}
};
var BookmarksStartView = {
_view: null,
get _grid() { return document.getElementById("start-bookmarks-grid"); },
init: function init() {
this._view = new BookmarksView(this._grid, StartUI.maxResultsPerSection, Bookmarks.metroRoot);
},
uninit: function uninit() {
this._view.destruct();
},
show: function show() {
this._grid.arrangeItems();
}
};
var BookmarksPanelView = {
_view: null,
get _grid() { return document.getElementById("bookmarks-list"); },
get visible() { return PanelUI.isPaneVisible("bookmarks-container"); },
init: function init() {
this._view = new BookmarksView(this._grid, null, Bookmarks.metroRoot);
},
show: function show() {
this._grid.arrangeItems();
},
uninit: function uninit() {
this._view.destruct();
}
};
/**
* Observes bookmark changes and keeps a linked BookmarksView updated.
*
* @param aView An instance of BookmarksView.
*/
function BookmarkChangeListener(aView) {
this._view = aView;
};
BookmarkChangeListener.prototype = {
//////////////////////////////////////////////////////////////////////////////
//// nsINavBookmarkObserver
onBeginUpdateBatch: function () { },
onEndUpdateBatch: function () { },
onItemAdded: function bCL_onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGUID, aParentGUID) {
if (!this._view.inCurrentView(aParentId, aIndex, aItemType))
return;
this._view.addBookmark(aItemId);
},
onItemChanged: function bCL_onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, aLastModified, aItemType, aParentId, aGUID, aParentGUID) {
let itemIndex = PlacesUtils.bookmarks.getItemIndex(aItemId);
if (!this._view.inCurrentView(aParentId, itemIndex, aItemType))
return;
this._view.updateBookmark(aItemId);
},
onItemMoved: function bCL_onItemMoved(aItemId, aOldParentId, aOldIndex, aNewParentId, aNewIndex, aItemType, aGUID, aOldParentGUID, aNewParentGUID) {
let wasInView = this._view.inCurrentView(aOldParentId, aOldIndex, aItemType);
let nowInView = this._view.inCurrentView(aNewParentId, aNewIndex, aItemType);
if (!wasInView && nowInView)
this._view.addBookmark(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded);
if (wasInView && !nowInView)
this._view.removeBookmark(aItemId);
},
onBeforeItemRemoved: function (aItemId, aItemType, aParentId, aGUID, aParentGUID) { },
onItemRemoved: function bCL_onItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, aParentGUID) {
if (!this._view.inCurrentView(aParentId, aIndex, aItemType))
return;
this._view.removeBookmark(aItemId);
},
onItemVisited: function(aItemId, aVisitId, aTime, aTransitionType, aURI, aParentId, aGUID, aParentGUID) { },
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};

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

@ -0,0 +1,170 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
/*
* JS modules
*/
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
"resource://pdf.js/PdfJs.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
"resource://gre/modules/DownloadUtils.jsm");
/*
* Services
*/
#ifdef XP_WIN
XPCOMUtils.defineLazyServiceGetter(this, "MetroUtils",
"@mozilla.org/windows-metroutils;1",
"nsIWinMetroUtils");
#else
// Stub nsIWinMetroUtils implementation for testing on non-Windows platforms:
var MetroUtils = {
snappedState: Ci.nsIWinMetroUtils.fullScreenLandscape,
immersive: false,
handPreference: Ci.nsIWinMetroUtils.handPreferenceLeft,
unsnap: function() {},
launchInDesktop: function() {},
pinTileAsync: function() {},
unpinTileAsync: function() {},
isTilePinned: function() { return false; },
keyboardVisible: false,
keyboardX: 0,
keyboardY: 0,
keyboardWidth: 0,
keyboardHeight: 0
};
#endif
XPCOMUtils.defineLazyServiceGetter(this, "StyleSheetSvc",
"@mozilla.org/content/style-sheet-service;1",
"nsIStyleSheetService");
XPCOMUtils.defineLazyServiceGetter(window, "gHistSvc",
"@mozilla.org/browser/nav-history-service;1",
"nsINavHistoryService",
"nsIBrowserHistory");
XPCOMUtils.defineLazyServiceGetter(window, "gURIFixup",
"@mozilla.org/docshell/urifixup;1",
"nsIURIFixup");
XPCOMUtils.defineLazyServiceGetter(window, "gFaviconService",
"@mozilla.org/browser/favicon-service;1",
"nsIFaviconService");
XPCOMUtils.defineLazyServiceGetter(window, "gFocusManager",
"@mozilla.org/focus-manager;1",
"nsIFocusManager");
#ifdef MOZ_CRASHREPORTER
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
"@mozilla.org/xre/app-info;1",
"nsICrashReporter");
#endif
/*
* window.Rect is used by
* http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect
* so it is not possible to set a lazy getter for Geometry.jsm.
*/
Cu.import("resource://gre/modules/Geometry.jsm");
/*
* Browser scripts
*/
[
["WebProgress", "chrome://browser/content/WebProgress.js"],
["FindHelperUI", "chrome://browser/content/helperui/FindHelperUI.js"],
["FormHelperUI", "chrome://browser/content/helperui/FormHelperUI.js"],
["BrowserTouchHandler", "chrome://browser/content/BrowserTouchHandler.js"],
["AlertsHelper", "chrome://browser/content/helperui/AlertsHelper.js"],
["CapturePickerUI", "chrome://browser/content/helperui/CapturePickerUI.js"],
["CharsetMenu", "chrome://browser/content/helperui/CharsetMenu.js"],
["AutofillMenuUI", "chrome://browser/content/helperui/MenuUI.js"],
["ContextMenuUI", "chrome://browser/content/helperui/MenuUI.js"],
["MenuControlUI", "chrome://browser/content/helperui/MenuUI.js"],
["MenuPopup", "chrome://browser/content/helperui/MenuUI.js"],
["IdentityUI", "chrome://browser/content/helperui/IdentityUI.js"],
["IndexedDB", "chrome://browser/content/helperui/IndexedDB.js"],
["MasterPasswordUI", "chrome://browser/content/helperui/MasterPasswordUI.js"],
["OfflineApps", "chrome://browser/content/helperui/OfflineApps.js"],
["SelectHelperUI", "chrome://browser/content/helperui/SelectHelperUI.js"],
["SelectionHelperUI", "chrome://browser/content/helperui/SelectionHelperUI.js"],
["SharingUI", "chrome://browser/content/helperui/SharingUI.js"],
["FullScreenVideo", "chrome://browser/content/video.js"],
["AnimatedZoom", "chrome://browser/content/AnimatedZoom.js"],
["CommandUpdater", "chrome://browser/content/commandUtil.js"],
["ContextCommands", "chrome://browser/content/ContextCommands.js"],
["Bookmarks", "chrome://browser/content/bookmarks.js"],
["Downloads", "chrome://browser/content/downloads.js"],
["BookmarksPanelView", "chrome://browser/content/bookmarks.js"],
["ConsolePanelView", "chrome://browser/content/console.js"],
["DownloadsPanelView", "chrome://browser/content/downloads.js"],
["DownloadsView", "chrome://browser/content/downloads.js"],
["Downloads", "chrome://browser/content/downloads.js"],
["PreferencesPanelView", "chrome://browser/content/preferences.js"],
["BookmarksStartView", "chrome://browser/content/bookmarks.js"],
["HistoryView", "chrome://browser/content/history.js"],
["HistoryStartView", "chrome://browser/content/history.js"],
["HistoryPanelView", "chrome://browser/content/history.js"],
["TopSitesView", "chrome://browser/content/TopSites.js"],
["TopSitesSnappedView", "chrome://browser/content/TopSites.js"],
["TopSitesStartView", "chrome://browser/content/TopSites.js"],
["InputSourceHelper", "chrome://browser/content/input.js"],
["PageActions", "chrome://browser/content/PageActions.js"],
["Sanitizer", "chrome://browser/content/sanitize.js"],
["SSLExceptions", "chrome://browser/content/exceptions.js"],
#ifdef MOZ_SERVICES_SYNC
["WeaveGlue", "chrome://browser/content/sync.js"],
["SyncPairDevice", "chrome://browser/content/sync.js"],
["RemoteTabsView", "chrome://browser/content/RemoteTabs.js"],
["RemoteTabsPanelView", "chrome://browser/content/RemoteTabs.js"],
["RemoteTabsStartView", "chrome://browser/content/RemoteTabs.js"],
#endif
].forEach(function (aScript) {
let [name, script] = aScript;
XPCOMUtils.defineLazyGetter(window, name, function() {
let sandbox = {};
Services.scriptloader.loadSubScript(script, sandbox);
return sandbox[name];
});
});
#ifdef MOZ_SERVICES_SYNC
XPCOMUtils.defineLazyGetter(this, "Weave", function() {
Components.utils.import("resource://services-sync/main.js");
return Weave;
});
#endif
/*
* Delay load some global scripts using a custom namespace
*/
XPCOMUtils.defineLazyGetter(this, "GlobalOverlay", function() {
let GlobalOverlay = {};
Services.scriptloader.loadSubScript("chrome://global/content/globalOverlay.js", GlobalOverlay);
return GlobalOverlay;
});
XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
let ContentAreaUtils = {};
Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
return ContentAreaUtils;
});
XPCOMUtils.defineLazyGetter(this, "ZoomManager", function() {
let sandbox = {};
Services.scriptloader.loadSubScript("chrome://global/content/viewZoomOverlay.js", sandbox);
return sandbox.ZoomManager;
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,221 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
browser[remote="false"] {
-moz-binding: url("chrome://browser/content/bindings/browser.xml#local-browser");
}
browser[remote="true"] {
-moz-binding: url("chrome://browser/content/bindings/browser.xml#remote-browser");
}
#content-navigator {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#content-navigator");
}
#tabs {
-moz-binding: url("chrome://browser/content/bindings/tabs.xml#tablist");
}
documenttab {
-moz-binding: url("chrome://browser/content/bindings/tabs.xml#documenttab");
}
appbar {
-moz-binding: url('chrome://browser/content/bindings/appbar.xml#appbarBinding');
}
flyoutpanel {
-moz-binding: url('chrome://browser/content/bindings/flyoutpanel.xml#flyoutpanelBinding');
}
settings {
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings");
}
setting {
display: none;
}
setting[type="bool"] {
display: -moz-box;
-moz-binding: url("chrome://browser/content/bindings/toggleswitch.xml#setting-fulltoggle-bool");
}
setting[type="bool"][localized="true"] {
display: -moz-box;
-moz-binding: url("chrome://browser/content/bindings/toggleswitch.xml#setting-fulltoggle-localized-bool");
}
setting[type="boolint"] {
display: -moz-box;
-moz-binding: url("chrome://browser/content/bindings/toggleswitch.xml#setting-fulltoggle-boolint");
}
setting[type="integer"] {
display: -moz-box;
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer");
}
setting[type="control"] {
display: -moz-box;
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control");
}
setting[type="string"] {
display: -moz-box;
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string");
}
setting[type="color"] {
display: -moz-box;
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-color");
}
setting[type="file"],
setting[type="directory"] {
display: -moz-box;
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-path");
}
setting[type="radio"],
setting[type="menulist"] {
display: -moz-box;
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-multi");
}
#selection-overlay {
-moz-binding: url("chrome://browser/content/bindings/selectionoverlay.xml#selection-binding");
}
#urlbar-edit {
-moz-binding: url("chrome://browser/content/bindings/autocomplete.xml#autocomplete");
}
#start-autocomplete {
-moz-binding: url("chrome://browser/content/bindings/autocomplete.xml#autocomplete-popup");
}
richgrid {
-moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid");
}
richgriditem {
-moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid-item");
}
placeitem {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#place-item");
background-color: transparent;
}
placeitem[type="folder"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#place-folder");
}
placelabel {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#place-label");
}
radio {
-moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
}
checkbox.toggleswitch {
-moz-binding: url("chrome://browser/content/bindings/toggleswitch.xml#checkbox-toggleswitch");
}
menulist {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#menulist");
}
.chrome-select-option {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#chrome-select-option");
}
/* richlist defaults ------------------------------------------------------- */
richlistbox[batch] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#richlistbox-batch");
}
richlistbox[bindingType="contextmenu"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#richlistbox-contextmenu");
}
richlistitem {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#richlistitem");
}
richgriditem[typeName="download"][state="-1"] {
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-not-started");
}
richgriditem[typeName="download"] {
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-downloading");
}
richgriditem[typeName="download"][state="1"] {
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-done");
}
richgriditem[typeName="download"][state="2"],
richgriditem[typeName="download"][state="3"] {
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-retry");
}
richgriditem[typeName="download"][state="4"] {
-moz-binding: url("chrome://browser/content/bindings/downloads.xml#download-paused");
}
richlistitem[type="error"],
richlistitem[type="warning"] {
-moz-binding: url("chrome://browser/content/bindings/console.xml#error");
}
richlistitem[type="message"]{
-moz-binding: url("chrome://browser/content/bindings/console.xml#message");
}
dialog {
-moz-binding: url("chrome://browser/content/bindings/dialog.xml#dialog");
}
/* Do not allow these to inherit from the toolkit binding */
dialog.content-dialog {
-moz-binding: none;
}
pageaction {
-moz-binding: url("chrome://browser/content/bindings/pageaction.xml#pageaction");
}
arrowbox {
-moz-binding: url("chrome://browser/content/bindings/arrowbox.xml#arrowbox");
}
/* Disable context menus in textboxes */
.textbox-input-box,
.textbox-input-box[spellcheck="true"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#input-box");
}
textbox {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#textbox");
}
textbox[multiline="true"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#textarea");
}
textbox[type="timed"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#timed-textbox");
}
textbox[type="search"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#search-textbox");
}
textbox[type="number"] {
-moz-binding: url("chrome://browser/content/bindings/bindings.xml#numberbox");
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,691 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/forms.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % prefsDTD SYSTEM "chrome://browser/locale/preferences.dtd">
%prefsDTD;
#ifdef MOZ_SERVICES_SYNC
<!ENTITY % syncDTD SYSTEM "chrome://browser/locale/sync.dtd">
%syncDTD;
#endif
]>
<window id="main-window"
onload="Browser.startup();"
onunload="Browser.shutdown();"
onclose="return Browser.closing();"
windowtype="navigator:browser"
chromedir="&locale.dir;"
title="&brandShortName;"
width="1366"
height="768"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-scripts.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-ui.js"/>
<script type="application/javascript" src="chrome://browser/content/Util.js"/>
<script type="application/javascript" src="chrome://browser/content/input.js"/>
<script type="application/javascript;version=1.8" src="chrome://browser/content/appbar.js"/>
<broadcasterset id="broadcasterset">
<broadcaster id="bcast_contentShowing" disabled="false"/>
<broadcaster id="bcast_urlbarState" mode="view"/>
<broadcaster id="bcast_preciseInput" input="imprecise"/>
<broadcaster id="bcast_windowState" viewstate=""/>
</broadcasterset>
<observerset id="observerset">
<observes id="observe_contentShowing" element="bcast_contentShowing" attribute="disabled" onbroadcast="BrowserUI.updateUIFocus();"/>
</observerset>
<commandset id="mainCommandSet">
<!-- basic navigation -->
<command id="cmd_back" label="&back.label;" disabled="true" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_forward" label="&forward.label;" disabled="true" oncommand="CommandUpdater.doCommand(this.id);" observes="bcast_urlbarState"/>
<command id="cmd_handleBackspace" oncommand="BrowserUI.handleBackspace();" />
<command id="cmd_handleShiftBackspace" oncommand="BrowserUI.handleShiftBackspace();" />
<command id="cmd_reload" label="&reload.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_forceReload" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_stop" label="&stop.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_go" label="&go.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_openLocation" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_home" oncommand="CommandUpdater.doCommand(this.id);"/>
<!-- tabs -->
<command id="cmd_newTab" label="&newtab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_closeTab" label="&closetab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_undoCloseTab" label="&undoclosetab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
#ifdef MOZ_SERVICES_SYNC
<command id="cmd_remoteTabs" oncommand="CommandUpdater.doCommand(this.id);"/>
#endif
<!-- misc -->
<command id="cmd_close" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_quit" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_actions" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_panel" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_flyout_back" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_addBookmark" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_bookmarks" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_history" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_sanitize" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_contextUI" oncommand="CommandUpdater.doCommand(this.id);"/>
<!-- screen/display -->
<command id="cmd_zoomin" observes="bcast_contentShowing" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_zoomout" observes="bcast_contentShowing" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_lockscreen" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_volumeLeft" observes="bcast_contentShowing" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_volumeRight" observes="bcast_contentShowing" oncommand="CommandUpdater.doCommand(this.id);"/>
<!-- scrolling -->
<command id="cmd_scrollPageUp" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_scrollPageDown" oncommand="CommandUpdater.doCommand(this.id);"/>
<!-- editing -->
<command id="cmd_cut" label="&cut.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_copy" label="&copy.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_copylink" label="&copylink.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_paste" label="&paste.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_delete" label="&delete.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_selectAll" label="&selectAll.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<!-- forms navigation -->
<command id="cmd_formPrevious" oncommand="FormHelperUI.goToPrevious();"/>
<command id="cmd_formNext" oncommand="FormHelperUI.goToNext();"/>
<command id="cmd_formClose" oncommand="FormHelperUI.hide();"/>
<!-- find navigation -->
<command id="cmd_findPrevious" oncommand="FindHelperUI.goToPrevious();"/>
<command id="cmd_findNext" oncommand="FindHelperUI.goToNext();"/>
<command id="cmd_findClose" oncommand="FindHelperUI.hide();"/>
<command id="cmd_find" oncommand="FindHelperUI.show();"/>
</commandset>
<keyset id="mainKeyset">
<!-- basic navigation -->
<key id="key_back" keycode="VK_LEFT" command="cmd_back" modifiers="alt"/>
<key id="key_forward" keycode="VK_RIGHT" command="cmd_forward" modifiers="alt"/>
<key id="key_backspace" keycode="VK_BACK" command="cmd_handleBackspace"/>
<key id="key_shift_backspace" keycode="VK_BACK" command="cmd_handleShiftBackspace" modifiers="shift"/>
<key id="key_reload" keycode="VK_F5" command="cmd_reload"/>
<key id="key_reload2" key="r" modifiers="accel" command="cmd_reload"/>
<key id="key_forceReload" keycode="VK_F5" modifiers="shift" command="cmd_forceReload"/>
<key id="key_forceReload2" key="r" modifiers="accel,shift" command="cmd_forceReload"/>
<key id="key_focusURL" key="l" modifiers="accel" command="cmd_openLocation"/>
<key id="key_focusURL2" key="&urlbar.accesskey;" modifiers="alt" command="cmd_openLocation"/>
<key id="key_home" keycode="VK_HOME" modifiers="accel" command="cmd_home"/>
<!-- misc -->
<key id="key_zoomin" key="+" modifiers="accel" command="cmd_zoomin"/>
<key id="key_zoomout" key="-" modifiers="accel" command="cmd_zoomout"/>
<key id="key_find" key="f" modifiers="accel" command="cmd_find"/>
<key id="key_find" key="/" command="cmd_find"/>
<key id="key_findNext" keycode="VK_F3" command="cmd_findNext"/>
<key id="key_findNext2" key="g" modifiers="accel" command="cmd_findNext"/>
<key id="key_findPrevious" keycode="VK_F3" modifiers="shift" command="cmd_findPrevious"/>
<key id="key_findPrevious2" key="g" modifiers="accel,shift" command="cmd_findPrevious"/>
<key id="key_quit" key="q" modifiers="accel" command="cmd_quit"/>
<key id="key_addBoomkark" key="d" modifiers="accel" command="cmd_addBookmark"/>
<!-- manage tabs -->
<key id="key_newTab" key="t" modifiers="accel" command="cmd_newTab"/>
<key id="key_newTab2" key="n" modifiers="accel" command="cmd_newTab"/>
<key id="key_closeTab" key="w" modifiers="accel" command="cmd_closeTab"/>
<key id="key_closeTab2" keycode="VK_F4" modifiers="accel" command="cmd_closeTab"/>
<key id="key_undoCloseTab" key="t" modifiers="accel,shift" command="cmd_undoCloseTab"/>
<!-- tab selection -->
<key id="key_nextTab" oncommand="BrowserUI.selectNextTab();" keycode="VK_TAB" modifiers="accel"/>
<key id="key_nextTab2" oncommand="BrowserUI.selectNextTab();" keycode="VK_PAGE_DOWN" modifiers="accel"/>
<key id="key_prevTab" oncommand="BrowserUI.selectPreviousTab();" keycode="VK_TAB" modifiers="accel,shift"/>
<key id="key_prevTab2" oncommand="BrowserUI.selectPreviousTab();" keycode="VK_PAGE_UP" modifiers="accel"/>
<key id="key_selectTab1" oncommand="BrowserUI.selectTabAtIndex(0);" key="1" modifiers="accel"/>
<key id="key_selectTab2" oncommand="BrowserUI.selectTabAtIndex(1);" key="2" modifiers="accel"/>
<key id="key_selectTab3" oncommand="BrowserUI.selectTabAtIndex(2);" key="3" modifiers="accel"/>
<key id="key_selectTab4" oncommand="BrowserUI.selectTabAtIndex(3);" key="4" modifiers="accel"/>
<key id="key_selectTab5" oncommand="BrowserUI.selectTabAtIndex(4);" key="5" modifiers="accel"/>
<key id="key_selectTab6" oncommand="BrowserUI.selectTabAtIndex(5);" key="6" modifiers="accel"/>
<key id="key_selectTab7" oncommand="BrowserUI.selectTabAtIndex(6);" key="7" modifiers="accel"/>
<key id="key_selectTab8" oncommand="BrowserUI.selectTabAtIndex(7);" key="8" modifiers="accel"/>
<key id="key_selectLastTab" oncommand="BrowserUI.selectTabAtIndex(-1);" key="9" modifiers="accel"/>
</keyset>
<stack id="stack" flex="1">
<!-- Page Area -->
<vbox id="page">
<vbox id="tray" class="tray-toolbar" visible="true" observes="bcast_windowState" >
<!-- Tabs -->
<hbox id="tabs-container">
<box id="tabs" flex="1"
observes="bcast_preciseInput"
onselect="BrowserUI.selectTabAndDismiss(this);"
onclosetab="BrowserUI.closeTab(this);"/>
<vbox id="tabs-controls">
<toolbarbutton id="newtab-button" command="cmd_newTab" label="&newtab.label;"/>
</vbox>
</hbox>
<!-- Main Toolbar -->
<hbox id="toolbar-container" observes="bcast_windowState" >
<toolbar id="toolbar" flex="1">
<observes element="bcast_windowState" attribute="*"/>
<observes element="bcast_urlbarState" attribute="*"/>
<hbox id="unified-back-forward-button" class="chromeclass-toolbar-additional"
observes="bcast_windowState"
context="backForwardMenu" removable="true"
forwarddisabled="true"
title="Back/Forward">
<toolbarbutton id="back-button" class="toolbarbutton"
label="Back"
command="cmd_back"/>
<toolbarbutton id="forward-button" class="toolbarbutton"
label="Forward"
command="cmd_forward"/>
<dummyobservertarget hidden="true"
onbroadcast="if (this.getAttribute('disabled') == 'true')
this.parentNode.setAttribute('forwarddisabled', 'true');
else
this.parentNode.removeAttribute('forwarddisabled');">
<observes element="cmd_forward" attribute="disabled"/>
</dummyobservertarget>
</hbox>
<hbox id="urlbar-container" flex="1" observes="bcast_urlbarState">
<hbox id="urlbar" flex="1">
<box id="identity-box" role="button"
onclick="IdentityUI.handleIdentityButtonEvent();"
onkeypress="IdentityUI.handleIdentityButtonEvent();">
<hbox id="identity-box-inner" align="center" mousethrough="always">
<image id="identity-icon"/>
<hbox id="identity-icon-labels">
<label id="identity-icon-label" class="plain" flex="1"/>
<label id="identity-icon-country-label" class="plain"/>
</hbox>
</hbox>
</box>
<textbox id="urlbar-edit"
type="url"
class="uri-element"
autocompletesearch="history"
autocompletepopup="start-autocomplete"
completeselectedindex="true"
placeholder="&urlbar.emptytext;"
flex="1"
ontextentered="BrowserUI.handleUrlbarEnter(param);"
onkeydown="BrowserUI.navEditKeyPress();"
onclick="BrowserUI._urlbarClicked(event);"
onblur="BrowserUI._urlbarBlurred();"/>
</hbox>
</hbox>
<hbox id="urlbar-icons" observes="bcast_urlbarState">
<toolbarbutton id="tool-reload" oncommand="CommandUpdater.doCommand(event.shiftKey ? 'cmd_forceReload' : 'cmd_reload');"/>
<toolbarbutton id="tool-stop" command="cmd_stop"/>
</hbox>
</toolbar>
<box id="toolbar-transition" observes="bcast_windowState" >
<toolbarbutton id="tool-new-tab" command="cmd_newTab"/>
</box>
</hbox>
<hbox id="progress-control" layer="true"></hbox>
<!-- Start UI -->
<hbox id="start-container" flex="1" observes="bcast_windowState" class="meta content-height content-width" onclick="false;">
<!-- portrait/landscape/filled view -->
<hbox id="start" class="start-page" flex="1" observes="bcast_windowState">
<scrollbox id="start-scrollbox" orient="horizontal" flex="1">
<vbox id="start-topsites" class="meta-section">
<label class="meta-section-title" value="&startTopSitesHeader.label;"/>
<richgrid id="start-topsites-grid" seltype="single" flex="1"/>
</vbox>
<vbox id="start-bookmarks" class="meta-section">
<label class="meta-section-title" value="&startBookmarksHeader.label;"
onclick="PanelUI.show('bookmarks-container');"/>
<richgrid id="start-bookmarks-grid" seltype="single" flex="1"/>
</vbox>
<vbox id="start-history" class="meta-section">
<label class="meta-section-title" value="&startHistoryHeader.label;"/>
<richgrid id="start-history-grid" seltype="single" flex="1"/>
</vbox>
<vbox id="start-remotetabs" class="meta-section">
<label class="meta-section-title" value="&startRemoteTabsHeader.label;"
onclick="PanelUI.show('remotetabs-container');"/>
<richgrid id="start-remotetabs-grid" seltype="single" flex="1"/>
</vbox>
</scrollbox>
</hbox>
<!-- snapped view -->
<vbox id="snapped-start" class="start-page" observes="bcast_windowState">
<scrollbox id="snapped-scrollbox" orient="vertical" flex="1">
<vbox id="snapped-topsites">
<label class="meta-section-title" value="&startTopSitesHeader.label;"/>
<!-- TODO bug 835999 -->
</vbox>
<label class="meta-section-title" value="&startBookmarksHeader.label;"
onclick="PanelUI.show('bookmarks-container');"/>
<label class="meta-section-title" value="&startHistoryHeader.label;"
onclick="PanelUI.show('history-container');" inputProcessing="true"/>
<label class="meta-section-title" value="&startRemoteTabsHeader.label;"
onclick="PanelUI.show('remotetabs-container');" inputProcessing="true"/>
</scrollbox>
</vbox>
<!-- Autocompletion interface -->
<box id="start-autocomplete"/>
</hbox>
</vbox> <!-- end tray -->
<!-- Content viewport -->
<stack id="content-viewport">
<deck id="browsers" flex="1"/>
<box id="vertical-scroller" class="scroller" orient="vertical" end="0" top="0"/>
<box id="horizontal-scroller" class="scroller" orient="horizontal" left="0" bottom="0"/>
</stack>
</vbox>
<!-- popup for content navigator helper -->
<vbox id="content-navigator" top="0">
<textbox id="find-helper-textbox" class="search-bar content-navigator-item" oncommand="FindHelperUI.search(this.value)" oninput="FindHelperUI.updateCommands(this.value);" type="search"/>
</vbox>
<!-- Windows 8 Appbar -->
<appbar id="appbar" mousethrough="never" observes="bcast_windowState">
<!-- contextual actions temporarily hidden, pending #800996, #831918 -->
<hbox id="contextualactions-tray" flex="1" hidden="true">
<toolbarbutton id="delete-selected-button" hidden="true" oncommand="Appbar.dispatchContextualAction('delete')"/>
<toolbarbutton id="restore-selected-button" hidden="true" oncommand="Appbar.dispatchContextualAction('restore')"/>
<toolbarbutton id="pin-selected-button" hidden="true" oncommand="Appbar.dispatchContextualAction('pin')"/>
<toolbarbutton id="unpin-selected-button" hidden="true" oncommand="Appbar.dispatchContextualAction('unpin')"/>
</hbox>
<hbox flex="1">
<toolbarbutton id="download-button" oncommand="Appbar.onDownloadButton()"/>
<toolbarbutton id="console-button" oncommand="Appbar.onConsoleButton()"/>
<toolbarbutton id="jsshell-button" oncommand="Appbar.onJSShellButton()"/>
</hbox>
<hbox>
<toolbarbutton id="more-button" onclick="Appbar.onMoreButton(event)" />
<toolbarbutton id="zoomout-button" oncommand="Appbar.onZoomOutButton()"/>
<toolbarbutton id="zoomin-button" oncommand="Appbar.onZoomInButton()"/>
<toolbarbutton id="star-button" type="checkbox" oncommand="Appbar.onStarButton()"/>
<toolbarbutton id="pin-button" type="checkbox" oncommand="Appbar.onPinButton()"/>
</hbox>
</appbar>
<!-- popup for site identity information -->
<arrowbox id="identity-container" hidden="true" mode="unknownIdentity" offset="18" flex="1" type="dialog" observes="bcast_urlbarState">
<box id="identity-popup-container" flex="1" align="top">
<image id="identity-popup-icon"/>
<vbox id="identity-popup-content-box" flex="1">
<box id="identity-popup-connected-box" flex="1">
<label id="identity-popup-connectedToLabel" value="&identity.connectedTo2;"/>
<label id="identity-popup-connectedToLabel2" flex="1">&identity.unverifiedsite2;</label>
<description id="identity-popup-content-host" flex="1"/>
</box>
<box id="identity-popup-runBy-box">
<label id="identity-popup-runByLabel" value="&identity.runBy2;"/>
<description id="identity-popup-content-owner"/>
<description id="identity-popup-content-supplemental"/>
</box>
<description id="identity-popup-content-verifier"/>
</vbox>
<box id="identity-popup-encryption-box">
<image id="identity-popup-encryption-icon"/>
<description id="identity-popup-encryption-label"/>
</box>
</box>
<hbox id="pageactions-container" hidden="true">
<pageaction id="pageaction-findinpage" title="&pageactions.findInPage;"
onclick="FindHelperUI.show();"/>
<pageaction id="pageaction-share" title="&pageactions.share.page;"
onclick="SharingUI.show(getBrowser().currentURI.spec, getBrowser().contentTitle);"/>
<pageaction id="pageaction-pinsite" title="&pageactions.pin.site;"
onclick="PageActions.pinSite();"/>
<pageaction id="pageaction-password" title="&pageactions.password.forget;"
onclick="PageActions.forgetPassword(event);"/>
<pageaction id="pageaction-reset" title="&pageactions.reset;"
onclick="PageActions.clearPagePermissions(event);"/>
<pageaction id="pageaction-search" title="&pageactions.search.addNew;"/>
<pageaction id="pageaction-charset" title="&pageactions.charEncoding;" onclick="CharsetMenu.show();"/>
</hbox>
</arrowbox>
<vbox id="panel-container" hidden="true" class="window-width window-height meta">
<hbox id="panel-header">
<toolbarbutton id="panel-close-button" command="cmd_panel"/>
<menulist id="panel-view-switcher" oncommand="PanelUI.switchPane(this.value);">
<menupopup>
<menuitem label="&bookmarksHeader.label;" value="bookmarks-container" id="menuitem-bookmarks"/>
<menuitem label="&startHistoryHeader.label;" value="history-container" id="menuitem-history"/>
<menuitem label="&startRemoteTabsHeader.label;" value="remotetabs-container" id="menuitem-remotetabs"/>
<menuitem label="&downloadsHeader.label;" value="downloads-container" id="menuitem-downloads"/>
<menuitem label="&consoleHeader.label;" value="console-container" id="menuitem-console"/>
</menupopup>
</menulist>
</hbox>
<deck id="panel-items" selectedIndex="0" flex="1">
<scrollbox id="bookmarks-container" flex="1">
<richgrid id="bookmarks-list" seltype="single" flex="1"/>
</scrollbox>
<scrollbox id="history-container" flex="1">
<richgrid id="history-list" seltype="single" flex="1"/>
</scrollbox>
<scrollbox id="downloads-container" flex="1">
<richgrid id="downloads-list" seltype="single" flex="1"/>
</scrollbox>
<scrollbox id="remotetabs-container" flex="1">
<richgrid id="remotetabs-list" seltype="single" flex="1"/>
</scrollbox>
<vbox id="console-container" flex="1">
<vbox id="console-header" class="panel-list">
<label class="panel-header" value="&consoleHeader.label;"/>
<hbox align="center">
<label value="&consoleCodeEval.label;" control="console-eval-textbox"/>
<textbox id="console-eval-textbox" class="toolbar search-bar" value="" onkeypress="ConsolePanelView.onEvalKeyPress(event)" flex="1"/>
<button id="console-button-eval" class="show-text" label="&consoleEvaluate.label;" oncommand="ConsolePanelView.evaluateTypein()"/>
</hbox>
<hbox align="center" pack="end">
<radiogroup id="console-filter" oncommand="ConsolePanelView.changeMode();">
<radio id="console-filter-all" label="&consoleAll.label;" value="all" selected="true"/>
<radio id="console-filter-messages" label="&consoleMessages.label;" value="message"/>
<radio id="console-filter-warnings" label="&consoleWarnings.label;" value="warning"/>
<radio id="console-filter-errors" label="&consoleErrors.label;" value="error"/>
</radiogroup>
<button id="console-clear" class="show-text" oncommand="ConsolePanelView.clearConsole();" label="&consoleClear.label;"/>
</hbox>
</vbox>
<richlistbox id="console-box" class="panel-list console-box" flex="1" onkeypress="ConsolePanelView.onConsoleBoxKeyPress(event)" oncontextmenu="ConsolePanelView.onContextMenu(event);"/>
</vbox>
</deck>
</vbox>
<flyoutpanel id="about-flyoutpanel" headertext="&aboutHeader.label;">
<label id="about-product-label" value="&aboutHeaderProduct.label;"/>
<label value="&aboutHeaderCompany.label;"/>
#expand <label id="about-version-label">__MOZ_APP_VERSION__</label>
<label id="about-policy-label"
onclick="if (event.button == 0) { Browser.onAboutPolicyClick(); }"
class="text-link" value="&aboutHeaderPolicy.label;"/>
</flyoutpanel>
<flyoutpanel id="prefs-flyoutpanel" headertext="&optionsHeader.label;">
<settings id="prefs-startup" label="&startup.title;">
<setting id="prefs-homepage" title="&homepage.title;" type="menulist" pref="browser.startup.sessionRestore" class="setting-expanded">
<menulist id="prefs-homepage-options">
<menupopup position="after_end">
<menuitem id="prefs-homepage-default" label="&homepage.startPage;" value="false"/>
<menuitem id="prefs-homepage-session" label="&homepage.sessionRestore;" value="true"/>
</menupopup>
</menulist>
</setting>
</settings>
<setting pref="browser.tabs.tabsOnly" title="&toggleTabsOnly.label;" type="bool"/>
<settings id="prefs-sync" label="&sync.title;">
<setting id="sync-connect" title="&sync.notconnected;" type="control">
<button label="&sync.connect;" oncommand="WeaveGlue.tryConnect();" />
</setting>
<setting id="sync-connected" class="setting-group" title="&sync.connected;" type="control" collapsed="true">
<button id="sync-pairdevice" label="&sync.pair.title;" oncommand="SyncPairDevice.open();" />
<button id="sync-details" label="&sync.details;" type="checkbox" autocheck="false" checked="false" oncommand="WeaveGlue.showDetails();" />
</setting>
<setting id="sync-sync" class="setting-subgroup" type="control" collapsed="true">
<button id="sync-syncButton" label="&sync.syncNow;" oncommand="WeaveGlue.sync();"/>
</setting>
<setting id="sync-device" class="setting-subgroup" type="string" title="&sync.deviceName;" onchange="WeaveGlue.changeName(this);" collapsed="true"/>
<setting id="sync-disconnect" class="setting-subgroup" type="control" collapsed="true">
<button label="&sync.disconnect;" oncommand="WeaveGlue.disconnect();" />
</setting>
</settings>
<settings id="prefs-privacy" label="&privacy.title;">
<setting pref="signon.rememberSignons" title="&rememberPasswords.title;" type="bool"/>
<setting pref="privacy.donottrackheader.enabled" title="&doNotTrack.title;" type="bool"/>
<setting id="prefs-master-password" title="&masterPassword.title;" type="bool" oncommand="MasterPasswordUI.show(this.value);"/>
<setting title="&clearPrivateData2.title;" type="control">
<button id="prefs-clear-data" label="&clearPrivateData.button;" command="cmd_sanitize"/>
</setting>
</settings>
</flyoutpanel>
<!-- Form Helper form validation helper popup -->
<arrowbox id="form-helper-validation-container" class="arrowbox-dark" flex="1" hidden="true" offset="0" top="0" left="0">
<label/>
</arrowbox>
#ifdef MOZ_SERVICES_SYNC
<box id="syncsetup-container" class="perm-modal-block" hidden="true">
<dialog id="syncsetup-dialog" class="content-dialog" flex="1">
<vbox class="prompt-inner">
<hbox class="prompt-title">
<description>&sync.setup.title;</description>
</hbox>
<vbox id="syncsetup-simple" class="syncsetup-page" flex="1">
<scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
<description class="syncsetup-desc" flex="1">&sync.setup.pair;</description>
<description class="link" flex="1" onclick="WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
<separator/>
<vbox flex="1" pack="center" align="start">
<description id="syncsetup-code1" class="syncsetup-code">....</description>
<description id="syncsetup-code2" class="syncsetup-code">....</description>
<description id="syncsetup-code3" class="syncsetup-code">....</description>
</vbox>
<separator/>
<description class="link" flex="1" onclick="WeaveGlue.openManual();">&sync.fallback;</description>
<separator flex="1"/>
</scrollbox>
<hbox class="prompt-buttons">
<button class="prompt-button" oncommand="WeaveGlue.close();">&sync.setup.cancel;</button>
</hbox>
</vbox>
<vbox id="syncsetup-waiting" class="syncsetup-page" flex="1" hidden="true">
<progressmeter id="syncsetup-progressbar" mode="undetermined"/>
<vbox id="syncsetup-waiting-top" align="center" flex="1">
<description id="syncsetup-waiting-desc" class="syncsetup-desc" flex="1">&sync.setup.waiting2;</description>
<description id="syncsetup-waitingdownload-desc" class="syncsetup-desc" hidden="true" flex="1">&sync.setup.waitingdownload;</description>
</vbox>
<hbox class="prompt-buttons" pack="center" align="end">
<button id="syncsetup-waiting-cancel" class="prompt-button" oncommand="WeaveGlue.close();">&sync.setup.cancel;</button>
<button id="syncsetup-waiting-close" class="prompt-button" hidden="true" oncommand="WeaveGlue.close();">&sync.setup.close;</button>
</hbox>
</vbox>
<vbox id="syncsetup-fallback" class="syncsetup-page" flex="1" hidden="true">
<scrollbox class="prompt-message" orient="vertical" flex="1">
<description class="syncsetup-desc" flex="1">&sync.setup.manual;</description>
<separator/>
<textbox id="syncsetup-account" class="prompt-edit" placeholder="&sync.account;" oninput="WeaveGlue.canConnect();"/>
<textbox id="syncsetup-password" class="prompt-edit" placeholder="&sync.password;" type="password" oninput="WeaveGlue.canConnect();"/>
<textbox id="syncsetup-synckey" class="prompt-edit" placeholder="&sync.recoveryKey;" oninput="WeaveGlue.canConnect();"/>
<separator class="thin"/>
<checkbox id="syncsetup-usecustomserver" label="&sync.customServer;" oncommand="WeaveGlue.toggleCustomServer();"/>
<textbox id="syncsetup-customserver" class="prompt-edit" placeholder="&sync.serverURL;"/>
<separator flex="1"/>
</scrollbox>
<hbox class="prompt-buttons">
<button class="prompt-button" oncommand="WeaveGlue.close();">&sync.setup.cancel;</button>
<separator/>
<button id="syncsetup-button-connect" class="prompt-button" oncommand="WeaveGlue.close(); WeaveGlue.connect();">&sync.setup.connect;</button>
</hbox>
</vbox>
</vbox>
</dialog>
</box>
<box id="syncpair-container" class="perm-modal-block" hidden="true">
<dialog id="syncpair-dialog" class="content-dialog" flex="1">
<vbox class="prompt-inner">
<hbox class="prompt-title">
<description>&sync.pair.title;</description>
</hbox>
<vbox id="syncpair-simple" class="syncsetup-page" flex="1">
<scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
<description class="syncsetup-desc" flex="1">&sync.pair.description;</description>
<description class="link" flex="1" onclick="SyncPairDevice.close(); WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
<separator/>
<vbox align="center" flex="1">
<textbox id="syncpair-code1" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
<textbox id="syncpair-code2" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
<textbox id="syncpair-code3" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
</vbox>
<separator flex="1"/>
</scrollbox>
<hbox class="prompt-buttons" pack="center">
<button class="prompt-button" oncommand="SyncPairDevice.close();">&sync.setup.cancel;</button>
<button id="syncpair-connectbutton" class="prompt-button" disabled="true" oncommand="SyncPairDevice.connect();">&sync.setup.connect;</button>
</hbox>
</vbox>
</vbox>
</dialog>
</box>
#endif
<box id="context-container" class="menu-container" hidden="true">
<vbox id="context-popup" class="menu-popup">
<richlistbox id="context-commands" bindingType="contextmenu" flex="1">
<!-- Text related -->
<richlistitem id="context-copy" type="copy" onclick="ContextCommands.copy();">
<label value="&copy.label;"/>
</richlistitem>
<richlistitem id="context-copy-all" type="copy-all" onclick="ContextCommands.copy();">
<label value="&copyAll.label;"/>
</richlistitem>
<richlistitem id="context-paste" type="paste" onclick="ContextCommands.paste();">
<label value="&paste.label;"/>
</richlistitem>
<richlistitem id="context-paste-n-go" type="paste-url" onclick="ContextCommands.pasteAndGo();">
<label value="&pasteAndGo.label;"/>
</richlistitem>
<richlistitem id="context-select-all" type="select-all" onclick="ContextCommands.selectAll();">
<label value="&selectAll.label;"/>
</richlistitem>
<!-- Image related -->
<richlistitem id="context-viewinnewtab" type="image" onclick="ContextCommands.openInNewTab();">
<label value="&contextViewInNewTab.label;"/>
</richlistitem>
<richlistitem id="context-saveimage" type="image-loaded" onclick="ContextCommands.saveImage();">
<label value="&contextSaveImage.label;"/>
</richlistitem>
<richlistitem id="context-saveimage" type="image-loaded" onclick="ContextCommands.saveImageTo();">
<label value="&contextSaveImageTo.label;"/>
</richlistitem>
<richlistitem id="context-copy-image-location" type="image" onclick="ContextCommands.copyImageLocation();">
<label value="&contextCopyImageLocation.label;"/>
</richlistitem>
<!-- Link related -->
<richlistitem id="context-openinnewtab" type="link-openable" onclick="ContextCommands.openInNewTab();">
<label value="&contextOpenInNewTab.label;"/>
</richlistitem>
<richlistitem id="context-bookmark-link" type="link" onclick="ContextCommands.bookmarkLink();">
<label value="&contextBookmarkLink.label;"/>
</richlistitem>
<richlistitem id="context-copy-link" type="link" onclick="ContextCommands.copyLink();">
<label value="&contextCopyLink.label;"/>
</richlistitem>
<richlistitem id="context-copy-email" type="mailto" onclick="ContextCommands.copyEmail();">
<label value="&contextCopyEmail.label;"/>
</richlistitem>
<richlistitem id="context-copy-phone" type="callto" onclick="ContextCommands.copyPhone();">
<label value="&contextCopyPhone.label;"/>
</richlistitem>
<!-- Video related -->
<richlistitem id="context-play-media" type="media-paused" onclick="ContextCommands.sendCommand('play');">
<label value="&contextPlayMedia.label;"/>
</richlistitem>
<richlistitem id="context-pause-video" type="media-playing" onclick="ContextCommands.sendCommand('pause');">
<label value="&contextPauseMedia.label;"/>
</richlistitem>
<richlistitem id="context-videotab" type="video" onclick="ContextCommands.sendCommand('videotab');">
<label value="&contextVideoTab.label;"/>
</richlistitem>
<richlistitem id="context-save-video" type="video" onclick="ContextCommands.saveVideo();">
<label value="&contextSaveVideo.label;"/>
</richlistitem>
<richlistitem id="context-save-video" type="video" onclick="ContextCommands.saveVideoTo();">
<label value="&contextSaveVideoTo.label;"/>
</richlistitem>
<!-- Misc. related -->
<richlistitem id="context-editbookmark" type="edit-bookmark" onclick="ContextCommands.editBookmark();">
<label value="&contextEditBookmark.label;"/>
</richlistitem>
<richlistitem id="context-removebookmark" type="edit-bookmark" onclick="ContextCommands.removeBookmark();">
<label value="&contextRemoveBookmark.label;"/>
</richlistitem>
<richlistitem id="context-findinpage" type="find-in-page" onclick="ContextCommands.findInPage();">
<label value="&appbarFindInPage.label;"/>
</richlistitem>
<richlistitem id="context-viewondesktop" type="view-on-desktop" onclick="ContextCommands.viewOnDesktop();">
<label value="&appbarViewOnDesktop.label;"/>
</richlistitem>
</richlistbox>
</vbox>
</box>
<vbox id="select-container" class="menu-container" hidden="true">
<vbox id="select-popup" class="select-popup">
<richlistbox id="select-commands" flex="1"/>
</vbox>
</vbox>
<box id="menucontrol-container" class="menu-container" hidden="true">
<vbox id="menucontrol-popup" class="menu-popup">
<richlistbox id="menupopup-commands" onclick="if (event.target != this) MenuControlUI.selectByIndex(this.selectedIndex);" flex="1"/>
</vbox>
</box>
<box id="autofill-container" class="menu-container" hidden="true">
<vbox id="autofill-popup" class="menu-popup">
<richlistbox id="menupopup-commands" onclick="if (event.target != this) AutofillMenuUI.selectByIndex(this.selectedIndex);" flex="1"/>
</vbox>
</box>
<!-- alerts for content -->
<hbox id="alerts-container" hidden="true" align="start" bottom="0" onclick="AlertsHelper.click(event);">
<image id="alerts-image"/>
<vbox flex="1">
<label id="alerts-title" value=""/>
<description id="alerts-text" flex="1"/>
</vbox>
</hbox>
<!-- Selection overlay - this should be below any content that can have selectable text -->
<box class="selection-overlay-hidden" id="selection-overlay"/>
</stack>
<svg:svg height="0">
<svg:clipPath id="forward-button-clip-path" clipPathUnits="objectBoundingBox">
<svg:path d="M 0,0 C 0.15,0.12 0.25,0.3 0.25,0.5 0.25,0.7 0.15,0.88 0,1 L 1,1 1,0 0,0 z"/>
</svg:clipPath>
<svg:clipPath id="back-button-clip-path" clipPathUnits="userSpaceOnUse">
<svg:path d="m -1,-5 0,4.03 C 3.6,1.8 18,21.4 0,40 l 0,27 10000,0 0,-55 L 0,-5 z" />
</svg:clipPath>
</svg:svg>
</window>

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

@ -0,0 +1,164 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Command Updater
*/
var CommandUpdater = {
/**
* Gets a controller that can handle a particular command.
* @param command
* A command to locate a controller for, preferring controllers that
* show the command as enabled.
* @returns In this order of precedence:
* - the first controller supporting the specified command
* associated with the focused element that advertises the
* command as ENABLED
* - the first controller supporting the specified command
* associated with the global window that advertises the
* command as ENABLED
* - the first controller supporting the specified command
* associated with the focused element
* - the first controller supporting the specified command
* associated with the global window
*/
_getControllerForCommand: function(command) {
try {
var controller = top.document.commandDispatcher.getControllerForCommand(command);
if (controller && controller.isCommandEnabled(command))
return controller;
}
catch(e) {
}
var controllerCount = window.controllers.getControllerCount();
for (var i = 0; i < controllerCount; ++i) {
var current = window.controllers.getControllerAt(i);
try {
if (current.supportsCommand(command) && current.isCommandEnabled(command))
return current;
}
catch (e) {
}
}
return controller || window.controllers.getControllerForCommand(command);
},
/**
* Updates the state of a XUL <command> element for the specified command
* depending on its state.
* @param command
* The name of the command to update the XUL <command> element for
*/
updateCommand: function(command) {
var enabled = false;
try {
var controller = this._getControllerForCommand(command);
if (controller) {
enabled = controller.isCommandEnabled(command);
}
}
catch(ex) { }
this.enableCommand(command, enabled);
},
/**
* Updates the state of a XUL <command> element for the specified command
* depending on its state.
* @param command
* The name of the command to update the XUL <command> element for
*/
updateCommands: function(_commands) {
var commands = _commands.split(",");
for (var command in commands) {
this.updateCommand(commands[command]);
}
},
/**
* Enables or disables a XUL <command> element.
* @param command
* The name of the command to enable or disable
* @param enabled
* true if the command should be enabled, false otherwise.
*/
enableCommand: function(command, enabled) {
var element = document.getElementById(command);
if (!element)
return;
if (enabled)
element.removeAttribute("disabled");
else
element.setAttribute("disabled", "true");
},
/**
* Performs the action associated with a specified command using the most
* relevant controller.
* @param command
* The command to perform.
*/
doCommand: function(command) {
var controller = this._getControllerForCommand(command);
if (!controller)
return;
controller.doCommand(command);
},
/**
* Changes the label attribute for the specified command.
* @param command
* The command to update.
* @param labelAttribute
* The label value to use.
*/
setMenuValue: function(command, labelAttribute) {
var commandNode = top.document.getElementById(command);
if (commandNode)
{
var label = commandNode.getAttribute(labelAttribute);
if ( label )
commandNode.setAttribute('label', label);
}
},
/**
* Changes the accesskey attribute for the specified command.
* @param command
* The command to update.
* @param valueAttribute
* The value attribute to use.
*/
setAccessKey: function(command, valueAttribute) {
var commandNode = top.document.getElementById(command);
if (commandNode)
{
var value = commandNode.getAttribute(valueAttribute);
if ( value )
commandNode.setAttribute('accesskey', value);
}
},
/**
* Inform all the controllers attached to a node that an event has occurred
* (e.g. the tree controllers need to be informed of blur events so that they can change some of the
* menu items back to their default values)
* @param node
* The node receiving the event
* @param event
* The event.
*/
onEvent: function(node, event) {
var numControllers = node.controllers.getControllerCount();
var controller;
for ( var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++ )
{
controller = node.controllers.getControllerAt(controllerIndex);
if ( controller )
controller.onEvent(event);
}
}
};

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

@ -0,0 +1,391 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
var ViewConfig = {
get _container() {
delete this._container;
return this._container = document.getElementById("prefs-container");
},
get _editor() {
delete this._editor;
return this._editor = document.getElementById("editor");
},
init: function init() {
window.addEventListener("resize", this, false);
window.addEventListener("prefchange", this, false);
window.addEventListener("prefnew", this, false);
this._handleWindowResize();
this.filter("");
document.getElementById("textbox").focus();
},
uninit: function uninit() {
window.removeEventListener("resize", this, false);
window.removeEventListener("prefchange", this, false);
window.removeEventListener("prefnew", this, false);
},
filter: function filter(aValue) {
let row = document.getElementById("editor-row");
row.setAttribute("hidden", aValue != "");
let container = this._container;
container.scrollBoxObject.scrollTo(0, 0);
// Clear the list by replacing with a shallow copy
let empty = container.cloneNode(false);
empty.appendChild(row);
container.parentNode.replaceChild(empty, container);
this._container = empty;
let result = Utils.getPrefs(aValue);
this._container.setItems(result.map(this._createItem, this));
},
open: function open(aType) {
let buttons = document.getElementById("editor-buttons-add");
buttons.setAttribute("hidden", "true");
let shouldFocus = false;
let setting = document.getElementById("editor-setting");
switch (aType) {
case Ci.nsIPrefBranch.PREF_INT:
setting.setAttribute("type", "integer");
setting.setAttribute("min", -Infinity);
break;
case Ci.nsIPrefBranch.PREF_BOOL:
setting.setAttribute("type", "bool");
break;
case Ci.nsIPrefBranch.PREF_STRING:
setting.setAttribute("type", "string");
break;
}
setting.removeAttribute("title");
setting.removeAttribute("pref");
if (setting.input)
setting.input.value = "";
document.getElementById("editor-container").appendChild(this._editor);
let nameField = document.getElementById("editor-name");
nameField.value = "";
this._editor.setAttribute("hidden", "false");
this._currentItem = null;
nameField.focus();
},
close: function close(aValid) {
this._editor.setAttribute("hidden", "true");
let buttons = document.getElementById("editor-buttons-add");
buttons.setAttribute("hidden", "false");
if (aValid) {
let name = document.getElementById("editor-name").inputField.value;
if (name != "") {
let setting = document.getElementById("editor-setting");
setting.setAttribute("pref", name);
setting.valueToPreference();
}
}
document.getElementById("editor-container").appendChild(this._editor);
},
_currentItem: null,
delayEdit: function(aItem) {
setTimeout(this.edit.bind(this), 0, aItem);
},
edit: function(aItem) {
if (!aItem)
return;
let pref = Utils.getPref(aItem.getAttribute("name"));
if (pref.lock || !pref.name || aItem == this._currentItem)
return;
this.close(false);
this._currentItem = aItem;
let setting = document.getElementById("editor-setting");
let shouldFocus = false;
switch (pref.type) {
case Ci.nsIPrefBranch.PREF_BOOL:
setting.setAttribute("type", "bool");
break;
case Ci.nsIPrefBranch.PREF_INT:
setting.setAttribute("type", "integer");
setting.setAttribute("increment", this.getIncrementForValue(pref.value));
setting.setAttribute("min", -Infinity);
shouldFocus = true;
break;
case Ci.nsIPrefBranch.PREF_STRING:
setting.setAttribute("type", "string");
shouldFocus = true;
break;
}
setting.setAttribute("title", pref.name);
setting.setAttribute("pref", pref.name);
this._container.insertBefore(this._editor, aItem);
let resetButton = document.getElementById("editor-reset");
resetButton.setAttribute("disabled", pref.default);
this._editor.setAttribute("default", pref.default);
this._editor.setAttribute("hidden", "false");
if (shouldFocus && setting.input)
setting.input.focus();
},
reset: function reset(aItem) {
let setting = document.getElementById("editor-setting");
let pref = Utils.getPref(setting.getAttribute("pref"));
if (!pref.default)
Utils.resetPref(pref.name);
},
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "resize":
this._handleWindowResize();
break;
case "prefchange":
case "prefnew":
this._handlePrefChange(aEvent.detail, aEvent.type == "prefnew");
break;
}
},
_handleWindowResize: function _handleWindowResize() {
let mainBox = document.getElementById("main-container");
let textbox = document.getElementById("textbox");
let height = window.innerHeight - textbox.getBoundingClientRect().height;
mainBox.setAttribute("height", height);
},
_handlePrefChange: function _handlePrefChange(aIndex, aNew) {
let isEditing = !this._editor.hidden;
let shouldUpdateEditor = false;
if (isEditing) {
let setting = document.getElementById("editor-setting");
let editorIndex = Utils.getPrefIndex(setting.getAttribute("pref"));
shouldUpdateEditor = (aIndex == editorIndex);
if(shouldUpdateEditor || aIndex > editorIndex)
aIndex += 1;
}
// XXX An item display value will probably fail if a pref is changed in the
// background while there is a filter on the pref
let item = shouldUpdateEditor ? this._editor.nextSibling
: this._container.childNodes[aIndex + 1];// add 1 because of the new pref row
if (!item) // the pref is not viewable
return;
if (aNew) {
let pref = Utils.getPrefByIndex(aIndex);
let row = this._createItem(pref);
this._container.insertBefore(row, item);
return;
}
let pref = Utils.getPref(item.getAttribute("name"));
if (shouldUpdateEditor) {
this._editor.setAttribute("default", pref.default);
let resetButton = document.getElementById("editor-reset");
resetButton.disabled = pref.default;
}
item.setAttribute("default", pref.default);
item.lastChild.setAttribute("value", pref.value);
},
_createItem: function _createItem(aPref) {
let row = document.createElement("richlistitem");
row.setAttribute("name", aPref.name);
row.setAttribute("type", aPref.type);
row.setAttribute("role", "button");
row.setAttribute("default", aPref.default);
let label = document.createElement("label");
label.setAttribute("class", "preferences-title");
label.setAttribute("value", aPref.name);
label.setAttribute("crop", "end");
row.appendChild(label);
label = document.createElement("label");
label.setAttribute("class", "preferences-value");
label.setAttribute("value", aPref.value);
label.setAttribute("crop", "end");
row.appendChild(label);
return row;
},
getIncrementForValue: function getIncrementForValue(aValue) {
let count = 1;
while (aValue >= 100) {
aValue /= 10;
count *= 10;
}
return count;
}
};
var Utils = {
QueryInterface: function(aIID) {
if (!aIID.equals(Ci.nsIObserver) && !aIID.equals(Ci.nsISupportsWeakReference))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
get _branch() {
delete this._branch;
this._branch = Services.prefs.getBranch(null);
this._branch.addObserver("", this, true);
return this._branch;
},
get _preferences() {
delete this._preferences;
let list = this._branch.getChildList("", {}).filter(function(element) {
return !(/^capability\./.test(element));
});
return this._preferences = list.sort().map(this.getPref, this);
},
getPrefs: function getPrefs(aValue) {
let result = this._preferences.slice();;
if (aValue != "") {
let reg = this._generateRegexp(aValue);
if (!reg)
return [];
result = this._preferences.filter(function(element, index, array) {
return reg.test(element.name + ";" + element.value);
});
}
return result;
},
getPref: function getPref(aPrefName) {
let branch = this._branch;
let pref = {
name: aPrefName,
value: "",
default: !branch.prefHasUserValue(aPrefName),
lock: branch.prefIsLocked(aPrefName),
type: branch.getPrefType(aPrefName)
};
try {
switch (pref.type) {
case Ci.nsIPrefBranch.PREF_BOOL:
pref.value = branch.getBoolPref(aPrefName).toString();
break;
case Ci.nsIPrefBranch.PREF_INT:
pref.value = branch.getIntPref(aPrefName).toString();
break;
default:
case Ci.nsIPrefBranch.PREF_STRING:
pref.value = branch.getComplexValue(aPrefName, Ci.nsISupportsString).data;
// Try in case it's a localized string (will throw an exception if not)
if (pref.default && /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value))
pref.value = branch.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
break;
}
} catch (e) {}
return pref;
},
getPrefByIndex: function getPrefByIndex(aIndex) {
return this._preferences[aIndex];
},
getPrefIndex: function getPrefIndex(aPrefName) {
let prefs = this._preferences;
let high = prefs.length - 1;
let low = 0, middle, element;
while (low <= high) {
middle = parseInt((low + high) / 2);
element = prefs[middle];
if (element.name > aPrefName)
high = middle - 1;
else if (element.name < aPrefName)
low = middle + 1;
else
return middle;
}
return -1;
},
resetPref: function resetPref(aPrefName) {
this._branch.clearUserPref(aPrefName);
},
observe: function observe(aSubject, aTopic, aPrefName) {
if (aTopic != "nsPref:changed" || /^capability\./.test(aPrefName)) // avoid displaying "private" preferences
return;
let type = "prefchange";
let index = this.getPrefIndex(aPrefName);
if (index != - 1) {
// update the inner array
let pref = this.getPref(aPrefName);
this._preferences[index].value = pref.value;
}
else {
// XXX we could do better here
let list = this._branch.getChildList("", {}).filter(function(element, index, array) {
return !(/^capability\./.test(element));
});
this._preferences = list.sort().map(this.getPref, this);
type = "prefnew";
index = this.getPrefIndex(aPrefName);
}
let evt = document.createEvent("UIEvents");
evt.initUIEvent(type, true, true, window, index);
window.dispatchEvent(evt);
},
_generateRegexp: function _generateRegexp(aValue) {
if (aValue.charAt(0) == "/") {
try {
let rv = aValue.match(/^\/(.*)\/(i?)$/);
return RegExp(rv[1], rv[2]);
}
catch (e) {
return null; // Do nothing on incomplete or bad RegExp
}
}
return RegExp(aValue.replace(/([^* \w])/g, "\\$1").replace(/^\*+/, "")
.replace(/\*+/g, ".*"), "i");
}
};

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

@ -0,0 +1,65 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/config.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % configDTD SYSTEM "chrome://browser/locale/config.dtd">
%configDTD;
]>
<window id="about:config"
onload="ViewConfig.init();"
onunload="ViewConfig.uninit();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript" src="chrome://browser/content/config.js"/>
<vbox class="panel-dark" flex="1">
<textbox id="textbox"
oncommand="ViewConfig.filter(this.value)"
type="search"
timeout="400"
emptytext="&empty.label;"/>
<hbox id="main-container" class="panel-dark">
<richlistbox id="prefs-container" flex="1" onselect="ViewConfig.delayEdit(this.selectedItem)" batch="25">
<richlistitem id="editor-row">
<vbox id="editor-container" flex="1">
<hbox align="center" flex="1">
<label value="&newpref.label;" flex="1"/>
<spacer flex="1" />
<hbox id="editor-buttons-add">
<button label="&integer.label;" oncommand="ViewConfig.open(Ci.nsIPrefBranch.PREF_INT)"/>
<button label="&boolean.label;" oncommand="ViewConfig.open(Ci.nsIPrefBranch.PREF_BOOL)"/>
<button label="&string.label;" oncommand="ViewConfig.open(Ci.nsIPrefBranch.PREF_STRING)"/>
</hbox>
</hbox>
<vbox id="editor" hidden="true">
<hbox align="center">
<textbox id="editor-name" emptytext="&addpref.name;" flex="1"/>
<setting id="editor-setting" emptytext="&addpref.value;" onlabel="true" offlabel="false" flex="1"/>
</hbox>
<hbox id="editor-buttons">
<button id="editor-cancel" label="&cancel.label;" oncommand="ViewConfig.close(false)"/>
<spacer flex="1"/>
<button id="editor-reset" label="&reset.label;" oncommand="ViewConfig.reset(this.parentNode.parentNode.nextSibling)"/>
<button id="editor-done" label="&done.label;" oncommand="ViewConfig.close(true)"/>
</hbox>
</vbox>
</vbox>
</richlistitem>
</richlistbox>
</hbox>
</vbox>
</window>

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

@ -0,0 +1,298 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let ConsolePanelView = {
_list: null,
_inited: false,
_evalTextbox: null,
_evalFrame: null,
_evalCode: "",
_bundle: null,
_showChromeErrors: -1,
_enabledPref: "devtools.errorconsole.enabled",
init: function cv_init() {
if (this._list)
return;
this._list = document.getElementById("console-box");
this._evalTextbox = document.getElementById("console-eval-textbox");
this._bundle = Strings.browser;
this._count = 0;
this.limit = 250;
try {
// update users using the legacy pref
if (Services.prefs.getBoolPref("browser.console.showInPanel")) {
Services.prefs.setBoolPref(this._enabledPref, true);
Services.prefs.clearUserPref("browser.console.showInPanel");
}
} catch(ex) {
// likely don't have an old pref
}
this.updateVisibility();
Services.prefs.addObserver(this._enabledPref, this, false);
},
show: function show() {
if (this._inited)
return;
this._inited = true;
this.init(); // In case the panel is selected before init has been called.
Services.console.registerListener(this);
this.appendInitialItems();
// Delay creation of the iframe for startup performance
this._evalFrame = document.createElement("iframe");
this._evalFrame.id = "console-evaluator";
this._evalFrame.collapsed = true;
document.getElementById("console-container").appendChild(this._evalFrame);
this._evalFrame.addEventListener("load", this.loadOrDisplayResult.bind(this), true);
},
uninit: function cv_uninit() {
if (this._inited)
Services.console.unregisterListener(this);
Services.prefs.removeObserver(this._enabledPref, this, false);
},
get enabled() {
return Services.prefs.getBoolPref(this._enabledPref);
},
updateVisibility: function ec_updateVisibility(aVal, aPref) {
let button = document.getElementById("menuitem-console");
button.hidden = !this.enabled;
Appbar._updateDebugButtons();
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "nsPref:changed")
this.updateVisibility();
else
this.appendItem(aSubject);
},
showChromeErrors: function() {
if (this._showChromeErrors != -1)
return this._showChromeErrors;
try {
let pref = Services.prefs;
return this._showChromeErrors = pref.getBoolPref("javascript.options.showInConsole");
}
catch(ex) {
return this._showChromeErrors = false;
}
},
appendItem: function cv_appendItem(aObject) {
try {
// Try to QI it to a script error to get more info
let scriptError = aObject.QueryInterface(Ci.nsIScriptError);
// filter chrome urls
if (!this.showChromeErrors && scriptError.sourceName.substr(0, 9) == "chrome://")
return;
this.appendError(scriptError);
}
catch (ex) {
try {
// Try to QI it to a console message
let msg = aObject.QueryInterface(Ci.nsIConsoleMessage);
if (msg.message)
this.appendMessage(msg.message);
else // observed a null/"clear" message
this.clearConsole();
}
catch (ex2) {
// Give up and append the object itself as a string
this.appendMessage(aObject);
}
}
},
appendError: function cv_appendError(aObject) {
let row = this.createConsoleRow();
let nsIScriptError = Ci.nsIScriptError;
// Is this error actually just a non-fatal warning?
let warning = aObject.flags & nsIScriptError.warningFlag != 0;
let typetext = warning ? "typeWarning" : "typeError";
row.setAttribute("typetext", this._bundle.GetStringFromName(typetext));
row.setAttribute("type", warning ? "warning" : "error");
row.setAttribute("msg", aObject.errorMessage);
row.setAttribute("category", aObject.category);
if (aObject.lineNumber || aObject.sourceName) {
row.setAttribute("href", aObject.sourceName);
row.setAttribute("line", aObject.lineNumber);
}
else {
row.setAttribute("hideSource", "true");
}
if (aObject.sourceLine) {
row.setAttribute("code", aObject.sourceLine.replace(/\s/g, " "));
if (aObject.columnNumber) {
row.setAttribute("col", aObject.columnNumber);
row.setAttribute("errorDots", this.repeatChar(" ", aObject.columnNumber));
row.setAttribute("errorCaret", " ");
}
else {
row.setAttribute("hideCaret", "true");
}
}
else {
row.setAttribute("hideCode", "true");
}
let mode = document.getElementById("console-filter").value;
if (mode != "all" && mode != row.getAttribute("type"))
row.collapsed = true;
this.appendConsoleRow(row);
},
appendMessage: function cv_appendMessage (aMessage) {
let row = this.createConsoleRow();
row.setAttribute("type", "message");
row.setAttribute("msg", aMessage);
let mode = document.getElementById("console-filter").value;
if (mode != "all" && mode != "message")
row.collapsed = true;
this.appendConsoleRow(row);
},
createConsoleRow: function cv_createConsoleRow() {
let row = document.createElement("richlistitem");
row.setAttribute("class", "console-row");
return row;
},
appendConsoleRow: function cv_appendConsoleRow(aRow) {
this._list.appendChild(aRow);
if (++this._count > this.limit)
this.deleteFirst();
},
deleteFirst: function cv_deleteFirst() {
let node = this._list.firstChild;
this._list.removeChild(node);
--this._count;
},
appendInitialItems: function cv_appendInitialItems() {
let messages = Services.console.getMessageArray();
// In case getMessageArray returns 0-length array as null
if (!messages)
messages = [];
let limit = messages.length - this.limit;
if (limit < 0)
limit = 0;
// Checks if console ever been cleared
for (var i = messages.length - 1; i >= limit; --i)
if (!messages[i].message)
break;
// Populate with messages after latest "clear"
while (++i < messages.length)
this.appendItem(messages[i]);
},
clearConsole: function cv_clearConsole() {
if (this._count == 0) // already clear
return;
this._count = 0;
let newRows = this._list.cloneNode(false);
this._list.parentNode.replaceChild(newRows, this._list);
this._list = newRows;
this.selectedItem = null;
},
changeMode: function cv_changeMode() {
let mode = document.getElementById("console-filter").value;
if (this._list.getAttribute("mode") != mode) {
let rows = this._list.childNodes;
for (let i=0; i < rows.length; i++) {
let row = rows[i];
if (mode == "all" || row.getAttribute ("type") == mode)
row.collapsed = false;
else
row.collapsed = true;
}
this._list.mode = mode;
this._list.scrollToIndex(0);
}
},
onContextMenu: function cv_onContextMenu(aEvent) {
let row = aEvent.target;
let text = ["msg", "href", "line", "code", "col"].map(function(attr) row.getAttribute(attr))
.filter(function(x) x).join("\n");
ContextMenuUI.showContextMenu({
target: row,
json: {
types: ["copy"],
string: text
}
});
},
onEvalKeyPress: function cv_onEvalKeyPress(aEvent) {
if (aEvent.keyCode == 13)
this.evaluateTypein();
},
onConsoleBoxKeyPress: function cv_onConsoleBoxKeyPress(aEvent) {
if ((aEvent.charCode == 99 || aEvent.charCode == 67) && aEvent.ctrlKey && this._list && this._list.selectedItem) {
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
clipboard.copyString(this._list.selectedItem.getAttribute("msg"), document);
}
},
evaluateTypein: function cv_evaluateTypein() {
this._evalCode = this._evalTextbox.value;
this.loadOrDisplayResult();
},
loadOrDisplayResult: function cv_loadOrDisplayResult() {
if (this._evalCode) {
this._evalFrame.contentWindow.location = "javascript: " + this._evalCode.replace(/%/g, "%25");
this._evalCode = "";
return;
}
let resultRange = this._evalFrame.contentDocument.createRange();
resultRange.selectNode(this._evalFrame.contentDocument.documentElement);
let result = resultRange.toString();
if (result)
Services.console.logStringMessage(result);
// or could use appendMessage which doesn't persist
},
repeatChar: function cv_repeatChar(aChar, aCol) {
if (--aCol <= 0)
return "";
for (let i = 2; i < aCol; i += i)
aChar += aChar;
return aChar + aChar.slice(0, aCol - aChar.length);
}
};

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

@ -0,0 +1,121 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
dump("### ConsoleAPIObserver.js loaded\n");
/*
* ConsoleAPIObserver
*
*/
var ConsoleAPIObserver = {
init: function init() {
addMessageListener("Browser:TabOpen", this);
addMessageListener("Browser:TabClose", this);
},
receiveMessage: function receiveMessage(aMessage) {
let json = aMessage.json;
switch (aMessage.name) {
case "Browser:TabOpen":
Services.obs.addObserver(this, "console-api-log-event", false);
break;
case "Browser:TabClose":
Services.obs.removeObserver(this, "console-api-log-event", false);
break;
}
},
observe: function observe(aMessage, aTopic, aData) {
let contentWindowId = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
aMessage = aMessage.wrappedJSObject;
if (aMessage.ID != contentWindowId)
return;
let mappedArguments = Array.map(aMessage.arguments, this.formatResult, this);
let joinedArguments = Array.join(mappedArguments, " ");
if (aMessage.level == "error" || aMessage.level == "warn") {
let flag = (aMessage.level == "error" ? Ci.nsIScriptError.errorFlag : Ci.nsIScriptError.warningFlag);
let consoleMsg = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
consoleMsg.init(joinedArguments, null, null, 0, 0, flag, "content javascript");
Services.console.logMessage(consoleMsg);
} else if (aMessage.level == "trace") {
let bundle = Services.strings.createBundle("chrome://global/locale/headsUpDisplay.properties");
let args = aMessage.arguments;
let filename = this.abbreviateSourceURL(args[0].filename);
let functionName = args[0].functionName || bundle.GetStringFromName("stacktrace.anonymousFunction");
let lineNumber = args[0].lineNumber;
let body = bundle.formatStringFromName("stacktrace.outputMessage", [filename, functionName, lineNumber], 3);
body += "\n";
args.forEach(function(aFrame) {
body += aFrame.filename + " :: " + aFrame.functionName + " :: " + aFrame.lineNumber + "\n";
});
Services.console.logStringMessage(body);
} else {
Services.console.logStringMessage(joinedArguments);
}
},
getResultType: function getResultType(aResult) {
let type = aResult === null ? "null" : typeof aResult;
if (type == "object" && aResult.constructor && aResult.constructor.name)
type = aResult.constructor.name;
return type.toLowerCase();
},
formatResult: function formatResult(aResult) {
let output = "";
let type = this.getResultType(aResult);
switch (type) {
case "string":
case "boolean":
case "date":
case "error":
case "number":
case "regexp":
output = aResult.toString();
break;
case "null":
case "undefined":
output = type;
break;
default:
if (aResult.toSource) {
try {
output = aResult.toSource();
} catch (ex) { }
}
if (!output || output == "({})") {
output = aResult.toString();
}
break;
}
return output;
},
abbreviateSourceURL: function abbreviateSourceURL(aSourceURL) {
// Remove any query parameters.
let hookIndex = aSourceURL.indexOf("?");
if (hookIndex > -1)
aSourceURL = aSourceURL.substring(0, hookIndex);
// Remove a trailing "/".
if (aSourceURL[aSourceURL.length - 1] == "/")
aSourceURL = aSourceURL.substring(0, aSourceURL.length - 1);
// Remove all but the last path component.
let slashIndex = aSourceURL.lastIndexOf("/");
if (slashIndex > -1)
aSourceURL = aSourceURL.substring(slashIndex + 1);
return aSourceURL;
}
};
ConsoleAPIObserver.init();

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

@ -0,0 +1,696 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This stays here because otherwise it's hard to tell if there's a parsing error
dump("### Content.js loaded\n");
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
let Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "Services", function() {
Cu.import("resource://gre/modules/Services.jsm");
return Services;
});
XPCOMUtils.defineLazyGetter(this, "Rect", function() {
Cu.import("resource://gre/modules/Geometry.jsm");
return Rect;
});
XPCOMUtils.defineLazyGetter(this, "Point", function() {
Cu.import("resource://gre/modules/Geometry.jsm");
return Point;
});
XPCOMUtils.defineLazyServiceGetter(this, "gFocusManager",
"@mozilla.org/focus-manager;1", "nsIFocusManager");
XPCOMUtils.defineLazyServiceGetter(this, "gDOMUtils",
"@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
let XULDocument = Ci.nsIDOMXULDocument;
let HTMLHtmlElement = Ci.nsIDOMHTMLHtmlElement;
let HTMLIFrameElement = Ci.nsIDOMHTMLIFrameElement;
let HTMLFrameElement = Ci.nsIDOMHTMLFrameElement;
let HTMLFrameSetElement = Ci.nsIDOMHTMLFrameSetElement;
let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
const kReferenceDpi = 240; // standard "pixel" size used in some preferences
const kStateActive = 0x00000001; // :active pseudoclass for elements
/*
* ElementTouchHelper
*
* Assists users by watching for mouse clicks in content and redirect
* them to the best found target.
*/
const ElementTouchHelper = {
get radius() {
let prefs = Services.prefs;
delete this.radius;
return this.radius = { "top": prefs.getIntPref("ui.touch.radius.topmm"),
"right": prefs.getIntPref("ui.touch.radius.rightmm"),
"bottom": prefs.getIntPref("ui.touch.radius.bottommm"),
"left": prefs.getIntPref("ui.touch.radius.leftmm")
};
},
get weight() {
delete this.weight;
return this.weight = { "visited": Services.prefs.getIntPref("ui.touch.radius.visitedWeight")
};
},
/* Retrieve the closest element to a point by looking at borders position */
getClosest: function getClosest(aWindowUtils, aX, aY) {
if (!this.dpiRatio)
this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
let dpiRatio = this.dpiRatio;
let target = aWindowUtils.elementFromPoint(aX, aY,
true, /* ignore root scroll frame*/
false); /* don't flush layout */
// return early if the click is just over a clickable element
if (this._isElementClickable(target))
return target;
let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio,
this.radius.right * dpiRatio,
this.radius.bottom * dpiRatio,
this.radius.left * dpiRatio, true, false);
let threshold = Number.POSITIVE_INFINITY;
for (let i = 0; i < nodes.length; i++) {
let current = nodes[i];
if (!current.mozMatchesSelector || !this._isElementClickable(current))
continue;
let rect = current.getBoundingClientRect();
let distance = this._computeDistanceFromRect(aX, aY, rect);
// increase a little bit the weight for already visited items
if (current && current.mozMatchesSelector("*:visited"))
distance *= (this.weight.visited / 100);
if (distance < threshold) {
target = current;
threshold = distance;
}
}
return target;
},
_isElementClickable: function _isElementClickable(aElement) {
const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label";
for (let elem = aElement; elem; elem = elem.parentNode) {
if (this._hasMouseListener(elem))
return true;
if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector))
return true;
}
return false;
},
_computeDistanceFromRect: function _computeDistanceFromRect(aX, aY, aRect) {
let x = 0, y = 0;
let xmost = aRect.left + aRect.width;
let ymost = aRect.top + aRect.height;
// compute horizontal distance from left/right border depending if X is
// before/inside/after the element's rectangle
if (aRect.left < aX && aX < xmost)
x = Math.min(xmost - aX, aX - aRect.left);
else if (aX < aRect.left)
x = aRect.left - aX;
else if (aX > xmost)
x = aX - xmost;
// compute vertical distance from top/bottom border depending if Y is
// above/inside/below the element's rectangle
if (aRect.top < aY && aY < ymost)
y = Math.min(ymost - aY, aY - aRect.top);
else if (aY < aRect.top)
y = aRect.top - aY;
if (aY > ymost)
y = aY - ymost;
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
},
_els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService),
_clickableEvents: ["mousedown", "mouseup", "click"],
_hasMouseListener: function _hasMouseListener(aElement) {
let els = this._els;
let listeners = els.getListenerInfoFor(aElement, {});
for (let i = 0; i < listeners.length; i++) {
if (this._clickableEvents.indexOf(listeners[i].type) != -1)
return true;
}
return false;
}
};
/*
* Global functions
*/
/*
* elementFromPoint
*
* @param x,y Browser coordinates
* @return Element at position, null if no active browser or no element found
*/
function elementFromPoint(x, y) {
// browser's elementFromPoint expect browser-relative client coordinates.
// subtract browser's scroll values to adjust
let cwu = Util.getWindowUtils(content);
let elem = ElementTouchHelper.getClosest(cwu, x, y);
// step through layers of IFRAMEs and FRAMES to find innermost element
while (elem && (elem instanceof HTMLIFrameElement ||
elem instanceof HTMLFrameElement)) {
// adjust client coordinates' origin to be top left of iframe viewport
let rect = elem.getBoundingClientRect();
x -= rect.left;
y -= rect.top;
let windowUtils = elem.contentDocument
.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
elem = ElementTouchHelper.getClosest(windowUtils, x, y);
}
return elem;
}
/*
* getBoundingContentRect
*
* @param aElement
* @return Bounding content rect adjusted for scroll and frame offsets.
*/
function getBoundingContentRect(aElement) {
if (!aElement)
return new Rect(0, 0, 0, 0);
let document = aElement.ownerDocument;
while(document.defaultView.frameElement)
document = document.defaultView.frameElement.ownerDocument;
let offset = ContentScroll.getScrollOffset(content);
offset = new Point(offset.x, offset.y);
let r = aElement.getBoundingClientRect();
// step out of iframes and frames, offsetting scroll values
let view = aElement.ownerDocument.defaultView;
for (let frame = view; frame != content; frame = frame.parent) {
// adjust client coordinates' origin to be top left of iframe viewport
let rect = frame.frameElement.getBoundingClientRect();
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
offset.add(rect.left + parseInt(left), rect.top + parseInt(top));
}
return new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height);
}
/*
* getOverflowContentBoundingRect
*
* @param aElement
* @return Bounding content rect adjusted for scroll and frame offsets.
*/
function getOverflowContentBoundingRect(aElement) {
let r = getBoundingContentRect(aElement);
// If the overflow is hidden don't bother calculating it
let computedStyle = aElement.ownerDocument.defaultView.getComputedStyle(aElement);
let blockDisplays = ["block", "inline-block", "list-item"];
if ((blockDisplays.indexOf(computedStyle.getPropertyValue("display")) != -1 &&
computedStyle.getPropertyValue("overflow") == "hidden") ||
aElement instanceof HTMLSelectElement) {
return r;
}
for (let i = 0; i < aElement.childElementCount; i++) {
r = r.union(getBoundingContentRect(aElement.children[i]));
}
return r;
}
/*
* Content
*
* Browser event receiver for content.
*/
let Content = {
_debugEvents: false,
get formAssistant() {
delete this.formAssistant;
return this.formAssistant = new FormAssistant();
},
init: function init() {
this._isZoomedToElement = false;
// Asyncronous messages sent from the browser
addMessageListener("Browser:Blur", this);
addMessageListener("Browser:SaveAs", this);
addMessageListener("Browser:MozApplicationCache:Fetch", this);
addMessageListener("Browser:SetCharset", this);
addMessageListener("Browser:CanUnload", this);
addMessageListener("Browser:PanBegin", this);
addEventListener("touchstart", this, false);
addEventListener("click", this, true);
addEventListener("keydown", this);
addEventListener("keyup", this);
// Synchronous events caught during the bubbling phase
addEventListener("MozApplicationManifest", this, false);
addEventListener("DOMContentLoaded", this, false);
addEventListener("pagehide", this, false);
// Attach a listener to watch for "click" events bubbling up from error
// pages and other similar page. This lets us fix bugs like 401575 which
// require error page UI to do privileged things, without letting error
// pages have any privilege themselves.
addEventListener("click", this, false);
docShell.QueryInterface(Ci.nsIDocShellHistory).useGlobalHistory = true;
},
/*******************************************
* Events
*/
handleEvent: function handleEvent(aEvent) {
if (this._debugEvents) Util.dumpLn("Content:", aEvent.type);
switch (aEvent.type) {
case "MozApplicationManifest": {
let doc = aEvent.originalTarget;
sendAsyncMessage("Browser:MozApplicationManifest", {
location: doc.documentURIObject.spec,
manifest: doc.documentElement.getAttribute("manifest"),
charset: doc.characterSet
});
break;
}
case "keydown":
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
this.formAssistant.close();
break;
case "keyup":
// If after a key is pressed we still have no input, then close
// the autocomplete. Perhaps the user used backspace or delete.
if (!aEvent.target.value)
this.formAssistant.close();
else
this.formAssistant.open(aEvent.target);
break;
case "click":
if (aEvent.eventPhase == aEvent.BUBBLING_PHASE)
this._onClick(aEvent);
else
this._genericMouseClick(aEvent);
break;
case "DOMContentLoaded":
this._maybeNotifyErroPage();
break;
case "pagehide":
if (aEvent.target == content.document)
this._resetFontSize();
break;
case "touchstart":
let touch = aEvent.changedTouches[0];
this._genericMouseDown(touch.clientX, touch.clientY);
break;
}
},
receiveMessage: function receiveMessage(aMessage) {
if (this._debugEvents) Util.dumpLn("Content:", aMessage.name);
let json = aMessage.json;
let x = json.x;
let y = json.y;
let modifiers = json.modifiers;
switch (aMessage.name) {
case "Browser:Blur":
gFocusManager.clearFocus(content);
break;
case "Browser:CanUnload":
let canUnload = docShell.contentViewer.permitUnload();
sendSyncMessage("Browser:CanUnload:Return", { permit: canUnload });
break;
case "Browser:SaveAs":
break;
case "Browser:MozApplicationCache:Fetch": {
let currentURI = Services.io.newURI(json.location, json.charset, null);
let manifestURI = Services.io.newURI(json.manifest, json.charset, currentURI);
let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
.getService(Ci.nsIOfflineCacheUpdateService);
updateService.scheduleUpdate(manifestURI, currentURI, content);
break;
}
case "Browser:SetCharset": {
docShell.charset = json.charset;
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
break;
}
case "Browser:PanBegin":
this._cancelTapHighlight();
break;
}
},
/******************************************************
* generic input handlers
*
* regardless of whether the input was received via
* message manager or sent directly via dispatch.
*/
_genericMouseDown: function _genericMouseDown(x, y) {
let element = elementFromPoint(x, y);
if (!element)
return;
// There is no need to have a feedback for disabled element
let isDisabled = element instanceof HTMLOptionElement ?
(element.disabled || element.parentNode.disabled) : element.disabled;
if (isDisabled)
return;
// Set the target element to active
this._doTapHighlight(element);
},
_genericMouseClick: function _genericMouseClick(aEvent) {
ContextMenuHandler.reset();
let element = elementFromPoint(aEvent.clientX, aEvent.clientY);
if (!element)
return;
// Only show autocomplete after the item is clicked
if (!this.lastClickElement || this.lastClickElement != element) {
this.lastClickElement = element;
if (aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE &&
!(element instanceof HTMLSelectElement)) {
return;
}
}
this.formAssistant.focusSync = true;
// The form manager handles focus related changes on form elements.
// If it returns false, it didn't find anything to act on. If the
// target element doesn't match the current focus element, clear
// focus. This allows users to remove focus from form elements by
// taping on white space content.
if (!this.formAssistant.open(element, aEvent.clientX, aEvent.clientY)) {
if (gFocusManager.focusedElement &&
gFocusManager.focusedElement != element) {
gFocusManager.focusedElement.blur();
}
// This may not have any effect if element is unfocusable.
gFocusManager.setFocus(element, Ci.nsIFocusManager.FLAG_NOSCROLL);
sendAsyncMessage("FindAssist:Hide", { });
}
// Fire mouse events on everything but selects, see bug 685197
if (element instanceof HTMLSelectElement) {
aEvent.preventDefault()
aEvent.stopPropagation()
}
this._cancelTapHighlight();
this.formAssistant.focusSync = false;
},
/******************************************************
* Event handlers
*/
// Checks clicks we care about - events bubbling up from about pages.
_onClick: function _onClick(aEvent) {
// Don't trust synthetic events
if (!aEvent.isTrusted)
return;
let ot = aEvent.originalTarget;
let errorDoc = ot.ownerDocument;
if (!errorDoc)
return;
// If the event came from an ssl error page, it is probably either
// "Add Exception…" or "Get me out of here!" button.
if (/^about:certerror\?e=nssBadCert/.test(errorDoc.documentURI)) {
let perm = errorDoc.getElementById("permanentExceptionButton");
let temp = errorDoc.getElementById("temporaryExceptionButton");
if (ot == temp || ot == perm) {
let action = (ot == perm ? "permanent" : "temporary");
sendAsyncMessage("Browser:CertException",
{ url: errorDoc.location.href, action: action });
} else if (ot == errorDoc.getElementById("getMeOutOfHereButton")) {
sendAsyncMessage("Browser:CertException",
{ url: errorDoc.location.href, action: "leave" });
}
} else if (/^about:blocked/.test(errorDoc.documentURI)) {
// The event came from a button on a malware/phishing block page
// First check whether it's malware or phishing, so that we can
// use the right strings/links.
let isMalware = /e=malwareBlocked/.test(errorDoc.documentURI);
if (ot == errorDoc.getElementById("getMeOutButton")) {
sendAsyncMessage("Browser:BlockedSite",
{ url: errorDoc.location.href, action: "leave" });
} else if (ot == errorDoc.getElementById("reportButton")) {
// This is the "Why is this site blocked" button. For malware,
// we can fetch a site-specific report, for phishing, we redirect
// to the generic page describing phishing protection.
let action = isMalware ? "report-malware" : "report-phising";
sendAsyncMessage("Browser:BlockedSite",
{ url: errorDoc.location.href, action: action });
} else if (ot == errorDoc.getElementById("ignoreWarningButton")) {
// Allow users to override and continue through to the site,
// but add a notify bar as a reminder, so that they don't lose
// track after, e.g., tab switching.
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
webNav.loadURI(content.location,
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
null, null, null);
}
}
},
/******************************************************
* General utilities
*/
_getContentClientRects: function getContentClientRects(aElement) {
let offset = ContentScroll.getScrollOffset(content);
offset = new Point(offset.x, offset.y);
let nativeRects = aElement.getClientRects();
// step out of iframes and frames, offsetting scroll values
for (let frame = aElement.ownerDocument.defaultView; frame != content;
frame = frame.parent) {
// adjust client coordinates' origin to be top left of iframe viewport
let rect = frame.frameElement.getBoundingClientRect();
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
offset.add(rect.left + parseInt(left), rect.top + parseInt(top));
}
let result = [];
for (let i = nativeRects.length - 1; i >= 0; i--) {
let r = nativeRects[i];
result.push({ left: r.left + offset.x,
top: r.top + offset.y,
width: r.width,
height: r.height
});
}
return result;
},
_maybeNotifyErroPage: function _maybeNotifyErroPage() {
// Notify browser that an error page is being shown instead
// of the target location. Necessary to get proper thumbnail
// updates on chrome for error pages.
if (content.location.href !== content.document.documentURI)
sendAsyncMessage("Browser:ErrorPage", null);
},
_resetFontSize: function _resetFontSize() {
this._isZoomedToElement = false;
this._setMinFontSize(0);
},
_highlightElement: null,
_doTapHighlight: function _doTapHighlight(aElement) {
gDOMUtils.setContentState(aElement, kStateActive);
this._highlightElement = aElement;
},
_cancelTapHighlight: function _cancelTapHighlight(aElement) {
gDOMUtils.setContentState(content.document.documentElement, kStateActive);
this._highlightElement = null;
},
/*
* _sendMouseEvent
*
* Delivers mouse events directly to the content window, bypassing
* the input overlay.
*/
_sendMouseEvent: function _sendMouseEvent(aName, aElement, aX, aY, aButton) {
// Elements can be off from the aX/aY point because due to touch radius.
// If outside, we move the touch point to the center of the element.
if (!(aElement instanceof HTMLHtmlElement)) {
let isTouchClick = true;
let rects = this._getContentClientRects(aElement);
for (let i = 0; i < rects.length; i++) {
let rect = rects[i];
// We might be able to deal with fractional pixels, but mouse
// events won't. Deflate the bounds in by 1 pixel to deal with
// any fractional scroll offset issues.
let inBounds =
(aX > rect.left + 1 && aX < (rect.left + rect.width - 1)) &&
(aY > rect.top + 1 && aY < (rect.top + rect.height - 1));
if (inBounds) {
isTouchClick = false;
break;
}
}
if (isTouchClick) {
let rect = new Rect(rects[0].left, rects[0].top,
rects[0].width, rects[0].height);
if (rect.isEmpty())
return;
let point = rect.center();
aX = point.x;
aY = point.y;
}
}
let button = aButton || 0;
let scrollOffset = ContentScroll.getScrollOffset(content);
let x = aX - scrollOffset.x;
let y = aY - scrollOffset.y;
// setting touch source here is important so that when this gets
// captured by our precise input detection we can ignore it.
let windowUtils = Util.getWindowUtils(content);
windowUtils.sendMouseEventToWindow(aName, x, y, button, 1, 0, true,
1.0, Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE);
},
_setMinFontSize: function _setMinFontSize(aSize) {
let viewer = docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
if (viewer)
viewer.minFontSize = aSize;
}
};
Content.init();
var FormSubmitObserver = {
init: function init(){
addMessageListener("Browser:TabOpen", this);
addMessageListener("Browser:TabClose", this);
addEventListener("pageshow", this, false);
Services.obs.addObserver(this, "invalidformsubmit", false);
},
handleEvent: function handleEvent(aEvent) {
let target = aEvent.originalTarget;
let isRootDocument = (target == content.document || target.ownerDocument == content.document);
if (!isRootDocument)
return;
// Reset invalid submit state on each pageshow
if (aEvent.type == "pageshow")
Content.formAssistant.invalidSubmit = false;
},
receiveMessage: function receiveMessage(aMessage) {
let json = aMessage.json;
switch (aMessage.name) {
case "Browser:TabOpen":
Services.obs.addObserver(this, "formsubmit", false);
break;
case "Browser:TabClose":
Services.obs.removeObserver(this, "formsubmit", false);
break;
}
},
notify: function notify(aFormElement, aWindow, aActionURI, aCancelSubmit) {
// Do not notify unless this is the window where the submit occurred
if (aWindow == content)
// We don't need to send any data along
sendAsyncMessage("Browser:FormSubmit", {});
},
notifyInvalidSubmit: function notifyInvalidSubmit(aFormElement, aInvalidElements) {
if (!aInvalidElements.length)
return;
let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
if (!(element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
element instanceof HTMLSelectElement ||
element instanceof HTMLButtonElement)) {
return;
}
Content.formAssistant.invalidSubmit = true;
Content.formAssistant.open(element);
},
QueryInterface : function(aIID) {
if (!aIID.equals(Ci.nsIFormSubmitObserver) &&
!aIID.equals(Ci.nsISupportsWeakReference) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
}
};
FormSubmitObserver.init();

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

@ -0,0 +1,360 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const kXLinkNamespace = "http://www.w3.org/1999/xlink";
dump("### ContextMenuHandler.js loaded\n");
var ContextMenuHandler = {
_types: [],
init: function ch_init() {
// Events we catch from content during the bubbling phase
addEventListener("contextmenu", this, false);
addEventListener("pagehide", this, false);
addEventListener("select", this, false);
// Messages we receive from browser
addMessageListener("Browser:ContextCommand", this, false);
this.popupNode = null;
},
_getLinkURL: function ch_getLinkURL(aLink) {
let href = aLink.href;
if (href)
return href;
href = aLink.getAttributeNS(kXLinkNamespace, "href");
if (!href || !href.match(/\S/)) {
// Without this we try to save as the current doc,
// for example, HTML case also throws if empty
throw "Empty href";
}
return Util.makeURLAbsolute(aLink.baseURI, href);
},
_getURI: function ch_getURI(aURL) {
try {
return Util.makeURI(aURL);
} catch (ex) { }
return null;
},
_getProtocol: function ch_getProtocol(aURI) {
if (aURI)
return aURI.scheme;
return null;
},
handleEvent: function ch_handleEvent(aEvent) {
switch (aEvent.type) {
case "contextmenu":
this._onContentContextMenu(aEvent);
break;
case "pagehide":
this.reset();
break;
case "select":
break;
}
},
receiveMessage: function ch_receiveMessage(aMessage) {
let node = this.popupNode;
let command = aMessage.json.command;
switch (command) {
case "play":
case "pause":
if (node instanceof Ci.nsIDOMHTMLMediaElement)
node[command]();
break;
case "videotab":
if (node instanceof Ci.nsIDOMHTMLVideoElement) {
node.pause();
Cu.import("resource:///modules/video.jsm");
Video.fullScreenSourceElement = node;
sendAsyncMessage("Browser:FullScreenVideo:Start");
}
break;
case "select-all":
this._onSelectAll();
break;
case "paste":
this._onPaste();
break;
}
},
/******************************************************
* Event handlers
*/
reset: function ch_reset() {
this.popupNode = null;
this._target = null;
},
// content contextmenu
_onContentContextMenu: function _onContentContextMenu(aEvent) {
if (aEvent.defaultPrevented)
return;
// Don't let these bubble up to input.js
aEvent.stopPropagation();
aEvent.preventDefault();
let state = {
types: [],
label: "",
linkURL: "",
linkTitle: "",
linkProtocol: null,
mediaURL: "",
x: aEvent.x,
y: aEvent.y
};
let popupNode = this.popupNode = aEvent.originalTarget;
let imageUrl = "";
// Do checks for nodes that never have children.
if (popupNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
// See if the user clicked on an image.
if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) {
state.types.push("image");
state.label = state.mediaURL = popupNode.currentURI.spec;
imageUrl = state.mediaURL;
// Retrieve the type of image from the cache since the url can fail to
// provide valuable informations
try {
let imageCache = Cc["@mozilla.org/image/cache;1"].getService(Ci.imgICache);
let props = imageCache.findEntryProperties(popupNode.currentURI,
content.document.characterSet);
if (props) {
state.contentType = String(props.get("type", Ci.nsISupportsCString));
state.contentDisposition = String(props.get("content-disposition",
Ci.nsISupportsCString));
}
} catch (ex) {
Util.dumpLn(ex.message);
// Failure to get type and content-disposition off the image is non-fatal
}
}
}
let elem = popupNode;
let isText = false;
while (elem) {
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
// is the target a link or a descendant of a link?
if (this._isLink(elem)) {
// If this is an image that links to itself, don't include both link and
// image otpions.
if (imageUrl == this._getLinkURL(elem)) {
elem = elem.parentNode;
continue;
}
state.types.push("link");
state.label = state.linkURL = this._getLinkURL(elem);
linkUrl = state.linkURL;
state.linkTitle = popupNode.textContent || popupNode.title;
state.linkProtocol = this._getProtocol(this._getURI(state.linkURL));
break;
} else if (this._isTextInput(elem)) {
let selectionStart = elem.selectionStart;
let selectionEnd = elem.selectionEnd;
state.types.push("input-text");
this._target = elem;
// Don't include "copy" for password fields.
if (!(elem instanceof Ci.nsIDOMHTMLInputElement) || elem.mozIsTextField(true)) {
if (selectionStart != selectionEnd) {
state.types.push("copy");
state.string = elem.value.slice(selectionStart, selectionEnd);
} else if (elem.value) {
state.types.push("copy-all");
state.string = elem.value;
}
}
if (selectionStart > 0 || selectionEnd < elem.textLength) {
state.types.push("select-all");
}
if (!elem.textLength) {
state.types.push("input-empty");
}
let flavors = ["text/unicode"];
let cb = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
let hasData = cb.hasDataMatchingFlavors(flavors,
flavors.length,
Ci.nsIClipboard.kGlobalClipboard);
if (hasData && !elem.readOnly) {
state.types.push("paste");
}
break;
} else if (this._isText(elem)) {
isText = true;
} else if (elem instanceof Ci.nsIDOMHTMLMediaElement ||
elem instanceof Ci.nsIDOMHTMLVideoElement) {
state.label = state.mediaURL = (elem.currentSrc || elem.src);
state.types.push((elem.paused || elem.ended) ?
"media-paused" : "media-playing");
if (elem instanceof Ci.nsIDOMHTMLVideoElement) {
state.types.push("video");
}
}
}
elem = elem.parentNode;
}
if (isText) {
// If this is text and has a selection, we want to bring
// up the copy option on the context menu.
if (content && content.getSelection() &&
content.getSelection().toString().length > 0) {
state.string = content.getSelection().toString();
state.types.push("copy");
state.types.push("selected-text");
} else {
// Add general content text if this isn't anything specific
if (state.types.indexOf("image") == -1 &&
state.types.indexOf("media") == -1 &&
state.types.indexOf("video") == -1 &&
state.types.indexOf("link") == -1 &&
state.types.indexOf("input-text") == -1) {
state.types.push("content-text");
}
}
}
// populate position and event source
state.xPos = aEvent.clientX;
state.yPos = aEvent.clientY;
state.source = aEvent.mozInputSource;
for (let i = 0; i < this._types.length; i++)
if (this._types[i].handler(state, popupNode))
state.types.push(this._types[i].name);
sendAsyncMessage("Content:ContextMenu", state);
},
_onSelectAll: function _onSelectAll() {
if (this._isTextInput(this._target)) {
// select all text in the input control
this._target.select();
} else {
// select the entire document
content.getSelection().selectAllChildren(content.document);
}
this.reset();
},
_onPaste: function _onPaste() {
// paste text if this is an input control
if (this._isTextInput(this._target)) {
let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
if (edit) {
edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
} else {
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
}
}
this.reset();
},
/*
* Utility routines used in testing for various
* HTML element types.
*/
_isTextInput: function _isTextInput(element) {
return ((element instanceof Ci.nsIDOMHTMLInputElement &&
element.mozIsTextField(false)) ||
element instanceof Ci.nsIDOMHTMLTextAreaElement);
},
_isLink: function _isLink(element) {
return ((element instanceof Ci.nsIDOMHTMLAnchorElement && element.href) ||
(element instanceof Ci.nsIDOMHTMLAreaElement && element.href) ||
element instanceof Ci.nsIDOMHTMLLinkElement ||
element.getAttributeNS(kXLinkNamespace, "type") == "simple");
},
_isText: function _isText(element) {
return (element instanceof Ci.nsIDOMHTMLParagraphElement ||
element instanceof Ci.nsIDOMHTMLDivElement ||
element instanceof Ci.nsIDOMHTMLLIElement ||
element instanceof Ci.nsIDOMHTMLPreElement ||
element instanceof Ci.nsIDOMHTMLHeadingElement ||
element instanceof Ci.nsIDOMHTMLTableCellElement ||
element instanceof Ci.nsIDOMHTMLBodyElement);
},
/**
* For add-ons to add new types and data to the ContextMenu message.
*
* @param aName A string to identify the new type.
* @param aHandler A function that takes a state object and a target element.
* If aHandler returns true, then aName will be added to the list of types.
* The function may also modify the state object.
*/
registerType: function registerType(aName, aHandler) {
this._types.push({name: aName, handler: aHandler});
},
/** Remove all handlers registered for a given type. */
unregisterType: function unregisterType(aName) {
this._types = this._types.filter(function(type) type.name != aName);
}
};
ContextMenuHandler.init();
ContextMenuHandler.registerType("mailto", function(aState, aElement) {
return aState.linkProtocol == "mailto";
});
ContextMenuHandler.registerType("callto", function(aState, aElement) {
let protocol = aState.linkProtocol;
return protocol == "tel" || protocol == "callto" || protocol == "sip" || protocol == "voipto";
});
ContextMenuHandler.registerType("link-openable", function(aState, aElement) {
return Util.isOpenableScheme(aState.linkProtocol);
});
["image", "video"].forEach(function(aType) {
ContextMenuHandler.registerType(aType+"-shareable", function(aState, aElement) {
if (aState.types.indexOf(aType) == -1)
return false;
let protocol = ContextMenuHandler._getProtocol(ContextMenuHandler._getURI(aState.mediaURL));
return Util.isShareableScheme(protocol);
});
});
ContextMenuHandler.registerType("image-loaded", function(aState, aElement) {
if (aState.types.indexOf("image") != -1) {
let request = aElement.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
return true;
}
return false;
});

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

@ -0,0 +1,76 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
dump("### FindHandler.js loaded\n");
var FindHandler = {
get _fastFind() {
delete this._fastFind;
this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
this._fastFind.init(docShell);
return this._fastFind;
},
init: function findHandlerInit() {
addMessageListener("FindAssist:Find", this);
addMessageListener("FindAssist:Next", this);
addMessageListener("FindAssist:Previous", this);
},
receiveMessage: function findHandlerReceiveMessage(aMessage) {
let findResult = Ci.nsITypeAheadFind.FIND_NOTFOUND;
let json = aMessage.json;
switch (aMessage.name) {
case "FindAssist:Find":
findResult = this._fastFind.find(json.searchString, false);
break;
case "FindAssist:Previous":
findResult = this._fastFind.findAgain(true, false);
break;
case "FindAssist:Next":
findResult = this._fastFind.findAgain(false, false);
break;
}
if (findResult == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
sendAsyncMessage("FindAssist:Show", { rect: null , result: findResult });
return;
}
let selection = this._fastFind.currentWindow.getSelection();
if (!selection.rangeCount || selection.isCollapsed) {
// The selection can be into an input or a textarea element
let nodes = content.document.querySelectorAll("input[type='text'], textarea");
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node instanceof Ci.nsIDOMNSEditableElement && node.editor) {
selection = node.editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL);
if (selection.rangeCount && !selection.isCollapsed)
break;
}
}
}
let scroll = ContentScroll.getScrollOffset(content);
for (let frame = this._fastFind.currentWindow; frame != content; frame = frame.parent) {
let rect = frame.frameElement.getBoundingClientRect();
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
scroll.add(rect.left + parseInt(left), rect.top + parseInt(top));
}
let rangeRect = selection.getRangeAt(0).getBoundingClientRect();
let rect = new Rect(scroll.x + rangeRect.left, scroll.y + rangeRect.top, rangeRect.width, rangeRect.height);
// Ensure the potential "scroll" event fired during a search as already fired
let timer = new Util.Timeout(function() {
sendAsyncMessage("FindAssist:Show", { rect: rect.isEmpty() ? null: rect , result: findResult });
});
timer.once(0);
}
};
FindHandler.init();

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

@ -0,0 +1,972 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let Ci = Components.interfaces;
let Cc = Components.classes;
dump("### FormHelper.js loaded\n");
const kPrefFormHelperEnabled = "formhelper.enabled";
let HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement;
let HTMLInputElement = Ci.nsIDOMHTMLInputElement;
let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
let HTMLIFrameElement = Ci.nsIDOMHTMLIFrameElement;
let HTMLDocument = Ci.nsIDOMHTMLDocument;
let HTMLHtmlElement = Ci.nsIDOMHTMLHtmlElement;
let HTMLBodyElement = Ci.nsIDOMHTMLBodyElement;
let HTMLLabelElement = Ci.nsIDOMHTMLLabelElement;
let HTMLButtonElement = Ci.nsIDOMHTMLButtonElement;
let HTMLOptGroupElement = Ci.nsIDOMHTMLOptGroupElement;
let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
let XULMenuListElement = Ci.nsIDOMXULMenuListElement;
/**
* Responsible of navigation between forms fields and of the opening of the assistant
*/
function FormAssistant() {
addMessageListener("FormAssist:Closed", this);
addMessageListener("FormAssist:Previous", this);
addMessageListener("FormAssist:Next", this);
addMessageListener("FormAssist:ChoiceSelect", this);
addMessageListener("FormAssist:ChoiceChange", this);
addMessageListener("FormAssist:AutoComplete", this);
addMessageListener("Content:SetWindowSize", this);
/* Listen text events in order to update the autocomplete suggestions as soon
* a key is entered on device
*/
addEventListener("text", this, false);
addEventListener("keypress", this, true);
addEventListener("keyup", this, false);
addEventListener("focus", this, true);
addEventListener("blur", this, true);
addEventListener("pageshow", this, false);
addEventListener("pagehide", this, false);
addEventListener("submit", this, false);
this._enabled = Services.prefs.prefHasUserValue(kPrefFormHelperEnabled) ?
Services.prefs.getBoolPref(kPrefFormHelperEnabled) : false;
};
FormAssistant.prototype = {
_debugEvents: false,
_selectWrapper: null,
_currentIndex: -1,
_elements: [],
invalidSubmit: false,
get currentElement() {
return this._elements[this._currentIndex];
},
get currentIndex() {
return this._currentIndex;
},
set currentIndex(aIndex) {
let element = this._elements[aIndex];
if (!element)
return -1;
if (this._isVisibleElement(element)) {
this._currentIndex = aIndex;
gFocusManager.setFocus(element, Ci.nsIFocusManager.FLAG_NOSCROLL);
// To ensure we get the current caret positionning of the focused
// element we need to delayed a bit the event
this._executeDelayed(function(self) {
// Bug 640870
// Sometimes the element inner frame get destroyed while the element
// receive the focus because the display is turned to 'none' for
// example, in this "fun" case just do nothing if the element is hidden
if (self._isVisibleElement(gFocusManager.focusedElement))
sendAsyncMessage("FormAssist:Show", self._getJSON());
});
} else {
// Repopulate the list of elements in the page, some could have gone
// because of AJAX changes for example
this._elements = [];
let currentIndex = this._getAllElements(gFocusManager.focusedElement)
if (aIndex < this._currentIndex)
this.currentIndex = currentIndex - 1;
else if (aIndex > this._currentIndex)
this.currentIndex = currentIndex + 1;
else if (this._currentIndex != currentIndex)
this.currentIndex = currentIndex;
}
return element;
},
_open: false,
open: function formHelperOpen(aElement, aX, aY) {
// If the click is on an option element we want to check if the parent
// is a valid target.
if (aElement instanceof HTMLOptionElement &&
aElement.parentNode instanceof HTMLSelectElement &&
!aElement.disabled) {
aElement = aElement.parentNode;
}
// The form assistant will close if a click happen:
// * outside of the scope of the form helper
// * hover a button of type=[image|submit]
// * hover a disabled element
if (!this._isValidElement(aElement)) {
let passiveButtons = { button: true, checkbox: true, file: true, radio: true, reset: true };
if ((aElement instanceof HTMLInputElement || aElement instanceof HTMLButtonElement) &&
passiveButtons[aElement.type] && !aElement.disabled)
return false;
// Check for plugins element
if (aElement instanceof Ci.nsIDOMHTMLEmbedElement) {
aX = aX || 0;
aY = aY || 0;
this._executeDelayed(function(self) {
let utils = Util.getWindowUtils(aElement.ownerDocument.defaultView);
if (utils.IMEStatus == utils.IME_STATUS_PLUGIN) {
let jsvar = {
current: {
id: aElement.id,
name: aElement.name,
title: "plugin",
value: null,
maxLength: 0,
type: (aElement.getAttribute("type") || "").toLowerCase(),
choices: null,
isAutocomplete: false,
validationMessage: null,
list: null,
rect: getBoundingContentRect(aElement),
caretRect: new Rect(aX, aY, 1, 10),
editable: true
},
hasPrevious: false,
hasNext: false
};
sendAsyncMessage("FormAssist:Show", jsvar);
}
});
return false;
}
return this.close();
}
// Look for a top editable element
if (this._isEditable(aElement))
aElement = this._getTopLevelEditable(aElement);
// There are some cases where we still want data to be sent to FormHelperUI
// even if form assistant is disabled:
// - the element is a choice list
// - the element has autocomplete suggestions
this._enabled = Services.prefs.prefHasUserValue(kPrefFormHelperEnabled) ?
Services.prefs.getBoolPref(kPrefFormHelperEnabled) : false;
if (!this._enabled && !this._isSelectElement(aElement) && !this._isAutocomplete(aElement)) {
return this.close();
}
if (this._enabled) {
this._elements = [];
this.currentIndex = this._getAllElements(aElement);
} else {
this._elements = [aElement];
this.currentIndex = 0;
}
return this._open = true;
},
close: function close() {
if (this._open) {
this._currentIndex = -1;
this._elements = [];
sendAsyncMessage("FormAssist:Hide", { });
this._open = false;
}
return this._open;
},
receiveMessage: function receiveMessage(aMessage) {
if (this._debugEvents) Util.dumpLn(aMessage.name);
let currentElement = this.currentElement;
if ((!this._enabled && !this._isAutocomplete(currentElement) && !getWrapperForElement(currentElement)) || !currentElement)
return;
let json = aMessage.json;
switch (aMessage.name) {
case "FormAssist:Previous":
this.currentIndex--;
break;
case "FormAssist:Next":
this.currentIndex++;
break;
case "Content:SetWindowSize":
// If the CSS viewport change just show the current element to the new
// position
sendAsyncMessage("FormAssist:Resize", this._getJSON());
break;
case "FormAssist:ChoiceSelect": {
this._selectWrapper = getWrapperForElement(currentElement);
this._selectWrapper.select(json.index, json.selected);
break;
}
case "FormAssist:ChoiceChange": {
// ChoiceChange could happened once we have move to an other element or
// to nothing, so we should keep the used wrapper in mind
this._selectWrapper.fireOnChange();
// New elements can be shown when a select is updated so we need to
// reconstruct the inner elements array and to take care of possible
// focus change, this is why we use "self.currentElement" instead of
// using directly "currentElement".
this._executeDelayed(function(self) {
let currentElement = self.currentElement;
if (!currentElement)
return;
self._elements = [];
self._currentIndex = self._getAllElements(currentElement);
});
break;
}
case "FormAssist:AutoComplete": {
try {
currentElement = currentElement.QueryInterface(Ci.nsIDOMNSEditableElement);
let imeEditor = currentElement.editor.QueryInterface(Ci.nsIEditorIMESupport);
if (imeEditor.composing)
imeEditor.forceCompositionEnd();
}
catch(e) {}
currentElement.value = json.value;
let event = currentElement.ownerDocument.createEvent("Events");
event.initEvent("DOMAutoComplete", true, true);
currentElement.dispatchEvent(event);
break;
}
case "FormAssist:Closed":
currentElement.blur();
this._currentIndex = null;
this._open = false;
break;
}
},
_els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService),
_hasKeyListener: function _hasKeyListener(aElement) {
let els = this._els;
let listeners = els.getListenerInfoFor(aElement, {});
for (let i = 0; i < listeners.length; i++) {
let listener = listeners[i];
if (["keyup", "keydown", "keypress"].indexOf(listener.type) != -1
&& !listener.inSystemEventGroup) {
return true;
}
}
return false;
},
focusSync: false,
handleEvent: function formHelperHandleEvent(aEvent) {
if (this._debugEvents) Util.dumpLn(aEvent.type, this.currentElement);
// focus changes should be taken into account only if the user has done a
// manual operation like manually clicking
let shouldIgnoreFocus = (aEvent.type == "focus" && !this._open && !this.focusSync);
if ((!this._open && aEvent.type != "focus") || shouldIgnoreFocus) {
return;
}
let currentElement = this.currentElement;
switch (aEvent.type) {
case "submit":
// submit is a final action and the form assistant should be closed
this.close();
break;
case "pagehide":
case "pageshow":
// When reacting to a page show/hide, if the focus is different this
// could mean the web page has dramatically changed because of
// an Ajax change based on fragment identifier
if (gFocusManager.focusedElement != currentElement)
this.close();
break;
case "focus":
let focusedElement = gFocusManager.getFocusedElementForWindow(content, true, {}) || aEvent.target;
// If a body element is editable and the body is the child of an
// iframe we can assume this is an advanced HTML editor, so let's
// redirect the form helper selection to the iframe element
if (focusedElement && this._isEditable(focusedElement)) {
let editableElement = this._getTopLevelEditable(focusedElement);
if (this._isValidElement(editableElement)) {
this._executeDelayed(function(self) {
self.open(editableElement);
});
}
return;
}
// if an element is focused while we're closed but the element can be handle
// by the assistant, try to activate it (only during mouseup)
if (!currentElement) {
if (focusedElement && this._isValidElement(focusedElement)) {
this._executeDelayed(function(self) {
self.open(focusedElement);
});
}
return;
}
let focusedIndex = this._getIndexForElement(focusedElement);
if (focusedIndex != -1 && this.currentIndex != focusedIndex)
this.currentIndex = focusedIndex;
break;
case "blur":
content.setTimeout(function(self) {
if (!self._open)
return;
// If the blurring causes focus be in no other element,
// we should close the form assistant.
let focusedElement = gFocusManager.getFocusedElementForWindow(content, true, {});
if (!focusedElement)
self.close();
}, 0, this);
break;
case "text":
if (this._isValidatable(aEvent.target))
sendAsyncMessage("FormAssist:ValidationMessage", this._getJSON());
if (this._isAutocomplete(aEvent.target))
sendAsyncMessage("FormAssist:AutoComplete", this._getJSON());
break;
// key processing inside a select element are done during the keypress
// handler, preventing this one to be fired cancel the selection change
case "keypress":
// There is no need to handle keys if there is not element currently
// used by the form assistant
if (!currentElement)
return;
let formExceptions = { button: true, checkbox: true, file: true, image: true, radio: true, reset: true, submit: true };
if (this._isSelectElement(currentElement) || formExceptions[currentElement.type] ||
currentElement instanceof HTMLButtonElement || (currentElement.getAttribute("role") == "button" && currentElement.hasAttribute("tabindex"))) {
switch (aEvent.keyCode) {
case aEvent.DOM_VK_RIGHT:
this._executeDelayed(function(self) {
self.currentIndex++;
});
aEvent.stopPropagation();
aEvent.preventDefault();
break;
case aEvent.DOM_VK_LEFT:
this._executeDelayed(function(self) {
self.currentIndex--;
});
aEvent.stopPropagation();
aEvent.preventDefault();
break;
}
}
break;
case "keyup":
// There is no need to handle keys if there is not element currently
// used by the form assistant
if (!currentElement)
return;
switch (aEvent.keyCode) {
case aEvent.DOM_VK_DOWN:
if (currentElement instanceof HTMLInputElement && !this._isAutocomplete(currentElement)) {
if (this._hasKeyListener(currentElement))
return;
} else if (currentElement instanceof HTMLTextAreaElement) {
let existSelection = currentElement.selectionEnd - currentElement.selectionStart;
let isEnd = (currentElement.textLength == currentElement.selectionEnd);
if (!isEnd || existSelection)
return;
} else if (getListForElement(currentElement)) {
this.currentIndex = this.currentIndex;
return;
}
this.currentIndex++;
break;
case aEvent.DOM_VK_UP:
if (currentElement instanceof HTMLInputElement && !this._isAutocomplete(currentElement)) {
if (this._hasKeyListener(currentElement))
return;
} else if (currentElement instanceof HTMLTextAreaElement) {
let existSelection = currentElement.selectionEnd - currentElement.selectionStart;
let isStart = (currentElement.selectionEnd == 0);
if (!isStart || existSelection)
return;
} else if (this._isSelectElement(currentElement)) {
this.currentIndex = this.currentIndex;
return;
}
this.currentIndex--;
break;
case aEvent.DOM_VK_RETURN:
if (!this._isVisibleElement(currentElement))
this.close();
break;
case aEvent.DOM_VK_ESCAPE:
case aEvent.DOM_VK_TAB:
break;
default:
if (this._isValidatable(aEvent.target)) {
sendAsyncMessage("FormAssist:ValidationMessage", this._getJSON());
}
if (this._isAutocomplete(aEvent.target)) {
sendAsyncMessage("FormAssist:AutoComplete", this._getJSON());
} else if (currentElement && this._isSelectElement(currentElement)) {
this.currentIndex = this.currentIndex;
}
break;
}
let caretRect = this._getCaretRect();
if (!caretRect.isEmpty())
sendAsyncMessage("FormAssist:Update", { caretRect: caretRect });
}
},
_executeDelayed: function formHelperExecuteSoon(aCallback) {
let self = this;
let timer = new Util.Timeout(function() {
aCallback(self);
});
timer.once(0);
},
_filterEditables: function formHelperFilterEditables(aNodes) {
let result = [];
for (let i = 0; i < aNodes.length; i++) {
let node = aNodes[i];
// Avoid checking the top level editable element of each node
if (this._isEditable(node)) {
let editableElement = this._getTopLevelEditable(node);
if (result.indexOf(editableElement) == -1)
result.push(editableElement);
}
else {
result.push(node);
}
}
return result;
},
_isEditable: function formHelperIsEditable(aElement) {
let canEdit = false;
if (aElement.isContentEditable || aElement.designMode == "on") {
canEdit = true;
} else if (aElement instanceof HTMLIFrameElement && (aElement.contentDocument.body.isContentEditable || aElement.contentDocument.designMode == "on")) {
canEdit = true;
} else {
canEdit = aElement.ownerDocument && aElement.ownerDocument.designMode == "on";
}
return canEdit;
},
_getTopLevelEditable: function formHelperGetTopLevelEditable(aElement) {
if (!(aElement instanceof HTMLIFrameElement)) {
let element = aElement;
// Retrieve the top element that is editable
if (element instanceof HTMLHtmlElement)
element = element.ownerDocument.body;
else if (element instanceof HTMLDocument)
element = element.body;
while (element && !this._isEditable(element))
element = element.parentNode;
// Return the container frame if we are into a nested editable frame
if (element && element instanceof HTMLBodyElement && element.ownerDocument.defaultView != content.document.defaultView)
return element.ownerDocument.defaultView.frameElement;
}
return aElement;
},
_isValidatable: function(aElement) {
return this.invalidSubmit &&
(aElement instanceof HTMLInputElement ||
aElement instanceof HTMLTextAreaElement ||
aElement instanceof HTMLSelectElement ||
aElement instanceof HTMLButtonElement);
},
_isAutocomplete: function formHelperIsAutocomplete(aElement) {
if (aElement instanceof HTMLInputElement) {
if (aElement.getAttribute("type") == "password")
return false;
let autocomplete = aElement.getAttribute("autocomplete");
let allowedValues = ["off", "false", "disabled"];
if (allowedValues.indexOf(autocomplete) == -1)
return true;
}
return false;
},
/*
* This function is similar to getListSuggestions from
* components/satchel/src/nsInputListAutoComplete.js but sadly this one is
* used by the autocomplete.xml binding which is not in used in fennec
*/
_getListSuggestions: function formHelperGetListSuggestions(aElement) {
if (!(aElement instanceof HTMLInputElement) || !aElement.list)
return [];
let suggestions = [];
let filter = !aElement.hasAttribute("mozNoFilter");
let lowerFieldValue = aElement.value.toLowerCase();
let options = aElement.list.options;
let length = options.length;
for (let i = 0; i < length; i++) {
let item = options.item(i);
let label = item.value;
if (item.label)
label = item.label;
else if (item.text)
label = item.text;
if (filter && label.toLowerCase().indexOf(lowerFieldValue) == -1)
continue;
suggestions.push({ label: label, value: item.value });
}
return suggestions;
},
_isValidElement: function formHelperIsValidElement(aElement) {
if (!aElement.getAttribute)
return false;
let formExceptions = { button: true, checkbox: true, file: true, image: true, radio: true, reset: true, submit: true };
if (aElement instanceof HTMLInputElement && formExceptions[aElement.type])
return false;
if (aElement instanceof HTMLButtonElement ||
(aElement.getAttribute("role") == "button" && aElement.hasAttribute("tabindex")))
return false;
return this._isNavigableElement(aElement) && this._isVisibleElement(aElement);
},
_isNavigableElement: function formHelperIsNavigableElement(aElement) {
if (aElement.disabled || aElement.getAttribute("tabindex") == "-1")
return false;
if (aElement.getAttribute("role") == "button" && aElement.hasAttribute("tabindex"))
return true;
if (this._isSelectElement(aElement) || aElement instanceof HTMLTextAreaElement)
return true;
if (aElement instanceof HTMLInputElement || aElement instanceof HTMLButtonElement)
return !(aElement.type == "hidden");
return this._isEditable(aElement);
},
_isVisibleElement: function formHelperIsVisibleElement(aElement) {
let style = aElement ? aElement.ownerDocument.defaultView.getComputedStyle(aElement, null) : null;
if (!style)
return false;
let isVisible = (style.getPropertyValue("visibility") != "hidden");
let isOpaque = (style.getPropertyValue("opacity") != 0);
let rect = aElement.getBoundingClientRect();
// Since the only way to show a drop-down menu for a select when the form
// assistant is enabled is to return true here, a select is allowed to have
// an opacity to 0 in order to let web developpers add a custom design on
// top of it. This is less important to use the form assistant for the
// other types of fields because even if the form assistant won't fired,
// the focus will be in and a VKB will popup if needed
return isVisible && (isOpaque || this._isSelectElement(aElement)) && (rect.height != 0 || rect.width != 0);
},
_isSelectElement: function formHelperIsSelectElement(aElement) {
return (aElement instanceof HTMLSelectElement || aElement instanceof XULMenuListElement);
},
/** Caret is used to input text for this element. */
_getCaretRect: function _formHelperGetCaretRect() {
let element = this.currentElement;
let focusedElement = gFocusManager.getFocusedElementForWindow(content, true, {});
if (element && (element.mozIsTextField && element.mozIsTextField(false) ||
element instanceof HTMLTextAreaElement) && focusedElement == element && this._isVisibleElement(element)) {
let utils = Util.getWindowUtils(element.ownerDocument.defaultView);
let rect = utils.sendQueryContentEvent(utils.QUERY_CARET_RECT, element.selectionEnd, 0, 0, 0);
if (rect) {
let scroll = ContentScroll.getScrollOffset(element.ownerDocument.defaultView);
return new Rect(scroll.x + rect.left, scroll.y + rect.top, rect.width, rect.height);
}
}
return new Rect(0, 0, 0, 0);
},
/** Gets a rect bounding important parts of the element that must be seen when assisting. */
_getRect: function _formHelperGetRect() {
const kDistanceMax = 100;
let element = this.currentElement;
let elRect = getBoundingContentRect(element);
let labels = this._getLabels();
for (let i=0; i<labels.length; i++) {
let labelRect = labels[i].rect;
if (labelRect.left < elRect.left) {
let isClose = Math.abs(labelRect.left - elRect.left) - labelRect.width < kDistanceMax &&
Math.abs(labelRect.top - elRect.top) - labelRect.height < kDistanceMax;
if (isClose) {
let width = labelRect.width + elRect.width + (elRect.left - labelRect.left - labelRect.width);
return new Rect(labelRect.left, labelRect.top, width, elRect.height).expandToIntegers();
}
}
}
return elRect;
},
_getLabels: function formHelperGetLabels() {
let associatedLabels = [];
let element = this.currentElement;
let labels = element.ownerDocument.getElementsByTagName("label");
for (let i=0; i<labels.length; i++) {
let label = labels[i];
if ((label.control == element || label.getAttribute("for") == element.id) && this._isVisibleElement(label)) {
associatedLabels.push({
rect: getBoundingContentRect(label),
title: label.textContent
});
}
}
return associatedLabels;
},
_getAllElements: function getAllElements(aElement) {
// XXX good candidate for tracing if possible.
// The tough ones are lenght and isVisibleElement.
let document = aElement.ownerDocument;
if (!document)
return;
let documents = Util.getAllDocuments(document);
let elements = this._elements;
for (let i = 0; i < documents.length; i++) {
let selector = "input, button, select, textarea, [role=button], iframe, [contenteditable=true]";
let nodes = documents[i].querySelectorAll(selector);
nodes = this._filterRadioButtons(nodes);
for (let j = 0; j < nodes.length; j++) {
let node = nodes[j];
if (!this._isNavigableElement(node) || !this._isVisibleElement(node))
continue;
elements.push(node);
}
}
this._elements = this._filterEditables(elements);
function orderByTabIndex(a, b) {
// for an explanation on tabbing navigation see
// http://www.w3.org/TR/html401/interact/forms.html#h-17.11.1
// In resume tab index navigation order is 1, 2, 3, ..., 32767, 0
if (a.tabIndex == 0 || b.tabIndex == 0)
return b.tabIndex;
return a.tabIndex > b.tabIndex;
}
this._elements = this._elements.sort(orderByTabIndex);
// retrieve the correct index
let currentIndex = this._getIndexForElement(aElement);
return currentIndex;
},
_getIndexForElement: function(aElement) {
let currentIndex = -1;
let elements = this._elements;
for (let i = 0; i < elements.length; i++) {
if (elements[i] == aElement)
return i;
}
return -1;
},
_getJSON: function() {
let element = this.currentElement;
let choices = getListForElement(element);
let editable = (element instanceof HTMLInputElement && element.mozIsTextField(false)) || this._isEditable(element);
let labels = this._getLabels();
return {
current: {
id: element.id,
name: element.name,
title: labels.length ? labels[0].title : "",
value: element.value,
maxLength: element.maxLength,
type: (element.getAttribute("type") || "").toLowerCase(),
choices: choices,
isAutocomplete: this._isAutocomplete(element),
validationMessage: this.invalidSubmit ? element.validationMessage : null,
list: this._getListSuggestions(element),
rect: this._getRect(),
caretRect: this._getCaretRect(),
editable: editable
},
hasPrevious: !!this._elements[this._currentIndex - 1],
hasNext: !!this._elements[this._currentIndex + 1]
};
},
/**
* For each radio button group, remove all but the checked button
* if there is one, or the first button otherwise.
*/
_filterRadioButtons: function(aNodes) {
// First pass: Find the checked or first element in each group.
let chosenRadios = {};
for (let i=0; i < aNodes.length; i++) {
let node = aNodes[i];
if (node.type == "radio" && (!chosenRadios.hasOwnProperty(node.name) || node.checked))
chosenRadios[node.name] = node;
}
// Second pass: Exclude all other radio buttons from the list.
let result = [];
for (let i=0; i < aNodes.length; i++) {
let node = aNodes[i];
if (node.type == "radio" && chosenRadios[node.name] != node)
continue;
result.push(node);
}
return result;
}
};
/******************************************************************************
* The next classes wraps some forms elements such as different type of list to
* abstract the difference between html and xul element while manipulating them
* - SelectWrapper : <html:select>
* - MenulistWrapper : <xul:menulist>
*****************************************************************************/
function getWrapperForElement(aElement) {
let wrapper = null;
if (aElement instanceof HTMLSelectElement) {
wrapper = new SelectWrapper(aElement);
}
else if (aElement instanceof XULMenuListElement) {
wrapper = new MenulistWrapper(aElement);
}
return wrapper;
}
function getListForElement(aElement) {
let wrapper = getWrapperForElement(aElement);
if (!wrapper)
return null;
let optionIndex = 0;
let result = {
multiple: wrapper.getMultiple(),
choices: []
};
// Build up a flat JSON array of the choices. In HTML, it's possible for select element choices
// to be under a group header (but not recursively). We distinguish between headers and entries
// using the boolean "list.group".
// XXX If possible, this would be a great candidate for tracing.
let children = wrapper.getChildren();
for (let i = 0; i < children.length; i++) {
let child = children[i];
if (wrapper.isGroup(child)) {
// This is the group element. Add an entry in the choices that says that the following
// elements are a member of this group.
result.choices.push({ group: true,
text: child.label || child.firstChild.data,
disabled: child.disabled
});
let subchildren = child.children;
for (let j = 0; j < subchildren.length; j++) {
let subchild = subchildren[j];
result.choices.push({
group: false,
inGroup: true,
text: wrapper.getText(subchild),
disabled: child.disabled || subchild.disabled,
selected: subchild.selected,
optionIndex: optionIndex++
});
}
}
else if (wrapper.isOption(child)) {
// This is a regular choice under no group.
result.choices.push({
group: false,
inGroup: false,
text: wrapper.getText(child),
disabled: child.disabled,
selected: child.selected,
optionIndex: optionIndex++
});
}
}
return result;
};
function SelectWrapper(aControl) {
this._control = aControl;
}
SelectWrapper.prototype = {
getSelectedIndex: function() {
return this._control.selectedIndex;
},
getMultiple: function() {
return this._control.multiple;
},
getOptions: function() {
return this._control.options;
},
getChildren: function() {
return this._control.children;
},
getText: function(aChild) {
return aChild.text;
},
isOption: function(aChild) {
return aChild instanceof HTMLOptionElement;
},
isGroup: function(aChild) {
return aChild instanceof HTMLOptGroupElement;
},
select: function(aIndex, aSelected) {
let options = this._control.options;
if (this.getMultiple())
options[aIndex].selected = aSelected;
else
options.selectedIndex = aIndex;
},
fireOnChange: function() {
let control = this._control;
let evt = this._control.ownerDocument.createEvent("Events");
evt.initEvent("change", true, true, this._control.ownerDocument.defaultView, 0,
false, false,
false, false, null);
content.setTimeout(function() {
control.dispatchEvent(evt);
}, 0);
}
};
// bug 559792
// Use wrappedJSObject when control is in content for extra protection
function MenulistWrapper(aControl) {
this._control = aControl;
}
MenulistWrapper.prototype = {
getSelectedIndex: function() {
let control = this._control.wrappedJSObject || this._control;
let result = control.selectedIndex;
return ((typeof result == "number" && !isNaN(result)) ? result : -1);
},
getMultiple: function() {
return false;
},
getOptions: function() {
let control = this._control.wrappedJSObject || this._control;
return control.menupopup.children;
},
getChildren: function() {
let control = this._control.wrappedJSObject || this._control;
return control.menupopup.children;
},
getText: function(aChild) {
return aChild.label;
},
isOption: function(aChild) {
return aChild instanceof Ci.nsIDOMXULSelectControlItemElement;
},
isGroup: function(aChild) {
return false;
},
select: function(aIndex, aSelected) {
let control = this._control.wrappedJSObject || this._control;
control.selectedIndex = aIndex;
},
fireOnChange: function() {
let control = this._control;
let evt = document.createEvent("XULCommandEvent");
evt.initCommandEvent("command", true, true, window, 0,
false, false,
false, false, null);
content.setTimeout(function() {
control.dispatchEvent(evt);
}, 0);
}
};

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

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var PluginCTPHandler = {
init: function() {
addEventListener("PluginClickToPlay", this, false);
},
addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
let self = this;
let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
linkNode.addEventListener("click",
function(evt) {
if (!evt.isTrusted)
return;
evt.preventDefault();
if (callbackArgs.length == 0)
callbackArgs = [ evt ];
(self[callbackName]).apply(self, callbackArgs);
},
true);
linkNode.addEventListener("keydown",
function(evt) {
if (!evt.isTrusted)
return;
if (evt.keyCode == evt.DOM_VK_RETURN) {
evt.preventDefault();
if (callbackArgs.length == 0)
callbackArgs = [ evt ];
evt.preventDefault();
(self[callbackName]).apply(self, callbackArgs);
}
},
true);
},
handleEvent : function(event) {
if (event.type != "PluginClickToPlay")
return;
let plugin = event.target;
PluginHandler.addLinkClickCallback(plugin, "reloadToEnablePlugin");
},
reloadToEnablePlugin: function() {
sendAsyncMessage("Browser:PluginClickToPlayClicked", { });
}
};
//PluginCTPHandler.init();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,136 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
dump("### ViewportHandler.js loaded\n");
// See getViewportMetadata. Blindly copied from Safari
// documentation for now.
const kViewportMinScale = 0;
const kViewportMaxScale = 10;
const kViewportMinWidth = 200;
const kViewportMaxWidth = 10000;
const kViewportMinHeight = 223;
const kViewportMaxHeight = 10000;
/*
* ViewportHandler
*
* Plucks zoom info out of web page metadata and forwards it to the
* browser.
*/
let ViewportHandler = {
init: function init() {
addEventListener("DOMWindowCreated", this, false);
addEventListener("DOMMetaAdded", this, false);
addEventListener("DOMContentLoaded", this, false);
addEventListener("pageshow", this, false);
},
handleEvent: function handleEvent(aEvent) {
let target = aEvent.originalTarget;
let isRootDocument = (target == content.document || target.ownerDocument == content.document);
if (!isRootDocument)
return;
switch (aEvent.type) {
case "DOMWindowCreated":
this.resetMetadata();
break;
case "DOMMetaAdded":
if (target.name == "viewport")
this.updateMetadata();
break;
case "DOMContentLoaded":
case "pageshow":
this.updateMetadata();
break;
}
},
resetMetadata: function resetMetadata() {
sendAsyncMessage("Browser:ViewportMetadata", null);
},
updateMetadata: function updateMetadata() {
sendAsyncMessage("Browser:ViewportMetadata", this.getViewportMetadata());
},
/**
* Returns an object with the page's preferred viewport properties:
* defaultZoom (optional float): The initial scale when the page is loaded.
* minZoom (optional float): The minimum zoom level.
* maxZoom (optional float): The maximum zoom level.
* width (optional int): The CSS viewport width in px.
* height (optional int): The CSS viewport height in px.
* autoSize (boolean): Resize the CSS viewport when the window resizes.
* allowZoom (boolean): Let the user zoom in or out.
* autoScale (boolean): Adjust the viewport properties to account for display density.
*/
getViewportMetadata: function getViewportMetadata() {
let doctype = content.document.doctype;
if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId))
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
let windowUtils = Util.getWindowUtils(content);
let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly");
if (handheldFriendly == "true")
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
if (content.document instanceof XULDocument)
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
// HACK: Since we can't set the scale in local tabs (bug 597081), we force
// them to device-width and scale=1 so they will lay out reasonably.
if (Util.isLocalScheme(content.document.documentURI))
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
// HACK: Since we can't set the scale correctly in frameset pages yet (bug 645756), we force
// them to device-width and scale=1 so they will lay out reasonably.
if (content.frames.length > 0 && (content.document.body instanceof HTMLFrameSetElement))
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
// viewport details found here
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html
// Note: These values will be NaN if parseFloat or parseInt doesn't find a number.
// Remember that NaN is contagious: Math.max(1, NaN) == Math.min(1, NaN) == NaN.
let scale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale"));
let minScale = parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale"));
let maxScale = parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale"));
let widthStr = windowUtils.getDocumentMetadata("viewport-width");
let heightStr = windowUtils.getDocumentMetadata("viewport-height");
let width = Util.clamp(parseInt(widthStr), kViewportMinWidth, kViewportMaxWidth);
let height = Util.clamp(parseInt(heightStr), kViewportMinHeight, kViewportMaxHeight);
let allowZoomStr = windowUtils.getDocumentMetadata("viewport-user-scalable");
let allowZoom = !/^(0|no|false)$/.test(allowZoomStr); // WebKit allows 0, "no", or "false"
scale = Util.clamp(scale, kViewportMinScale, kViewportMaxScale);
minScale = Util.clamp(minScale, kViewportMinScale, kViewportMaxScale);
maxScale = Util.clamp(maxScale, kViewportMinScale, kViewportMaxScale);
// If initial scale is 1.0 and width is not set, assume width=device-width
let autoSize = (widthStr == "device-width" ||
(!widthStr && (heightStr == "device-height" || scale == 1.0)));
return {
defaultZoom: scale,
minZoom: minScale,
maxZoom: maxScale,
width: width,
height: height,
autoSize: autoSize,
allowZoom: allowZoom,
autoScale: true
};
}
};
ViewportHandler.init();

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

@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
@namespace html url(http://www.w3.org/1999/xhtml);
xul|*:-moz-system-metric(touch-enabled) {
cursor: none !important;
}
html|*:-moz-system-metric(touch-enabled) {
cursor: none !important;
}

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

@ -0,0 +1,469 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const URI_GENERIC_ICON_DOWNLOAD = "chrome://browser/skin/images/alert-downloads-30.png";
var Downloads = {
_inited: false,
_progressAlert: null,
get manager() {
return Cc["@mozilla.org/download-manager;1"]
.getService(Ci.nsIDownloadManager);
},
_getReferrerOrSource: function dh__getReferrerOrSource(aDownload) {
return aDownload.referrer.spec || aDownload.source.spec;
},
_getLocalFile: function dh__getLocalFile(aFileURI) {
// XXX it's possible that using a null char-set here is bad
const fileUrl = Services.io.newURI(aFileURI.spec || aFileURI, null, null).QueryInterface(Ci.nsIFileURL);
return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
},
init: function dh_init() {
if (this._inited)
return;
this._inited = true;
Services.obs.addObserver(this, "dl-start", true);
Services.obs.addObserver(this, "dl-done", true);
},
uninit: function dh_uninit() {
Services.obs.removeObserver(this, "dl-start");
Services.obs.removeObserver(this, "dl-done");
},
openDownload: function dh_openDownload(aDownload) {
// expects xul item
let id = aDownload.getAttribute("downloadId");
let download = this.manager.getDownload(id)
let fileURI = download.target;
let file = this._getLocalFile(fileURI);
try {
file.launch();
} catch (ex) { }
},
removeDownload: function dh_removeDownload(aDownload) {
// aDownload is the XUL element here,
// and .target currently returns the target attribute (string value)
let id = aDownload.getAttribute("downloadId");
let download = this.manager.getDownload(id);
if (download) {
// TODO <sfoster> add class/mark element for removal transition?
this.manager.removeDownload(id);
}
},
cancelDownload: function dh_cancelDownload(aDownload) {
let id = aDownload.getAttribute("downloadId");
let download = this.manager.getDownload(id)
this.manager.cancelDownload(id);
let fileURI = download.target;
let file = this._getLocalFile(fileURI);
if (file.exists())
file.remove(false);
},
pauseDownload: function dh_pauseDownload(aDownload) {
let id = aDownload.getAttribute("downloadId");
this.manager.pauseDownload(id);
},
resumeDownload: function dh_resumeDownload(aDownload) {
let id = aDownload.getAttribute("downloadId");
this.manager.resumeDownload(id);
},
retryDownload: function dh_retryDownload(aDownload) {
let id = aDownload.getAttribute("downloadId");
this.manager.retryDownload(id);
},
showPage: function dh_showPage(aDownload) {
let id = aDownload.getAttribute("downloadId");
let download = this.manager.getDownload(id)
let uri = this._getReferrerOrSource(download);
if (uri)
BrowserUI.newTab(uri, Browser.selectedTab);
},
showAlert: function dh_showAlert(aName, aMessage, aTitle, aIcon) {
var notifier = Cc["@mozilla.org/alerts-service;1"]
.getService(Ci.nsIAlertsService);
// Callback for tapping on the alert popup
let observer = {
observe: function (aSubject, aTopic, aData) {
if (aTopic == "alertclickcallback")
PanelUI.show("downloads-container");
}
};
if (!aTitle)
aTitle = Strings.browser.GetStringFromName("alertDownloads");
if (!aIcon)
aIcon = URI_GENERIC_ICON_DOWNLOAD;
notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", observer, aName);
},
observe: function (aSubject, aTopic, aData) {
let download = aSubject.QueryInterface(Ci.nsIDownload);
let msgKey = "";
switch (aTopic) {
case "dl-start":
msgKey = "alertDownloadsStart";
if (!this._progressAlert) {
this._progressAlert = new AlertDownloadProgressListener();
this.manager.addListener(this._progressAlert);
}
break;
case "dl-done":
msgKey = "alertDownloadsDone";
break;
}
if (msgKey) {
let message = Strings.browser.formatStringFromName(msgKey, [download.displayName], 1);
let url = download.target.spec.replace("file:", "download:");
this.showAlert(url, message);
}
},
QueryInterface: function (aIID) {
if (!aIID.equals(Ci.nsIObserver) &&
!aIID.equals(Ci.nsISupportsWeakReference) &&
!aIID.equals(Ci.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
/**
* Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
* fills it with the user's downloads. It observes download notifications,
* so it'll automatically update to reflect current download progress.
*
* @param aSet Control implementing nsIDOMXULSelectControlElement.
* @param {Number} aLimit Maximum number of items to show in the view.
*/
function DownloadsView(aSet, aLimit) {
this._set = aSet;
this._limit = aLimit;
this._progress = new DownloadProgressListener(this);
Downloads.manager.addListener(this._progress);
// Look for the removed download notification
let obs = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
obs.addObserver(this, "download-manager-remove-download-guid", false);
this.getDownloads();
}
DownloadsView.prototype = {
_progress: null,
_stmt: null,
_timeoutID: null,
_set: null,
_limit: null,
_getItemForDownloadGuid: function dv__getItemForDownload(aGuid) {
return this._set.querySelector("richgriditem[downloadGuid='" + aGuid + "']");
},
_getItemForDownload: function dv__getItemForDownload(aDownload) {
return this._set.querySelector("richgriditem[downloadId='" + aDownload.id + "']");
},
_getDownloadForItem: function dv__getDownloadForItem(anItem) {
let id = anItem.getAttribute("downloadId");
return Downloads.manager.getDownload(id);
},
_getAttrsForDownload: function dv__getAttrsForDownload(aDownload) {
// expects a DownloadManager download object
return {
typeName: 'download',
downloadId: aDownload.id,
downloadGuid: aDownload.guid,
name: aDownload.displayName,
target: aDownload.target,
iconURI: "moz-icon://" + aDownload.displayName + "?size=64",
date: DownloadUtils.getReadableDates(new Date())[0],
domain: DownloadUtils.getURIHost(aDownload.source.spec)[0],
size: this._getDownloadSize(aDownload.size),
state: aDownload.state
};
},
_updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
for (let name in aAttrs)
anItem.setAttribute(name, aAttrs[name]);
},
_getDownloadSize: function dv__getDownloadSize (aSize) {
let displaySize = DownloadUtils.convertByteUnits(aSize);
if (displaySize[0] > 0) // [0] is size, [1] is units
return displaySize.join("");
else
return Strings.browser.GetStringFromName("downloadsUnknownSize");
},
_initStatement: function dv__initStatement() {
if (this._stmt)
this._stmt.finalize();
let limitClause = this._limit ? ("LIMIT " + this._limit) : "";
this._stmt = Downloads.manager.DBConnection.createStatement(
"SELECT id, guid, name, target, source, state, startTime, endTime, referrer, " +
"currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " +
"FROM moz_downloads " +
"ORDER BY isActive DESC, endTime DESC, startTime DESC " +
limitClause);
},
_stepDownloads: function dv__stepDownloads(aNumItems) {
try {
if (!this._stmt.executeStep()) {
// final record;
this._stmt.finalize();
this._stmt = null;
this._fire("DownloadsReady", this._set);
return;
}
let attrs = {
typeName: 'download',
// TODO: <sfoster> the remove event gives us guid not id; should we use guid as the download (record) id?
downloadGuid: this._stmt.row.guid,
downloadId: this._stmt.row.id,
name: this._stmt.row.name,
target: this._stmt.row.target,
iconURI: "moz-icon://" + this._stmt.row.name + "?size=25",
date: DownloadUtils.getReadableDates(new Date(this._stmt.row.endTime / 1000))[0],
domain: DownloadUtils.getURIHost(this._stmt.row.source)[0],
size: this._getDownloadSize(this._stmt.row.maxBytes),
state: this._stmt.row.state
};
let item = this._set.appendItem(attrs.target, attrs.downloadId);
this._updateItemWithAttrs(item, attrs);
} catch (e) {
// Something went wrong when stepping or getting values, so quit
this._stmt.reset();
return;
}
// Add another item to the list if we should;
// otherwise, let the UI update and continue later
if (aNumItems > 1) {
this._stepDownloads(aNumItems - 1);
} else {
// Use a shorter delay for earlier downloads to display them faster
let delay = Math.min(this._set.itemCount * 10, 300);
let self = this;
this._timeoutID = setTimeout(function() { self._stepDownloads(5); }, delay);
}
},
_fire: function _fire(aName, anElement) {
let event = document.createEvent("Events");
event.initEvent(aName, true, true);
anElement.dispatchEvent(event);
},
observe: function dv_managerObserver(aSubject, aTopic, aData) {
// observer-service message handler
switch (aTopic) {
case "download-manager-remove-download-guid":
let guid = aSubject.QueryInterface(Ci.nsISupportsCString);
this.removeDownload({
guid: guid
});
return;
}
},
getDownloads: function dv_getDownloads() {
this._initStatement();
clearTimeout(this._timeoutID);
// Since we're pulling in all downloads, clear the list to avoid duplication
this.clearDownloads();
this._stmt.reset();
this._stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED);
this._stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
this._stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
this._stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
this._stmt.bindInt32Parameter(4, Ci.nsIDownloadManager.DOWNLOAD_SCANNING);
// Take a quick break before we actually start building the list
let self = this;
this._timeoutID = setTimeout(function() {
self._stepDownloads(1);
}, 0);
},
clearDownloads: function dv_clearDownloads() {
while (this._set.itemCount > 0)
this._set.removeItemAt(0);
},
addDownload: function dv_addDownload(aDownload) {
// expects a download manager download object
let attrs = this._getAttrsForDownload(aDownload);
let item = this._set.insertItemAt(0, attrs.target, attrs.downloadId);
this._updateItemWithAttrs(item, attrs);
},
updateDownload: function dv_updateDownload(aDownload) {
// expects a download manager download object
let item = this._getItemForDownload(aDownload);
if (!item)
return;
let attrs = this._getAttrsForDownload(aDownload);
this._updateItemWithAttrs(item, attrs);
},
removeDownload: function dv_removeDownload(aDownload) {
// expects an object with id or guid property
let item;
if (aDownload.id) {
item = this._getItemForDownload(aDownload.id);
} else if (aDownload.guid) {
item = this._getItemForDownloadGuid(aDownload.guid);
}
if (!item)
return;
let idx = this._set.getIndexOfItem(item);
if (idx < 0)
return;
// any transition needed here?
this._set.removeItemAt(idx);
},
destruct: function dv_destruct() {
Downloads.manager.removeListener(this._progress);
}
};
var DownloadsPanelView = {
_view: null,
get _grid() { return document.getElementById("downloads-list"); },
get visible() { return PanelUI.isPaneVisible("downloads-container"); },
init: function init() {
this._view = new DownloadsView(this._grid);
},
show: function show() {
this._grid.arrangeItems();
},
uninit: function uninit() {
this._view.destruct();
}
};
/**
* Notifies a DownloadsView about updates in the state of various downloads.
*
* @param aView An instance of DownloadsView.
*/
function DownloadProgressListener(aView) {
this._view = aView;
}
DownloadProgressListener.prototype = {
_view: null,
//////////////////////////////////////////////////////////////////////////////
//// nsIDownloadProgressListener
onDownloadStateChange: function dPL_onDownloadStateChange(aState, aDownload) {
let state = aDownload.state;
switch (state) {
case Ci.nsIDownloadManager.DOWNLOAD_QUEUED:
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY:
this._view.addDownload(aDownload);
break;
case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
break;
}
this._view.updateDownload(aDownload);
},
onProgressChange: function dPL_onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
// TODO <jwilde>: Add more detailed progress information.
this._view.updateDownload(aDownload);
},
onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { },
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener])
};
/**
* Tracks download progress so that additional information can be displayed
* about its download in alert popups.
*/
function AlertDownloadProgressListener() { }
AlertDownloadProgressListener.prototype = {
//////////////////////////////////////////////////////////////////////////////
//// nsIDownloadProgressListener
onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload) {
let strings = Strings.browser;
let availableSpace = -1;
try {
// diskSpaceAvailable is not implemented on all systems
let availableSpace = aDownload.targetFile.diskSpaceAvailable;
} catch(ex) { }
let contentLength = aDownload.size;
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
Downloads.showAlert(aDownload.target.spec.replace("file:", "download:"),
strings.GetStringFromName("alertDownloadsNoSpace"),
strings.GetStringFromName("alertDownloadsSize"));
Downloads.cancelDownload(aDownload.id);
}
},
onDownloadStateChange: function(aState, aDownload) { },
onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { },
onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { },
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener])
};

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

@ -0,0 +1,115 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
/**
A class to add exceptions to override SSL certificate problems. The functionality
itself is borrowed from exceptionDialog.js.
*/
function SSLExceptions() {
this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
.getService(Ci.nsICertOverrideService);
}
SSLExceptions.prototype = {
_overrideService: null,
_sslStatus: null,
getInterface: function SSLE_getInterface(aIID) {
return this.QueryInterface(aIID);
},
QueryInterface: function SSLE_QueryInterface(aIID) {
if (aIID.equals(Ci.nsIBadCertListener2) ||
aIID.equals(Ci.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
/**
To collect the SSL status we intercept the certificate error here
and store the status for later use.
*/
notifyCertProblem: function SSLE_notifyCertProblem(socketInfo, sslStatus, targetHost) {
this._sslStatus = sslStatus.QueryInterface(Ci.nsISSLStatus);
return true; // suppress error UI
},
/**
Attempt to download the certificate for the location specified to get the SSLState
for the certificate and the errors.
*/
_checkCert: function SSLE_checkCert(aURI) {
this._sslStatus = null;
var req = new XMLHttpRequest();
try {
if(aURI) {
req.open("GET", aURI.prePath, false);
req.channel.notificationCallbacks = this;
req.send(null);
}
} catch (e) {
// We *expect* exceptions if there are problems with the certificate
// presented by the site. Log it, just in case, but we can proceed here,
// with appropriate sanity checks
Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " +
"This results in a (mostly harmless) exception being thrown. " +
"Logged for information purposes only: " + e);
}
return this._sslStatus;
},
/**
Internal method to create an override.
*/
_addOverride: function SSLE_addOverride(aURI, aWindow, temporary) {
var SSLStatus = this._checkCert(aURI);
var certificate = SSLStatus.serverCert;
var flags = 0;
// in private browsing do not store exceptions permanently ever
if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
temporary = true;
}
if(SSLStatus.isUntrusted)
flags |= this._overrideService.ERROR_UNTRUSTED;
if(SSLStatus.isDomainMismatch)
flags |= this._overrideService.ERROR_MISMATCH;
if(SSLStatus.isNotValidAtThisTime)
flags |= this._overrideService.ERROR_TIME;
this._overrideService.rememberValidityOverride(
aURI.asciiHost,
aURI.port,
certificate,
flags,
temporary);
},
/**
Creates a permanent exception to override all overridable errors for
the given URL.
*/
addPermanentException: function SSLE_addPermanentException(aURI, aWindow) {
this._addOverride(aURI, aWindow, false);
},
/**
Creates a temporary exception to override all overridable errors for
the given URL.
*/
addTemporaryException: function SSLE_addTemporaryException(aURI, aWindow) {
this._addOverride(aURI, aWindow, true);
}
};

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

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var AlertsHelper = {
_timeoutID: -1,
_listener: null,
_cookie: "",
_clickable: false,
get container() {
delete this.container;
let container = document.getElementById("alerts-container");
let self = this;
container.addEventListener("transitionend", function() {
self.alertTransitionOver();
}, true);
return this.container = container;
},
showAlertNotification: function ah_show(aImageURL, aTitle, aText, aTextClickable, aCookie, aListener) {
this._clickable = aTextClickable || false;
this._listener = aListener || null;
this._cookie = aCookie || "";
// Reset the container settings from the last time so layout can happen naturally
let container = this.container;
container.removeAttribute("width");
let alertText = document.getElementById("alerts-text");
alertText.style.whiteSpace = "";
document.getElementById("alerts-image").setAttribute("src", aImageURL);
document.getElementById("alerts-title").value = aTitle;
alertText.textContent = aText;
container.hidden = false;
let bcr = container.getBoundingClientRect();
if (bcr.width > window.innerWidth - 50) {
// If the window isn't wide enough, we need to re-layout
container.setAttribute("width", window.innerWidth - 50); // force a max width
alertText.style.whiteSpace = "pre-wrap"; // wrap text as needed
bcr = container.getBoundingClientRect(); // recalculate the bcr
}
container.setAttribute("width", bcr.width); // redundant but cheap
container.setAttribute("height", bcr.height);
container.classList.add("showing");
let timeout = Services.prefs.getIntPref("alerts.totalOpenTime");
let self = this;
if (this._timeoutID)
clearTimeout(this._timeoutID);
this._timeoutID = setTimeout(function() { self._timeoutAlert(); }, timeout);
},
_timeoutAlert: function ah__timeoutAlert() {
this._timeoutID = -1;
this.container.classList.remove("showing");
if (this._listener)
this._listener.observe(null, "alertfinished", this._cookie);
},
alertTransitionOver: function ah_alertTransitionOver() {
let container = this.container;
if (!container.classList.contains("showing")) {
container.height = 0;
container.hidden = true;
}
},
click: function ah_click(aEvent) {
if (this._clickable && this._listener)
this._listener.observe(null, "alertclickcallback", this._cookie);
if (this._timeoutID != -1) {
clearTimeout(this._timeoutID);
this._timeoutAlert();
}
}
};

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

@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var CaptureDialog = {
_video: null,
_container: null,
_lastOrientationEvent: null,
init: function() {
document.getElementsByAttribute('command', 'cmd_ok')[0].focus();
this._video = document.getElementById("capturepicker-video");
this._container = document.getElementById("capturepicker-container");
window.addEventListener("resize", this, false);
window.addEventListener("deviceorientation", this, false);
this.handleEvent({ type: "resize" });
},
setPreviewOrientation: function(aEvent) {
if (window.innerWidth < window.innerHeight) {
if (aEvent.beta > 0)
this._video.style.MozTransform = "rotate(90deg)";
else
this._video.style.MozTransform = "rotate(-90deg)";
this._container.classList.add("vertical");
}
else {
if (aEvent.gamma > 0)
this._video.style.MozTransform = "rotate(180deg)";
else
this._video.style.MozTransform = "";
this._container.classList.remove("vertical");
}
},
handleEvent: function(aEvent) {
if (aEvent.type == "deviceorientation") {
if (!this._lastOrientationEvent)
this.setPreviewOrientation(aEvent);
this._lastOrientationEvent = aEvent;
}
else if (aEvent.type == "resize") {
if (this._lastOrientationEvent)
this.setPreviewOrientation(this._lastOrientationEvent);
}
},
cancel: function() {
this.doClose(false);
},
capture: function() {
this.doClose(true);
},
doClose: function(aResult) {
window.removeEventListener("resize", this, false);
window.removeEventListener("deviceorientation", this, false);
let dialog = document.getElementById("capturepicker-dialog");
dialog.arguments.result = aResult;
if (aResult)
dialog.arguments.path = this.saveFrame();
document.getElementById("capturepicker-video").setAttribute("src", "");
dialog.close();
},
saveFrame: function() {
let Cc = Components.classes;
let Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
let width = 320;
let height = 240;
let rotation = 0;
if (window.innerWidth < window.innerHeight) {
width = 240;
height = 320;
if (this._lastOrientationEvent.beta > 0)
rotation = Math.PI / 2;
else
rotation = -Math.PI / 2;
}
else {
if (this._lastOrientationEvent.gamma > 0)
rotation = Math.PI;
}
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
//ctx.save();
if (rotation != 0) {
if (rotation == Math.PI / 2)
ctx.translate(width, 0);
else if (rotation == -Math.PI / 2)
ctx.translate(-width, 0);
else
ctx.translate(width, height);
ctx.rotate(rotation);
}
ctx.drawImage(document.getElementById("capturepicker-video"), 0, 0, 320, 240);
//ctx.restore();
let url = canvas.toDataURL("image/png");
let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
let file = tmpDir.clone();
file.append("capture.png");
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
let mDone = false;
let mFailed = false;
let progressListener = {
onProgressChange: function() {
/* Ignore progress callback */
},
onStateChange: function(aProgress, aRequest, aStateFlag, aStatus) {
if (aStateFlag & Ci.nsIWebProgressListener.STATE_STOP) {
mDone = true;
}
}
}
persist.progressListener = progressListener;
let source = Services.io.newURI(url, "UTF8", null);;
persist.saveURI(source, null, null, null, null, file);
// don't wait more than 3 seconds for a successful save
window.setTimeout(function() {
mDone = true;
mFailed = true;
}, 3000);
while (!mDone)
Services.tm.currentThread.processNextEvent(true);
return (mFailed ? null : file.path);
}
}

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

@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* appears to have something to do with capturing a camera image. tries to
* create a dialog, which will fail. TBD. */
var CapturePickerUI = {
init: function() {
this.messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
this.messageManager.addMessageListener("CapturePicker:Show", this);
},
receiveMessage: function(aMessage) {
switch (aMessage.name) {
case "CapturePicker:Show":
let params = { result: true };
let dialog = DialogUI.importModal(null, "chrome://browser/content/prompt/CaptureDialog.xul", params);
document.getElementById("capturepicker-title").appendChild(document.createTextNode(aMessage.json.title));
dialog.waitForClose();
return { value: params.result, path: params.path };
break;
}
// prevents warning from the script loader
return null;
}
};

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

@ -0,0 +1,89 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var CharsetMenu = {
_strings: null,
_charsets: null,
get strings() {
if (!this._strings)
this._strings = Services.strings.createBundle("chrome://global/locale/charsetTitles.properties");
return this._strings;
},
init: function() {
PageActions.register("pageaction-charset", this.updatePageAction, this);
},
updatePageAction: function(aNode) {
let pref = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data;
if (pref == "true") {
let charset = getBrowser().docShell.forcedCharset;
if (charset) {
charset = charset.toString();
charset = charset.trim().toLowerCase();
aNode.setAttribute("description", this.strings.GetStringFromName(charset + ".title"));
} else if (aNode.hasAttribute("description")) {
aNode.removeAttribute("description");
}
}
return ("true" == pref)
},
_toMenuItems: function(aCharsets, aCurrent) {
let ret = [];
aCharsets.forEach(function (aSet) {
try {
let string = aSet.trim().toLowerCase();
ret.push({
label: this.strings.GetStringFromName(string + ".title"),
value: string,
selected: (string == aCurrent)
});
} catch(ex) { }
}, this);
return ret;
},
menu : {
dispatchEvent: function(aEvent) {
if (aEvent.type == "command")
CharsetMenu.setCharset(this.menupopup.children[this.selectedIndex].value);
},
menupopup: {
hasAttribute: function(aAttr) { return false; },
},
selectedIndex: -1
},
get charsets() {
if (!this._charsets) {
this._charsets = Services.prefs.getComplexValue("intl.charsetmenu.browser.static", Ci.nsIPrefLocalizedString).data.split(",");
}
let charsets = this._charsets;
let currentCharset = getBrowser().docShell.forcedCharset;
if (currentCharset) {
currentCharset = currentCharset.toString();
currentCharset = currentCharset.trim().toLowerCase();
if (charsets.indexOf(currentCharset) == -1)
charsets.splice(0, 0, currentCharset);
}
return this._toMenuItems(charsets, currentCharset);
},
show: function showCharsetMenu() {
this.menu.menupopup.children = this.charsets;
MenuControlHelperUI.show(this.menu);
},
setCharset: function setCharset(aCharset) {
let browser = getBrowser();
browser.messageManager.sendAsyncMessage("Browser:SetCharset", {
charset: aCharset
});
let history = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
history.setCharsetForURI(browser.documentURI, aCharset);
}
};

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

@ -0,0 +1,173 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var FindHelperUI = {
type: "find",
commands: {
next: "cmd_findNext",
previous: "cmd_findPrevious",
close: "cmd_findClose"
},
_open: false,
_status: null,
get status() {
return this._status;
},
set status(val) {
if (val != this._status) {
this._status = val;
if (!val)
this._textbox.removeAttribute("status");
else
this._textbox.setAttribute("status", val);
this.updateCommands(this._textbox.value);
}
},
init: function findHelperInit() {
this._textbox = document.getElementById("find-helper-textbox");
this._container = document.getElementById("content-navigator");
this._cmdPrevious = document.getElementById(this.commands.previous);
this._cmdNext = document.getElementById(this.commands.next);
// Listen for find assistant messages from content
messageManager.addMessageListener("FindAssist:Show", this);
messageManager.addMessageListener("FindAssist:Hide", this);
// Listen for pan events happening on the browsers
Elements.browsers.addEventListener("PanBegin", this, false);
Elements.browsers.addEventListener("PanFinished", this, false);
// Listen for events where form assistant should be closed
Elements.tabList.addEventListener("TabSelect", this, true);
Elements.browsers.addEventListener("URLChanged", this, true);
window.addEventListener("MozContextUIShow", this, true);
window.addEventListener("MozContextUIExpand", this, true);
},
receiveMessage: function findHelperReceiveMessage(aMessage) {
let json = aMessage.json;
switch(aMessage.name) {
case "FindAssist:Show":
ContextUI.dismiss();
this.status = json.result;
if (json.rect)
this._zoom(Rect.fromRect(json.rect));
break;
case "FindAssist:Hide":
if (this._container.getAttribute("type") == this.type)
this.hide();
break;
}
},
handleEvent: function findHelperHandleEvent(aEvent) {
switch (aEvent.type) {
case "MozContextUIShow":
case "MozContextUIExpand":
case "TabSelect":
this.hide();
break;
case "URLChanged":
if (aEvent.detail && aEvent.target == getBrowser())
this.hide();
break;
case "PanBegin":
this._container.style.visibility = "hidden";
this._textbox.collapsed = true;
break;
case "PanFinished":
this._container.style.visibility = "visible";
this._textbox.collapsed = false;
break;
}
},
show: function findHelperShow() {
ContextUI.dismiss();
this._container.show(this);
this.search(this._textbox.value);
this._textbox.select();
this._textbox.focus();
this._open = true;
// Prevent the view to scroll automatically while searching
Browser.selectedBrowser.scrollSync = false;
},
hide: function findHelperHide() {
if (!this._open)
return;
this._textbox.value = "";
this.status = null;
this._textbox.blur();
this._container.hide(this);
this._open = false;
// Restore the scroll synchronisation
Browser.selectedBrowser.scrollSync = true;
},
goToPrevious: function findHelperGoToPrevious() {
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Previous", { });
},
goToNext: function findHelperGoToNext() {
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Next", { });
},
search: function findHelperSearch(aValue) {
this.updateCommands(aValue);
// Don't bother searching if the value is empty
if (aValue == "") {
this.status = null;
return;
}
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Find", { searchString: aValue });
},
updateCommands: function findHelperUpdateCommands(aValue) {
let disabled = (this._status == Ci.nsITypeAheadFind.FIND_NOTFOUND) || (aValue == "");
this._cmdPrevious.setAttribute("disabled", disabled);
this._cmdNext.setAttribute("disabled", disabled);
},
_zoom: function _findHelperZoom(aElementRect) {
let autozoomEnabled = Services.prefs.getBoolPref("findhelper.autozoom");
if (!aElementRect || !autozoomEnabled)
return;
if (Browser.selectedTab.allowZoom) {
let zoomLevel = Browser._getZoomLevelForRect(aElementRect);
// Clamp the zoom level relatively to the default zoom level of the page
let defaultZoomLevel = Browser.selectedTab.getDefaultZoomLevel();
zoomLevel = Util.clamp(zoomLevel, (defaultZoomLevel * kBrowserFormZoomLevelMin),
(defaultZoomLevel * kBrowserFormZoomLevelMax));
zoomLevel = Browser.selectedTab.clampZoomLevel(zoomLevel);
let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
AnimatedZoom.animateTo(zoomRect);
} else {
// Even if zooming is disabled we could need to reposition the view in
// order to keep the element on-screen
let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, getBrowser().scale);
AnimatedZoom.animateTo(zoomRect);
}
}
};

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

@ -0,0 +1,454 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Responsible for filling in form information.
* - When an element is focused, the browser view zooms in to the control.
* - The caret positionning and the view are sync to keep the type
* in text into view for input fields (text/textarea).
* - Provides autocomplete box for input fields.
*/
const kBrowserFormZoomLevelMin = 0.8;
const kBrowserFormZoomLevelMax = 2.0;
// Prefs
const kPrefFormHelperEnabled = "formhelper.enabled";
const kPrefFormHelperMode = "formhelper.mode";
const kPrefFormHelperZoom = "formhelper.autozoom";
const kPrefFormHelperZoomCaret = "formhelper.autozoom.caret";
var FormHelperUI = {
_debugEvents: false,
_currentBrowser: null,
_currentElement: null,
_currentCaretRect: null,
_currentElementRect: null,
type: "form",
get enabled() {
return Services.prefs.getBoolPref(kPrefFormHelperEnabled);
},
init: function formHelperInit() {
// Listen for form assistant messages from content
messageManager.addMessageListener("FormAssist:Show", this);
messageManager.addMessageListener("FormAssist:Hide", this);
messageManager.addMessageListener("FormAssist:Update", this);
messageManager.addMessageListener("FormAssist:Resize", this);
messageManager.addMessageListener("FormAssist:AutoComplete", this);
messageManager.addMessageListener("FormAssist:ValidationMessage", this);
// Listen for events where form assistant should be closed or updated
let tabs = Elements.tabList;
tabs.addEventListener("TabSelect", this, true);
tabs.addEventListener("TabClose", this, true);
Elements.browsers.addEventListener("URLChanged", this, true);
Elements.browsers.addEventListener("SizeChanged", this, true);
// Listen some events to show/hide arrows
Elements.browsers.addEventListener("PanBegin", this, false);
Elements.browsers.addEventListener("PanFinished", this, false);
// Dynamically enabled/disabled the form helper if needed depending on
// the size of the screen
let mode = Services.prefs.getIntPref(kPrefFormHelperMode);
let state = (mode == 2) ? false : !!mode;
Services.prefs.setBoolPref(kPrefFormHelperEnabled, state);
},
/*
* Open of the form helper proper. Currently doesn't display anything
* on metro since the nav buttons are off.
*/
show: function formHelperShow(aElement, aHasPrevious, aHasNext) {
// Delay the operation until all resize operations generated by the
// keyboard apparition are done. This avoid doing unuseful zooming
// operations.
if (aElement.editable &&
(MetroUtils.immersive && !ContentAreaObserver.isKeyboardOpened &&
!InputSourceHelper.isPrecise)) {
this._waitForKeyboard(aElement, aHasPrevious, aHasNext);
return;
}
this._currentBrowser = Browser.selectedBrowser;
this._currentCaretRect = null;
let lastElement = this._currentElement || null;
this._currentElement = {
id: aElement.id,
name: aElement.name,
title: aElement.title,
value: aElement.value,
maxLength: aElement.maxLength,
type: aElement.type,
choices: aElement.choices,
isAutocomplete: aElement.isAutocomplete,
validationMessage: aElement.validationMessage,
list: aElement.list,
rect: aElement.rect
};
this._zoom(Rect.fromRect(aElement.rect), Rect.fromRect(aElement.caretRect));
this._updateContainerForSelect(lastElement, this._currentElement);
this._updatePopupsFor(this._currentElement);
// Prevent the view to scroll automatically while typing
this._currentBrowser.scrollSync = false;
},
hide: function formHelperHide() {
SelectHelperUI.hide();
AutofillMenuUI.hide();
// Restore the scroll synchonisation
if (this._currentBrowser)
this._currentBrowser.scrollSync = true;
// reset current Element and Caret Rect
this._currentElementRect = null;
this._currentCaretRect = null;
this._updateContainerForSelect(this._currentElement, null);
if (this._currentBrowser)
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Closed", { });
},
/*
* Events
*/
handleEvent: function formHelperHandleEvent(aEvent) {
if (this._debugEvents) Util.dumpLn(aEvent.type);
if (!this._open)
return;
switch (aEvent.type) {
case "TabSelect":
case "TabClose":
case "PanBegin":
case "SizeChanged":
this.hide();
break;
case "URLChanged":
if (aEvent.detail && aEvent.target == getBrowser())
this.hide();
break;
}
},
receiveMessage: function formHelperReceiveMessage(aMessage) {
if (this._debugEvents) Util.dumpLn(aMessage.name);
let allowedMessages = ["FormAssist:Show", "FormAssist:Hide",
"FormAssist:AutoComplete", "FormAssist:ValidationMessage"];
if (!this._open && allowedMessages.indexOf(aMessage.name) == -1)
return;
let json = aMessage.json;
switch (aMessage.name) {
case "FormAssist:Show":
// if the user has manually disabled the Form Assistant UI we still
// want to show a UI for <select /> element and still want to show
// autocomplete suggestions but not managed by FormHelperUI
if (this.enabled) {
this.show(json.current, json.hasPrevious, json.hasNext)
} else if (json.current.choices) {
SelectHelperUI.show(json.current.choices, json.current.title, json.current.rect);
} else {
this._currentElementRect = Rect.fromRect(json.current.rect);
this._currentBrowser = getBrowser();
this._updatePopupsFor(json.current);
}
break;
case "FormAssist:Hide":
if (this.enabled) {
this.hide();
}
break;
case "FormAssist:Resize":
if (!ContentAreaObserver.isKeyboardOpened)
return;
let element = json.current;
this._zoom(Rect.fromRect(element.rect), Rect.fromRect(element.caretRect));
break;
case "FormAssist:ValidationMessage":
this._updatePopupsFor(json.current, { fromInput: true });
break;
case "FormAssist:AutoComplete":
this._updatePopupsFor(json.current, { fromInput: true });
break;
case "FormAssist:Update":
if (!ContentAreaObserver.isKeyboardOpened)
return;
//this._zoom(null, Rect.fromRect(json.caretRect));
break;
}
},
doAutoComplete: function formHelperDoAutoComplete(aData) {
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete",
{ value: aData });
},
get _open() {
// XXX we don't have the ability to test zooming
return true;
},
/*
* Update all popups based on the type of form element we are
* dealing with.
*/
_updatePopupsFor: function _formHelperUpdatePopupsFor(aElement, options) {
options = options || {};
let fromInput = 'fromInput' in options && options.fromInput;
// The order of the updates matters here. If the popup update was
// triggered from user input (e.g. key press in an input element),
// we first check if there are input suggestions then check for
// a validation message. The idea here is that the validation message
// will be shown straight away once the invalid element is focused
// and suggestions will be shown as user inputs data. Only one popup
// is shown at a time. If both are not shown, then we ensure any
// previous popups are hidden.
let noPopupsShown = fromInput ?
(!this._updateSuggestionsFor(aElement) &&
!this._updateFormValidationFor(aElement)) :
(!this._updateFormValidationFor(aElement) &&
!this._updateSuggestionsFor(aElement));
if (noPopupsShown) {
AutofillMenuUI.hide();
}
},
/*
* Populates the autofill menu for this element.
*/
_updateSuggestionsFor: function _formHelperUpdateSuggestionsFor(aElement) {
let suggestions = this._getAutocompleteSuggestions(aElement);
if (!suggestions.length)
return false;
// the scrollX/scrollY position can change because of the animated zoom so
// delay the suggestions positioning
/*
if (AnimatedZoom.isZooming()) {
let self = this;
this._waitForZoom(function() {
self._updateSuggestionsFor(aElement);
});
return true;
}
*/
AutofillMenuUI.show(this._currentElementRect, suggestions);
return true;
},
_updateFormValidationFor: function _formHelperUpdateFormValidationFor(aElement) {
if (!aElement.validationMessage)
return false;
/*
// the scrollX/scrollY position can change because of the animated zoom so
// delay the suggestions positioning
if (AnimatedZoom.isZooming()) {
let self = this;
this._waitForZoom(function() {
self._updateFormValidationFor(aElement);
});
return true;
}
let validationContainer = document.getElementById("form-helper-validation-container");
// Update label with form validation message
validationContainer.firstChild.value = aElement.validationMessage;
ContentPopupHelper.popup = validationContainer;
ContentPopupHelper.anchorTo(this._currentElementRect);
*/
return false;
},
/*
* Retrieve the autocomplete list from the autocomplete service for an element
*/
_getAutocompleteSuggestions: function _formHelperGetAutocompleteSuggestions(aElement) {
if (!aElement.isAutocomplete) {
return [];
}
let suggestions = [];
let autocompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete);
let results = autocompleteService.autoCompleteSearch(aElement.name || aElement.id, aElement.value, aElement, null);
if (results.matchCount > 0) {
for (let i = 0; i < results.matchCount; i++) {
let value = results.getValueAt(i);
// Do not show the value if it is the current one in the input field
if (value == aElement.value)
continue;
suggestions.push({ "label": value, "value": value});
}
}
// Add the datalist elements provided by the website, note that the
// displayed value can differ from the real value of the element.
let options = aElement.list;
for (let i = 0; i < options.length; i++)
suggestions.push(options[i]);
return suggestions;
},
/*
* Setup for displaying the selection choices menu
*/
_updateContainerForSelect: function _formHelperUpdateContainerForSelect(aLastElement, aCurrentElement) {
let lastHasChoices = aLastElement && (aLastElement.choices != null);
let currentHasChoices = aCurrentElement && (aCurrentElement.choices != null);
if (currentHasChoices)
SelectHelperUI.show(aCurrentElement.choices, aCurrentElement.title, aCurrentElement.rect);
else if (lastHasChoices)
SelectHelperUI.hide();
},
/*
* Zoom and move viewport so that element is legible and touchable.
*/
_zoom: function _formHelperZoom(aElementRect, aCaretRect) {
let browser = getBrowser();
let zoomRect = Rect.fromRect(browser.getBoundingClientRect());
this._currentElementRect = aElementRect;
// Zoom to a specified Rect
let autozoomEnabled = Services.prefs.getBoolPref(kPrefFormHelperZoom);
if (aElementRect && Browser.selectedTab.allowZoom && autozoomEnabled) {
this._currentElementRect = aElementRect;
// Zoom to an element by keeping the caret into view
let zoomLevel = Browser.selectedTab.clampZoomLevel(this._getZoomLevelForRect(aElementRect));
zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
AnimatedZoom.animateTo(zoomRect);
} else if (aElementRect && !Browser.selectedTab.allowZoom && autozoomEnabled) {
this._currentElementRect = aElementRect;
// Even if zooming is disabled we could need to reposition the view in
// order to keep the element on-screen
zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, browser.scale);
AnimatedZoom.animateTo(zoomRect);
}
this._ensureCaretVisible(aCaretRect);
},
_ensureCaretVisible: function _ensureCaretVisible(aCaretRect) {
if (!aCaretRect || !Services.prefs.getBoolPref(kPrefFormHelperZoomCaret))
return;
// the scrollX/scrollY position can change because of the animated zoom so
// delay the caret adjustment
if (AnimatedZoom.isZooming()) {
let self = this;
this._waitForZoom(function() {
self._ensureCaretVisible(aCaretRect);
});
return;
}
let browser = getBrowser();
let zoomRect = Rect.fromRect(browser.getBoundingClientRect());
this._currentCaretRect = aCaretRect;
let caretRect = aCaretRect.clone().scale(browser.scale, browser.scale);
let scroll = browser.getRootView().getPosition();
zoomRect = new Rect(scroll.x, scroll.y, zoomRect.width, zoomRect.height);
if (zoomRect.contains(caretRect))
return;
let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
if (deltaX != 0 || deltaY != 0) {
let view = browser.getRootView();
view.scrollBy(deltaX, deltaY);
}
},
_waitForZoom: function _formHelperWaitForZoom(aCallback) {
let currentElement = this._currentElement;
let self = this;
window.addEventListener("AnimatedZoomEnd", function() {
window.removeEventListener("AnimatedZoomEnd", arguments.callee, true);
// Ensure the current element has not changed during this interval
if (self._currentElement != currentElement)
return;
aCallback();
}, true);
},
_getZoomLevelForRect: function _getZoomLevelForRect(aRect) {
const margin = 30;
let zoomLevel = getBrowser().getBoundingClientRect().width / (aRect.width + margin);
// Clamp the zoom level relatively to the default zoom level of the page
let defaultZoomLevel = Browser.selectedTab.getDefaultZoomLevel();
return Util.clamp(zoomLevel, (defaultZoomLevel * kBrowserFormZoomLevelMin),
(defaultZoomLevel * kBrowserFormZoomLevelMax));
},
_getOffsetForCaret: function _formHelperGetOffsetForCaret(aCaretRect, aRect) {
// Determine if we need to move left or right to bring the caret into view
let deltaX = 0;
if (aCaretRect.right > aRect.right)
deltaX = aCaretRect.right - aRect.right;
if (aCaretRect.left < aRect.left)
deltaX = aCaretRect.left - aRect.left;
// Determine if we need to move up or down to bring the caret into view
let deltaY = 0;
if (aCaretRect.bottom > aRect.bottom)
deltaY = aCaretRect.bottom - aRect.bottom;
if (aCaretRect.top < aRect.top)
deltaY = aCaretRect.top - aRect.top;
return [deltaX, deltaY];
},
_waitForKeyboard: function formHelperWaitForKeyboard(aElement, aHasPrevious, aHasNext) {
let self = this;
window.addEventListener("KeyboardChanged", function(aEvent) {
window.removeEventListener("KeyboardChanged", arguments.callee, false);
if (AnimatedZoom.isZooming()) {
self._waitForZoom(function() {
self.show(aElement, aHasPrevious, aHasNext);
});
return;
}
self.show(aElement, aHasPrevious, aHasNext);
}, false);
}
};

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

@ -0,0 +1,289 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Utility class to handle manipulations of the identity indicators in the UI
*/
var IdentityUI = {
// Mode strings used to control CSS display
IDENTITY_MODE_IDENTIFIED : "verifiedIdentity", // High-quality identity information
IDENTITY_MODE_DOMAIN_VERIFIED : "verifiedDomain", // Minimal SSL CA-signed domain verification
IDENTITY_MODE_UNKNOWN : "unknownIdentity", // No trusted identity information
// Cache the most recent SSLStatus and Location seen in checkIdentity
_lastStatus : null,
_lastLocation : null,
/**
* Build out a cache of the elements that we need frequently.
*/
_cacheElements: function() {
this._identityBox = document.getElementById("identity-box");
this._identityPopup = document.getElementById("identity-container");
this._identityPopupContentBox = document.getElementById("identity-popup-content-box");
this._identityPopupContentHost = document.getElementById("identity-popup-content-host");
this._identityPopupContentOwner = document.getElementById("identity-popup-content-owner");
this._identityPopupContentSupp = document.getElementById("identity-popup-content-supplemental");
this._identityPopupContentVerif = document.getElementById("identity-popup-content-verifier");
this._identityPopupEncLabel = document.getElementById("identity-popup-encryption-label");
this._identityIcon = document.getElementById("identity-icon");
this._identityIconLabel = document.getElementById("identity-icon-label");
this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
},
init: function init() {
this._staticStrings = {};
this._staticStrings[this.IDENTITY_MODE_DOMAIN_VERIFIED] = {
encryption_label: Strings.browser.GetStringFromName("identity.encrypted2")
};
this._staticStrings[this.IDENTITY_MODE_IDENTIFIED] = {
encryption_label: Strings.browser.GetStringFromName("identity.encrypted2")
};
this._staticStrings[this.IDENTITY_MODE_UNKNOWN] = {
encryption_label: Strings.browser.GetStringFromName("identity.unencrypted2")
};
// Close the popup when reloading the page
Elements.browsers.addEventListener("URLChanged", this, true);
this._cacheElements();
},
getIdentityData: function() {
return this._lastStatus.serverCert;
},
/**
* Determine the identity of the page being displayed by examining its SSL cert
* (if available) and, if necessary, update the UI to reflect this.
*/
checkIdentity: function() {
let browser = getBrowser();
let state = browser.securityUI.state;
let location = browser.currentURI;
let currentStatus = browser.securityUI.SSLStatus;
this._lastStatus = currentStatus;
this._lastLocation = {};
try {
// make a copy of the passed in location to avoid cycles
this._lastLocation = { host: location.hostPort, hostname: location.host, port: location.port == -1 ? "" : location.port};
} catch(e) { }
if (state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
this.setMode(this.IDENTITY_MODE_IDENTIFIED);
else if (state & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
else
this.setMode(this.IDENTITY_MODE_UNKNOWN);
},
/**
* Return the eTLD+1 version of the current hostname
*/
getEffectiveHost: function() {
// Cache the eTLDService if this is our first time through
if (!this._eTLDService)
this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
.getService(Ci.nsIEffectiveTLDService);
try {
return this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
} catch (e) {
// If something goes wrong (e.g. hostname is an IP address) just fail back
// to the full domain.
return this._lastLocation.hostname;
}
},
/**
* Update the UI to reflect the specified mode, which should be one of the
* IDENTITY_MODE_* constants.
*/
setMode: function(newMode) {
this._identityBox.setAttribute("mode", newMode);
this.setIdentityMessages(newMode);
// Update the popup too, if it's open
if (!this._identityPopup.hidden)
this.setPopupMessages(newMode);
},
/**
* Set up the messages for the primary identity UI based on the specified mode,
* and the details of the SSL cert, where applicable
*
* @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
*/
setIdentityMessages: function(newMode) {
let strings = Strings.browser;
let tooltip = "";
let icon_country_label = "";
let icon_labels_dir = "ltr";
let icon_label = "";
if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
var iData = this.getIdentityData();
// We need a port number for all lookups. If one hasn't been specified, use
// the https default
var lookupHost = this._lastLocation.host;
if (lookupHost.indexOf(':') < 0)
lookupHost += ":443";
// Cache the override service the first time we need to check it
if (!this._overrideService)
this._overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(Ci.nsICertOverrideService);
// Verifier is either the CA Org, for a normal cert, or a special string
// for certs that are trusted because of a security exception.
tooltip = strings.formatStringFromName("identity.identified.verifier", [iData.caOrg], 1);
// Check whether this site is a security exception.
if (iData.isException)
tooltip = strings.GetStringFromName("identity.identified.verified_by_you");
}
else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
// If it's identified, then we can populate the dialog with credentials
iData = this.getIdentityData();
tooltip = strings.formatStringFromName("identity.identified.verifier", [iData.caOrg], 1);
icon_label = iData.subjectOrg;
if (iData.country)
icon_country_label = "(" + iData.country + ")";
// If the organization name starts with an RTL character, then
// swap the positions of the organization and country code labels.
// The Unicode ranges reflect the definition of the UCS2_CHAR_IS_BIDI
// macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
// fixed, this test should be replaced by one adhering to the
// Unicode Bidirectional Algorithm proper (at the paragraph level).
icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/.test(icon_label) ?
"rtl" : "ltr";
}
else {
tooltip = strings.GetStringFromName("identity.unknown.tooltip");
}
// Push the appropriate strings out to the UI
this._identityBox.tooltipText = tooltip;
this._identityIconLabel.value = icon_label;
this._identityIconCountryLabel.value = icon_country_label;
// Set cropping and direction
this._identityIconLabel.crop = icon_country_label ? "end" : "center";
this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
// Hide completely if the organization label is empty
this._identityIconLabel.parentNode.collapsed = icon_label ? false : true;
},
/**
* Set up the title and content messages for the identity message popup,
* based on the specified mode, and the details of the SSL cert, where
* applicable
*
* @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
*/
setPopupMessages: function(newMode) {
this._identityPopup.setAttribute("mode", newMode);
this._identityPopupContentBox.className = newMode;
// Set the static strings up front
this._identityPopupEncLabel.textContent = this._staticStrings[newMode].encryption_label;
// Initialize the optional strings to empty values
var supplemental = "";
var verifier = "";
if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
var iData = this.getIdentityData();
var host = this.getEffectiveHost();
var owner = Strings.browser.GetStringFromName("identity.ownerUnknown2");
verifier = this._identityBox.tooltipText;
supplemental = "";
}
else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
// If it's identified, then we can populate the dialog with credentials
iData = this.getIdentityData();
host = this.getEffectiveHost();
owner = iData.subjectOrg;
verifier = this._identityBox.tooltipText;
// Build an appropriate supplemental block out of whatever location data we have
if (iData.city)
supplemental += iData.city + "\n";
if (iData.state && iData.country)
supplemental += Strings.browser.formatStringFromName("identity.identified.state_and_country", [iData.state, iData.country], 2);
else if (iData.state) // State only
supplemental += iData.state;
else if (iData.country) // Country only
supplemental += iData.country;
} else {
// These strings will be hidden in CSS anyhow
host = "";
owner = "";
}
// Push the appropriate strings out to the UI
this._identityPopupContentHost.textContent = host;
this._identityPopupContentOwner.textContent = owner;
this._identityPopupContentSupp.textContent = supplemental;
this._identityPopupContentVerif.textContent = verifier;
PageActions.updateSiteMenu();
},
show: function ih_show() {
BrowserUI.showContent();
Elements.contentShowing.setAttribute("disabled", "true");
// Update the popup strings
this.setPopupMessages(this._identityBox.getAttribute("mode") || this.IDENTITY_MODE_UNKNOWN);
this._identityPopup.hidden = false;
this._identityBox.setAttribute("open", "true");
DialogUI.pushPopup(this, [this._identityPopup, this._identityBox, Elements.tray]);
this._identityPopup.anchorTo(this._identityBox, "after_start");
},
hide: function ih_hide() {
Elements.contentShowing.setAttribute("disabled", "false");
this._identityPopup.hidden = true;
this._identityBox.removeAttribute("open");
DialogUI.popPopup(this);
},
toggle: function ih_toggle() {
if (this._identityPopup.hidden)
this.show();
else
this.hide();
},
/**
* Click handler for the identity-box element in primary chrome.
*/
handleIdentityButtonEvent: function(aEvent) {
aEvent.stopPropagation();
if ((aEvent.type == "click" && aEvent.button != 0) ||
(aEvent.type == "keypress" && aEvent.charCode != KeyEvent.DOM_VK_SPACE &&
aEvent.keyCode != KeyEvent.DOM_VK_RETURN))
return; // Left click, space or enter only
this.toggle();
},
handleEvent: function(aEvent) {
if (aEvent.type == "URLChanged" && aEvent.target == Browser.selectedBrowser && !this._identityPopup.hidden)
this.hide();
}
};

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

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Helper class for IndexedDB, parent part. Listens to
* messages from the child and shows prompts for them.
*/
let IndexedDB = {
_permissionsPrompt: "indexedDB-permissions-prompt",
_permissionsResponse: "indexedDB-permissions-response",
_quotaPrompt: "indexedDB-quota-prompt",
_quotaResponse: "indexedDB-quota-response",
_quotaCancel: "indexedDB-quota-cancel",
_notificationIcon: "indexedDB-notification-icon",
receiveMessage: function(aMessage) {
switch (aMessage.name) {
case "IndexedDB:Prompt":
this.showPrompt(aMessage);
}
},
showPrompt: function(aMessage) {
let browser = aMessage.target;
let payload = aMessage.json;
let host = payload.host;
let topic = payload.topic;
let type;
if (topic == this._permissionsPrompt) {
type = "indexedDB";
payload.responseTopic = this._permissionsResponse;
} else if (topic == this._quotaPrompt) {
type = "indexedDBQuota";
payload.responseTopic = this._quotaResponse;
} else if (topic == this._quotaCancel) {
payload.permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
// XXX Need to actually save this?
return;
}
let prompt = Cc["@mozilla.org/content-permission/prompt;1"].createInstance(Ci.nsIContentPermissionPrompt);
// If the user waits a long time before responding, we default to UNKNOWN_ACTION.
let timeoutId = setTimeout(function() {
payload.permission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
timeoutId = null;
}, 30000);
function checkTimeout() {
if (timeoutId === null) return true;
clearTimeout(timeoutId);
timeoutId = null;
return false;
}
prompt.prompt({
type: type,
uri: Services.io.newURI(payload.location, null, null),
window: null,
element: aMessage.target,
cancel: function() {
if (checkTimeout()) return;
payload.permission = Ci.nsIPermissionManager.DENY_ACTION;
browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
},
allow: function() {
if (checkTimeout()) return;
payload.permission = Ci.nsIPermissionManager.ALLOW_ACTION;
browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
},
});
},
};

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

@ -0,0 +1,129 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var MasterPasswordUI = {
_dialog: null,
_tokenName: "",
get _secModuleDB() {
delete this._secModuleDB;
return this._secModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(Ci.nsIPKCS11ModuleDB);
},
get _pk11DB() {
delete this._pk11DB;
return this._pk11DB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(Ci.nsIPK11TokenDB);
},
_setPassword: function _setPassword(password) {
try {
let status;
let slot = this._secModuleDB.findSlotByName(this._tokenName);
if (slot)
status = slot.status;
else
return false;
let token = this._pk11DB.findTokenByName(this._tokenName);
if (status == Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED) {
token.initPassword(password);
} else if (status == Ci.nsIPKCS11Slot.SLOT_READY) {
token.changePassword("", password);
}
return true;
} catch(e) {
dump("--- MasterPasswordUI._setPassword exception: " + e + "\n");
return false;
}
},
_removePassword: function _removePassword(password) {
try {
let token = this._pk11DB.getInternalKeyToken();
if (token.checkPassword(password)) {
token.changePassword(password, "");
return true;
}
} catch(e) {
dump("--- MasterPasswordUI._removePassword exception: " + e + "\n");
}
return false;
},
show: function mp_show(aSet) {
let dialogId = aSet ? "masterpassword-change" : "masterpassword-remove";
if (document.getElementById(dialogId))
return;
let dialog = aSet ? "chrome://browser/content/prompt/masterPassword.xul"
: "chrome://browser/content/prompt/removeMasterPassword.xul";
this._dialog = DialogUI.importModal(window, dialog, null);
DialogUI.pushPopup(this, this._dialog);
if (aSet) {
this.checkPassword();
document.getElementById("masterpassword-newpassword1").focus();
} else {
document.getElementById("masterpassword-oldpassword").focus();
}
},
hide: function mp_hide(aValue) {
this.updatePreference();
this._dialog.close();
this._dialog = null;
DialogUI.popPopup(this);
},
setPassword: function mp_setPassword() {
if (!this.checkPassword())
return;
let newPasswordValue = document.getElementById("masterpassword-newpassword1").value;
if (this._setPassword(newPasswordValue)) {
this.hide();
}
},
removePassword: function mp_removePassword() {
let oldPassword = document.getElementById("masterpassword-oldpassword").value;
if (this._removePassword(oldPassword)) {
this.hide();
}
},
checkPassword: function mp_checkPassword() {
let newPasswordValue1 = document.getElementById("masterpassword-newpassword1").value;
let newPasswordValue2 = document.getElementById("masterpassword-newpassword2").value;
let buttonOk = this._dialog.getElementsByAttribute("class", "prompt-buttons")[0].firstChild;
let isPasswordValid = this._secModuleDB.isFIPSEnabled ? (newPasswordValue1 != "" && newPasswordValue1 == newPasswordValue2)
: (newPasswordValue1 == newPasswordValue2);
buttonOk.setAttribute("disabled", !isPasswordValid);
return isPasswordValid;
},
checkOldPassword: function mp_checkOldPassword() {
let oldPassword = document.getElementById("masterpassword-oldpassword");
let buttonOk = this._dialog.getElementsByAttribute("class", "prompt-buttons")[0].firstChild;
let isPasswordValid = this._pk11DB.getInternalKeyToken().checkPassword(oldPassword.value);
buttonOk.setAttribute("disabled", !isPasswordValid);
},
hasMasterPassword: function mp_hasMasterPassword() {
let slot = this._secModuleDB.findSlotByName(this._tokenName);
if (slot) {
let status = slot.status;
return status != Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED && status != Ci.nsIPKCS11Slot.SLOT_READY;
}
return false;
},
updatePreference: function mp_updatePreference() {
document.getElementById("prefs-master-password").value = this.hasMasterPassword();
}
};

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

@ -0,0 +1,438 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Positioning buffer enforced between the edge of a context menu
// and the edge of the screen.
const kPositionPadding = 10;
var AutofillMenuUI = {
_popupState: null,
__menuPopup: null,
get _panel() { return document.getElementById("autofill-container"); },
get _popup() { return document.getElementById("autofill-popup"); },
get _commands() { return this._popup.childNodes[0]; },
get _menuPopup() {
if (!this.__menuPopup)
this.__menuPopup = new MenuPopup(this._panel, this._popup);
this.__menuPopup._wantTypeBehind = true;
return this.__menuPopup;
},
_firePopupEvent: function _firePopupEvent(aEventName) {
let menupopup = this._currentControl.menupopup;
if (menupopup.hasAttribute(aEventName)) {
let func = new Function("event", menupopup.getAttribute(aEventName));
func.call(this);
}
},
_emptyCommands: function _emptyCommands() {
while (this._commands.firstChild)
this._commands.removeChild(this._commands.firstChild);
},
_positionOptions: function _positionOptions() {
let options = {
forcePosition: true
};
options.xPos = this._anchorRect.x;
options.yPos = this._anchorRect.y + this._anchorRect.height;
options.bottomAligned = false;
options.leftAligned = true;
options.source = Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH;
return options;
},
show: function show(aAnchorRect, aSuggestionsList) {
this._anchorRect = aAnchorRect;
this._emptyCommands();
for (let idx = 0; idx < aSuggestionsList.length; idx++) {
let item = document.createElement("richlistitem");
let label = document.createElement("label");
label.setAttribute("value", aSuggestionsList[idx].label);
item.setAttribute("data", aSuggestionsList[idx].value);
item.appendChild(label);
this._commands.appendChild(item);
}
this._menuPopup.show(this._positionOptions());
},
selectByIndex: function mn_selectByIndex(aIndex) {
this._menuPopup.hide();
FormHelperUI.doAutoComplete(this._commands.childNodes[aIndex].getAttribute("data"));
},
hide: function hide () {
this._menuPopup.hide();
}
};
var ContextMenuUI = {
_popupState: null,
__menuPopup: null,
get _panel() { return document.getElementById("context-container"); },
get _popup() { return document.getElementById("context-popup"); },
get _commands() { return this._popup.childNodes[0]; },
get _menuPopup() {
if (!this.__menuPopup)
this.__menuPopup = new MenuPopup(this._panel, this._popup);
return this.__menuPopup;
},
/*******************************************
* External api
*/
/*
* popupState - return the json object for this context. Called
* by context command to invoke actions on the target.
*/
get popupState() {
return this._popupState;
},
/*
* showContextMenu - display a context sensitive menu based
* on the data provided in a json data structure.
*
* @param aMessage data structure containing information about
* the context.
* aMessage.json - json data structure described below.
* aMessage.target - target element on which to evoke
*
* @returns true if the context menu was displayed,
* false otherwise.
*
* json: TBD
*/
showContextMenu: function ch_showContextMenu(aMessage) {
this._popupState = aMessage.json;
this._popupState.target = aMessage.target;
let contentTypes = this._popupState.types;
let optionsAvailable = false;
for (let i = 0; i < this._commands.childElementCount; i++) {
let command = this._commands.childNodes[i];
command.hidden = true;
let types = command.getAttribute("type").split(/\s+/);
for (let i = 0; i < types.length; i++) {
if (contentTypes.indexOf(types[i]) != -1) {
optionsAvailable = true;
command.hidden = false;
break;
}
}
}
if (!optionsAvailable) {
this._popupState = null;
return false;
}
this._menuPopup.show(this._popupState);
let event = document.createEvent("Events");
event.initEvent("CancelTouchSequence", true, false);
if (this._popupState.target) {
this._popupState.target.dispatchEvent(event);
}
return true;
},
hide: function hide () {
this._menuPopup.hide();
this._popupState = null;
},
reset: function reset() {
this._popupState = null;
}
};
var MenuControlUI = {
_currentControl: null,
__menuPopup: null,
get _panel() { return document.getElementById("menucontrol-container"); },
get _popup() { return document.getElementById("menucontrol-popup"); },
get _commands() { return this._popup.childNodes[0]; },
get _menuPopup() {
if (!this.__menuPopup)
this.__menuPopup = new MenuPopup(this._panel, this._popup);
return this.__menuPopup;
},
_firePopupEvent: function _firePopupEvent(aEventName) {
let menupopup = this._currentControl.menupopup;
if (menupopup.hasAttribute(aEventName)) {
let func = new Function("event", menupopup.getAttribute(aEventName));
func.call(this);
}
},
_emptyCommands: function _emptyCommands() {
while (this._commands.firstChild)
this._commands.removeChild(this._commands.firstChild);
},
_positionOptions: function _positionOptions() {
let position = this._currentControl.menupopup.position || "after_start";
let rect = this._currentControl.getBoundingClientRect();
let options = {
forcePosition: true
};
// TODO: Detect text direction and flip for RTL.
switch (position) {
case "before_start":
options.xPos = rect.left;
options.yPos = rect.top;
options.bottomAligned = true;
options.leftAligned = true;
break;
case "before_end":
options.xPos = rect.right;
options.yPos = rect.top;
options.bottomAligned = true;
options.rightAligned = true;
break;
case "after_start":
options.xPos = rect.left;
options.yPos = rect.bottom;
options.topAligned = true;
options.leftAligned = true;
break;
case "after_end":
options.xPos = rect.right;
options.yPos = rect.bottom;
options.topAligned = true;
options.rightAligned = true;
break;
// TODO: Support other popup positions.
}
return options;
},
show: function show(aMenuControl) {
this._currentControl = aMenuControl;
this._panel.setAttribute("for", aMenuControl.id);
this._firePopupEvent("onpopupshowing");
this._emptyCommands();
let children = this._currentControl.menupopup.children;
for (let i = 0; i < children.length; i++) {
let child = children[i];
let item = document.createElement("richlistitem");
if (child.disabled)
item.setAttribute("disabled", "true");
if (child.hidden)
item.setAttribute("hidden", "true");
// Add selected as a class name instead of an attribute to not being overidden
// by the richlistbox behavior (it sets the "current" and "selected" attribute
if (child.selected)
item.setAttribute("class", "selected");
let image = document.createElement("image");
image.setAttribute("src", child.image || "");
item.appendChild(image);
let label = document.createElement("label");
label.setAttribute("value", child.label);
item.appendChild(label);
this._commands.appendChild(item);
}
this._menuPopup.show(this._positionOptions());
},
selectByIndex: function mn_selectByIndex(aIndex) {
this._currentControl.selectedIndex = aIndex;
// Dispatch a xul command event to the attached menulist
if (this._currentControl.dispatchEvent) {
let evt = document.createEvent("XULCommandEvent");
evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
this._currentControl.dispatchEvent(evt);
}
this._menuPopup.hide();
}
};
function MenuPopup(aPanel, aPopup) {
this._panel = aPanel;
this._popup = aPopup;
this._wantTypeBehind = false;
}
MenuPopup.prototype = {
get _visible() { return !this._panel.hidden; },
get _commands() { return this._popup.childNodes[0]; },
show: function (aPositionOptions) {
if (this._visible)
return;
window.addEventListener("keypress", this, true);
window.addEventListener("mousedown", this, true);
this._panel.hidden = false;
this._position(aPositionOptions || {});
let self = this;
this._panel.addEventListener("transitionend", function () {
self._panel.removeEventListener("transitionend", arguments.callee);
self._panel.removeAttribute("showingfrom");
});
let popupFrom = (aPositionOptions.forcePosition && !aPositionOptions.bottomAligned) ? "above" : "below";
this._panel.setAttribute("showingfrom", popupFrom);
// Ensure the panel actually gets shifted before getting animated
setTimeout(function () {
self._panel.setAttribute("showing", "true");
}, 0);
},
hide: function () {
if (!this._visible)
return;
window.removeEventListener("keypress", this, true);
window.removeEventListener("mousedown", this, true);
let self = this;
this._panel.addEventListener("transitionend", function () {
self._panel.removeEventListener("transitionend", arguments.callee);
self._panel.hidden = true;
self._popupState = null;
});
this._panel.removeAttribute("showing");
},
_position: function _position(aPositionOptions) {
let aX = aPositionOptions.xPos;
let aY = aPositionOptions.yPos;
let aSource = aPositionOptions.source;
let forcePosition = aPositionOptions.forcePosition || false;
let isRightAligned = aPositionOptions.rightAligned || false;
let isBottomAligned = aPositionOptions.bottomAligned || false;
let width = this._popup.boxObject.width;
let height = this._popup.boxObject.height;
let halfWidth = width / 2;
let halfHeight = height / 2;
let screenWidth = ContentAreaObserver.width;
let screenHeight = ContentAreaObserver.height;
if (forcePosition) {
if (isRightAligned)
aX -= width;
if (isBottomAligned)
aY -= height;
} else {
let leftHand = MetroUtils.handPreference == MetroUtils.handPreferenceLeft;
// Add padding on the side of the menu per the user's hand preference
if (aSource && aSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
if (leftHand) {
this._commands.setAttribute("left-hand", true);
this._commands.removeAttribute("right-hand");
} else {
this._commands.setAttribute("right-hand", true);
this._commands.removeAttribute("left-hand");
}
}
let hLeft = (aX - halfWidth - width - kPositionPadding) > kPositionPadding;
let hRight = (aX + width + kPositionPadding) < screenWidth;
let hCenter = (aX - halfWidth - kPositionPadding) > kPositionPadding;
let vTop = (aY - height - kPositionPadding) > kPositionPadding;
let vCenter = (aY - halfHeight - kPositionPadding) > kPositionPadding &&
aY + halfHeight < screenHeight;
let vBottom = (aY + height + kPositionPadding) < screenHeight;
if (leftHand && hLeft && vCenter) {
dump('leftHand && hLeft && vCenter\n');
aX -= (width + halfWidth);
aY -= halfHeight;
} else if (!leftHand && hRight && vCenter) {
dump('!leftHand && hRight && vCenter\n');
aX += kPositionPadding;
aY -= halfHeight;
} else if (vBottom && hCenter) {
dump('vBottom && hCenter\n');
aX -= halfWidth;
} else if (vTop && hCenter) {
dump('vTop && hCenter\n');
aX -= halfWidth;
aY -= height;
} else if (hCenter && vCenter) {
dump('hCenter && vCenter\n');
aX -= halfWidth;
aY -= halfHeight;
} else {
dump('None, left hand: ' + leftHand + '!\n');
}
}
if (aX < 0)
aX = 0;
if (aY < 0)
aY = 0;
this._panel.left = aX;
this._panel.top = aY;
let excessY = (aY + height + kPositionPadding - screenHeight);
this._popup.style.maxHeight = (excessY > 0) ? (height - excessY) + "px" : "none";
let excessX = (aX + width + kPositionPadding - screenWidth);
this._popup.style.maxWidth = (excessX > 0) ? (width - excessX) + "px" : "none";
},
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "keypress":
if (!this._wantTypeBehind) {
// Hide the context menu so you can't type behind it.
aEvent.stopPropagation();
aEvent.preventDefault();
if (aEvent.keyCode != aEvent.DOM_VK_ESCAPE)
this.hide();
}
break;
case "mousedown":
if (!this._popup.contains(aEvent.target)) {
aEvent.stopPropagation();
this.hide();
}
break;
}
}
};

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

@ -0,0 +1,85 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Provide supports for Offline Applications
*/
var OfflineApps = {
offlineAppRequested: function(aRequest, aTarget) {
if (!Services.prefs.getBoolPref("browser.offline-apps.notify"))
return;
let currentURI = Services.io.newURI(aRequest.location, aRequest.charset, null);
// don't bother showing UI if the user has already made a decision
if (Services.perms.testExactPermission(currentURI, "offline-app") != Ci.nsIPermissionManager.UNKNOWN_ACTION)
return;
try {
if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
// all pages can use offline capabilities, no need to ask the user
return;
}
} catch(e) {
// this pref isn't set by default, ignore failures
}
let host = currentURI.asciiHost;
let notificationID = "offline-app-requested-" + host;
let notificationBox = Browser.getNotificationBox(aTarget);
let notification = notificationBox.getNotificationWithValue(notificationID);
let strings = Strings.browser;
if (notification) {
notification.documents.push(aRequest);
} else {
let buttons = [{
label: strings.GetStringFromName("offlineApps.allow"),
accessKey: null,
callback: function() {
for (let i = 0; i < notification.documents.length; i++)
OfflineApps.allowSite(notification.documents[i], aTarget);
}
},{
label: strings.GetStringFromName("offlineApps.never"),
accessKey: null,
callback: function() {
for (let i = 0; i < notification.documents.length; i++)
OfflineApps.disallowSite(notification.documents[i]);
}
},{
label: strings.GetStringFromName("offlineApps.notNow"),
accessKey: null,
callback: function() { /* noop */ }
}];
const priority = notificationBox.PRIORITY_INFO_LOW;
let message = strings.formatStringFromName("offlineApps.available2", [host], 1);
notification = notificationBox.appendNotification(message, notificationID, "", priority, buttons);
notification.documents = [aRequest];
}
},
allowSite: function(aRequest, aTarget) {
let currentURI = Services.io.newURI(aRequest.location, aRequest.charset, null);
Services.perms.add(currentURI, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
// When a site is enabled while loading, manifest resources will start
// fetching immediately. This one time we need to do it ourselves.
// The update must be started on the content process.
aTarget.messageManager.sendAsyncMessage("Browser:MozApplicationCache:Fetch", aRequest);
},
disallowSite: function(aRequest) {
let currentURI = Services.io.newURI(aRequest.location, aRequest.charset, null);
Services.perms.add(currentURI, "offline-app", Ci.nsIPermissionManager.DENY_ACTION);
},
receiveMessage: function receiveMessage(aMessage) {
if (aMessage.name == "Browser:MozApplicationManifest") {
this.offlineAppRequested(aMessage.json, aMessage.target);
}
}
};

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

@ -0,0 +1,157 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* SelectHelperUI: Provides an interface for making a choice in a list.
* Supports simultaneous selection of choices and group headers.
*/
var SelectHelperUI = {
_list: null,
get _container() {
delete this._container;
return this._container = document.getElementById("select-container");
},
get _listbox() {
delete this._listbox;
return this._listbox = document.getElementById("select-commands");
},
get _menuPopup() {
let popup = document.getElementById("select-popup");
delete this._menuPopup;
return this._menuPopup = new MenuPopup(this._container, popup);
},
show: function selectHelperShow(aList, aTitle, aRect) {
if (AnimatedZoom.isZooming()) {
FormHelperUI._waitForZoom(this.show.bind(this, aList, aTitle, aRect));
return;
}
if (this._list)
this.reset();
this._list = aList;
// The element label is used as a title to give more context
this._container.setAttribute("multiple", aList.multiple ? "true" : "false");
let firstSelected = null;
// Using a fragment prevent us to hang on huge list
let fragment = document.createDocumentFragment();
let choices = aList.choices;
for (let i = 0; i < choices.length; i++) {
let choice = choices[i];
let item = document.createElement("listitem");
item.setAttribute("class", "option-command listitem-iconic action-button");
item.setAttribute("flex", "1");
item.setAttribute("crop", "center");
item.setAttribute("label", choice.text);
choice.selected ? item.classList.add("selected")
: item.classList.remove("selected");
choice.disabled ? item.setAttribute("disabled", "true")
: item.removeAttribute("disabled");
fragment.appendChild(item);
if (choice.group) {
item.classList.add("optgroup");
continue;
}
item.optionIndex = choice.optionIndex;
item.choiceIndex = i;
if (choice.inGroup)
item.classList.add("in-optgroup");
if (choice.selected) {
item.classList.add("selected");
firstSelected = firstSelected || item;
}
}
this._listbox.appendChild(fragment);
this._container.addEventListener("click", this, false);
this._menuPopup.show(this._positionOptions(aRect));
this._listbox.ensureElementIsVisible(firstSelected);
},
reset: function selectHelperReset() {
this._updateControl();
while (this._listbox.hasChildNodes())
this._listbox.removeChild(this._listbox.lastChild);
this._list = null;
},
hide: function selectHelperHide() {
if (!this._list)
return;
this._container.removeEventListener("click", this, false);
this._menuPopup.hide();
this.reset();
},
_positionOptions: function _positionOptions(aRect) {
let browser = Browser.selectedBrowser;
let p0 = browser.transformBrowserToClient(aRect.left, aRect.top);
let p1 = browser.transformBrowserToClient(aRect.right, aRect.bottom);
return {
forcePosition: true,
xPos: p0.x,
yPos: p1.y,
bottomAligned: false,
leftAligned: true
};
},
_forEachOption: function _selectHelperForEachOption(aCallback) {
let children = this._listbox.childNodes;
for (let i = 0; i < children.length; i++) {
let item = children[i];
if (!item.hasOwnProperty("optionIndex"))
continue;
aCallback(item, i);
}
},
_updateControl: function _selectHelperUpdateControl() {
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
},
handleEvent: function selectHelperHandleEvent(aEvent) {
switch (aEvent.type) {
case "click":
let item = aEvent.target;
if (item && item.hasOwnProperty("optionIndex")) {
if (this._list.multiple) {
item.classList.toggle("selected");
} else {
item.classList.add("selected");
}
this.onSelect(item.optionIndex, item.classList.contains("selected"));
}
break;
}
},
onSelect: function selectHelperOnSelect(aIndex, aSelected) {
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", {
index: aIndex,
selected: aSelected
});
if (!this._list.multiple) {
this.hide();
}
}
};

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

@ -0,0 +1,678 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Markers
*/
// Y axis scroll distance that will disable this module and cancel selection
const kDisableOnScrollDistance = 25;
function MarkerDragger(aMarker) {
this._marker = aMarker;
}
MarkerDragger.prototype = {
_selectionHelperUI: null,
_marker: null,
_shutdown: false,
_dragging: false,
get marker() {
return this._marker;
},
set shutdown(aVal) {
this._shutdown = aVal;
},
get shutdown() {
return this._shutdown;
},
get dragging() {
return this._dragging;
},
freeDrag: function freeDrag() {
return true;
},
isDraggable: function isDraggable(aTarget, aContent) {
return { x: true, y: true };
},
dragStart: function dragStart(aX, aY, aTarget, aScroller) {
if (this._shutdown)
return false;
this._dragging = true;
this.marker.dragStart(aX, aY);
return true;
},
dragStop: function dragStop(aDx, aDy, aScroller) {
if (this._shutdown)
return false;
this._dragging = false;
this.marker.dragStop(aDx, aDy);
return true;
},
dragMove: function dragMove(aDx, aDy, aScroller, aIsKenetic, aClientX, aClientY) {
// Note if aIsKenetic is true this is synthetic movement,
// we don't want that so return false.
if (this._shutdown || aIsKenetic)
return false;
this.marker.moveBy(aDx, aDy, aClientX, aClientY);
// return true if we moved, false otherwise. The result
// is used in deciding if we should repaint between drags.
return true;
}
}
function Marker() {}
Marker.prototype = {
_element: null,
_selectionHelperUI: null,
_xPos: 0,
_yPos: 0,
_tag: "",
_hPlane: 0,
_vPlane: 0,
// Tweak me if the monocle graphics change in any way
_monocleRadius: 8,
_monocleXHitTextAdjust: -2,
_monocleYHitTextAdjust: -10,
get xPos() {
return this._xPos;
},
get yPos() {
return this._yPos;
},
get tag() {
return this._tag;
},
set tag(aVal) {
this._tag = aVal;
},
get dragging() {
return this._element.customDragger.dragging;
},
init: function init(aParent, aElementId, xPos, yPos) {
this._selectionHelperUI = aParent;
this._element = document.getElementById(aElementId);
// These get picked in input.js and receives drag input
this._element.customDragger = new MarkerDragger(this);
},
shutdown: function shutdown() {
this._element.hidden = true;
this._element.customDragger.shutdown = true;
delete this._element.customDragger;
this._selectionHelperUI = null;
this._element = null;
},
setTrackBounds: function setTrackBounds(aVerticalPlane, aHorizontalPlane) {
// monocle boundaries
this._hPlane = aHorizontalPlane;
this._vPlane = aVerticalPlane;
},
show: function show() {
this._element.hidden = false;
},
hide: function hide() {
this._element.hidden = true;
},
position: function position(aX, aY) {
if (aX < 0) {
Util.dumpLn("Marker: aX is negative");
aX = 0;
}
if (aY < 0) {
Util.dumpLn("Marker: aY is negative");
aY = 0;
}
this._xPos = aX;
this._yPos = aY;
this._setPosition();
},
_setPosition: function _setPosition() {
this._element.left = this._xPos + "px";
this._element.top = this._yPos + "px";
},
dragStart: function dragStart(aX, aY) {
this._selectionHelperUI.markerDragStart(this);
},
dragStop: function dragStop(aDx, aDy) {
this._selectionHelperUI.markerDragStop(this);
},
moveBy: function moveBy(aDx, aDy, aClientX, aClientY) {
this._xPos -= aDx;
this._yPos -= aDy;
this._selectionHelperUI.markerDragMove(this);
this._setPosition();
},
hitTest: function hitTest(aX, aY) {
// Gets the pointer of the arrow right in the middle of the
// monocle.
aY += this._monocleYHitTextAdjust;
aX += this._monocleXHitTextAdjust;
if (aX >= (this._xPos - this._monocleRadius) &&
aX <= (this._xPos + this._monocleRadius) &&
aY >= (this._yPos - this._monocleRadius) &&
aY <= (this._yPos + this._monocleRadius))
return true;
return false;
},
};
/*
* SelectionHelperUI
*/
var SelectionHelperUI = {
_debugEvents: false,
_popupState: null,
_startMark: null,
_endMark: null,
_target: null,
_movement: { active: false, x:0, y: 0 },
_activeSelectionRect: null,
get startMark() {
if (this._startMark == null) {
this._startMark = new Marker();
this._startMark.tag = "start";
this._startMark.init(this, "selectionhandle-start");
}
return this._startMark;
},
get endMark() {
if (this._endMark == null) {
this._endMark = new Marker();
this._endMark.tag = "end";
this._endMark.init(this, "selectionhandle-end");
}
return this._endMark;
},
get overlay() {
return document.getElementById("selection-overlay");
},
/*
* openEditSession
*
* Attempts to select underlying text at a point and begins editing
* the section.
*/
openEditSession: function openEditSession(aMessage) {
if (!this.canHandle(aMessage))
return;
/*
* aMessage - from _onContentContextMenu in ContextMenuHandler
* name: aMessage.name,
* target: aMessage.target
* json:
* types: [],
* label: "",
* linkURL: "",
* linkTitle: "",
* linkProtocol: null,
* mediaURL: "",
* xPos: aEvent.x,
* yPos: aEvent.y
*/
this._popupState = aMessage.json;
this._popupState._target = aMessage.target;
this._init();
Util.dumpLn("enableSelection target:", this._popupState._target);
// Set the track bounds for each marker NIY
this.startMark.setTrackBounds(this._popupState.xPos, this._popupState.yPos);
this.endMark.setTrackBounds(this._popupState.xPos, this._popupState.yPos);
// Send this over to SelectionHandler in content, they'll message us
// back with information on the current selection.
this._popupState._target.messageManager.sendAsyncMessage(
"Browser:SelectionStart",
{ xPos: this._popupState.xPos,
yPos: this._popupState.yPos });
this._setupDebugOptions();
},
/*
* canHandle
*
* Determines if we can handle a ContextMenuHandler message.
*/
canHandle: function canHandle(aMessage) {
// Reject empty text inputs, nothing to do here
if (aMessage.json.types.indexOf("input-empty") != -1) {
return false;
}
// Input with text or general text in a page
if (aMessage.json.types.indexOf("content-text") == -1 &&
aMessage.json.types.indexOf("input-text") == -1) {
return false;
}
return true;
},
/*
* isActive
*
* Determines if an edit session is currently active.
*/
isActive: function isActive() {
return (this._popupState && this._popupState._target);
},
/*
* closeEditSession
*
* Closes an active edit session and shuts down. Does not clear existing
* selection regions if they exist.
*/
closeEditSession: function closeEditSession() {
if (this._popupState._target)
this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionClose");
this._shutdown();
},
/*
* closeEditSessionAndClear
*
* Closes an active edit session and shuts down. Clears any selection region
* associated with the edit session.
*/
closeEditSessionAndClear: function closeEditSessionAndClear() {
if (this._popupState._target)
this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionClear");
this.closeEditSession();
},
/*
* Internal
*/
_init: function _init() {
// SelectionHandler messages
messageManager.addMessageListener("Content:SelectionRange", this);
messageManager.addMessageListener("Content:SelectionCopied", this);
messageManager.addMessageListener("Content:SelectionFail", this);
messageManager.addMessageListener("Content:SelectionDebugRect", this);
// selection related events
window.addEventListener("click", this, true);
window.addEventListener("dblclick", this, true);
window.addEventListener("MozPressTapGesture", this, true);
// Picking up scroll attempts
window.addEventListener("touchstart", this, true);
window.addEventListener("touchend", this, true);
window.addEventListener("touchmove", this, true);
// cancellation related events
window.addEventListener("keypress", this, true);
Elements.browsers.addEventListener("URLChanged", this, true);
Elements.browsers.addEventListener("SizeChanged", this, true);
Elements.browsers.addEventListener("ZoomChanged", this, true);
window.addEventListener("MozPrecisePointer", this, true);
this.overlay.enabled = true;
},
_shutdown: function _shutdown() {
messageManager.removeMessageListener("Content:SelectionRange", this);
messageManager.removeMessageListener("Content:SelectionCopied", this);
messageManager.removeMessageListener("Content:SelectionFail", this);
messageManager.removeMessageListener("Content:SelectionDebugRect", this);
window.removeEventListener("click", this, true);
window.removeEventListener("dblclick", this, true);
window.removeEventListener("MozPressTapGesture", this, true);
window.removeEventListener("touchstart", this, true);
window.removeEventListener("touchend", this, true);
window.removeEventListener("touchmove", this, true);
window.removeEventListener("keypress", this, true);
Elements.browsers.removeEventListener("URLChanged", this, true);
Elements.browsers.removeEventListener("SizeChanged", this, true);
Elements.browsers.removeEventListener("ZoomChanged", this, true);
window.removeEventListener("MozPrecisePointer", this, true);
if (this.startMark != null)
this.startMark.shutdown();
if (this.endMark != null)
this.endMark.shutdown();
delete this._startMark;
delete this._endMark;
this._popupState = null;
this._activeSelectionRect = null;
this.overlay.displayDebugLayer = false;
this.overlay.enabled = false;
},
/*
* _setupDebugOptions
*
* Sends a message over to content instructing it to
* turn on various debug features.
*/
_setupDebugOptions: function _setupDebugOptions() {
// Debug options for selection
let debugOpts = { dumpRanges: false, displayRanges: false, dumpEvents: false };
try {
if (Services.prefs.getBoolPref(kDebugSelectionDumpPref))
debugOpts.displayRanges = true;
} catch (ex) {}
try {
if (Services.prefs.getBoolPref(kDebugSelectionDisplayPref))
debugOpts.displayRanges = true;
} catch (ex) {}
try {
if (Services.prefs.getBoolPref(kDebugSelectionDumpEvents)) {
debugOpts.dumpEvents = true;
this._debugEvents = true;
}
} catch (ex) {}
if (debugOpts.displayRanges || debugOpts.dumpRanges || debugOpts.dumpEvents) {
// Turn on the debug layer
this.overlay.displayDebugLayer = true;
// Tell SelectionHandler what to do
this._popupState
._target
.messageManager
.sendAsyncMessage("Browser:SelectionDebug", debugOpts);
}
},
/*
* Event handlers for document events
*/
_checkForActiveDrag: function _checkForActiveDrag() {
return (this.startMark.dragging || this.endMark.dragging);
},
_hitTestSelection: function _hitTestSelection(aEvent) {
let clickPos =
this._popupState._target.transformClientToBrowser(aEvent.clientX,
aEvent.clientY);
// Ignore if the double tap isn't on our active selection rect.
if (this._activeSelectionRect &&
Util.pointWithinRect(clickPos.x, clickPos.y, this._activeSelectionRect)) {
return true;
}
return false;
},
_onTap: function _onTap(aEvent) {
// Trap single clicks which if forwarded to content will clear selection.
aEvent.stopPropagation();
aEvent.preventDefault();
if (this.startMark.hitTest(aEvent.clientX, aEvent.clientY) ||
this.endMark.hitTest(aEvent.clientX, aEvent.clientY)) {
// NIY
this._popupState._target.messageManager.sendAsyncMessage("Browser:ChangeMode", {});
}
},
_onLongTap: function _onLongTap(aEvent) {
// Check to see if this tap is on one of our monocles. Tap is
// easy to trigger when dragging them around.
if (this.startMark.hitTest(aEvent.clientX, aEvent.clientY) ||
this.endMark.hitTest(aEvent.clientX, aEvent.clientY)) {
aEvent.stopPropagation();
aEvent.preventDefault();
return;
}
},
/*
* Checks to see if the tap event was on our selection rect.
* If it is, we select the underlying text and shutdown.
*/
_onDblTap: function _onDblTap(aEvent) {
if (!this._hitTestSelection(aEvent)) {
// Clear and close
this.closeEditSessionAndClear();
return;
}
// Select and close
let clickPos =
this._popupState._target.transformClientToBrowser(aEvent.clientX,
aEvent.clientY);
let json = {
xPos: clickPos.x,
yPos: clickPos.y,
};
this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionCopy", json);
aEvent.stopPropagation();
aEvent.preventDefault();
},
_onSelectionCopied: function _onSelectionCopied(json) {
if (json.succeeded) {
this.showToast(Strings.browser.GetStringFromName("selectionHelper.textCopied"));
}
this.closeEditSessionAndClear();
},
_onSelectionRangeChange: function _onSelectionRangeChange(json) {
if (json.updateStart) {
let pos =
this._popupState._target.transformBrowserToClient(json.start.xPos, json.start.yPos);
this.startMark.position(pos.x, pos.y);
this.startMark.show();
}
if (json.updateEnd) {
let pos =
this._popupState._target.transformBrowserToClient(json.end.xPos, json.end.yPos);
this.endMark.position(pos.x, pos.y);
this.endMark.show();
}
this._activeSelectionRect = json.rect;
},
_onSelectionFail: function _onSelectionFail() {
Util.dumpLn("failed to get a selection.");
this.closeEditSession();
},
_onKeypress: function _onKeypress() {
this.closeEditSession();
},
_onResize: function _onResize() {
this._popupState._target
.messageManager
.sendAsyncMessage("Browser:SelectionUpdate", {});
},
_onDebugRectRequest: function _onDebugRectRequest(aMsg) {
this.overlay.addDebugRect(aMsg.left, aMsg.top, aMsg.right, aMsg.bottom,
aMsg.color, aMsg.fill, aMsg.id);
},
/*
* Events
*/
_initMouseEventFromEvent: function _initMouseEventFromEvent(aDestEvent, aSrcEvent, aType) {
event.initNSMouseEvent(aType, true, true, content, 0,
aSrcEvent.screenX, aSrcEvent.screenY, aSrcEvent.clientX, aSrcEvent.clientY,
false, false, false, false,
aSrcEvent.button, aSrcEvent.relatedTarget, 1.0,
Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
},
handleEvent: function handleEvent(aEvent) {
if (this._debugEvents && aEvent.type != "touchmove") {
Util.dumpLn("SelectionHelperUI:", aEvent.type);
}
switch (aEvent.type) {
case "click":
this._onTap(aEvent);
break;
case "dblclick":
this._onDblTap(aEvent);
break;
case "MozPressTapGesture":
this._onLongTap(aEvent);
break;
case "touchstart": {
if (aEvent.touches.length != 1)
break;
let touch = aEvent.touches[0];
this._movement.x = this._movement.y = 0;
this._movement.x = touch.clientX;
this._movement.y = touch.clientY;
this._movement.active = true;
break;
}
case "touchend":
if (aEvent.touches.length == 0)
this._movement.active = false;
break;
case "touchmove": {
if (aEvent.touches.length != 1)
break;
let touch = aEvent.touches[0];
// Clear our selection overlay when the user starts to pan the page
if (!this._checkForActiveDrag() && this._movement.active) {
let distanceY = touch.clientY - this._movement.y;
if (Math.abs(distanceY) > kDisableOnScrollDistance) {
this.closeEditSessionAndClear();
}
}
break;
}
case "keypress":
this._onKeypress(aEvent);
break;
case "SizeChanged":
this._onResize(aEvent);
break;
case "ZoomChanged":
case "URLChanged":
case "MozPrecisePointer":
this.closeEditSessionAndClear();
break;
}
},
receiveMessage: function sh_receiveMessage(aMessage) {
if (this._debugEvents) Util.dumpLn("SelectionHelperUI:", aMessage.name);
let json = aMessage.json;
switch (aMessage.name) {
case "Content:SelectionFail":
this._onSelectionFail();
break;
case "Content:SelectionRange":
this._onSelectionRangeChange(json);
break;
case "Content:SelectionCopied":
this._onSelectionCopied(json);
break;
case "Content:SelectionDebugRect":
this._onDebugRectRequest(json);
break;
}
},
/*
* Callbacks from markers
*/
_getMarkerBaseMessage: function _getMarkerBaseMessage() {
/*
This appears to be adjusted for scroll and scale. It should only
adjust for scale, content handles scroll offsets.
let startPos =
this._popupState._target.transformBrowserToClient(this.startMark.xPos,
this.startMark.yPos);
let endPos =
this._popupState._target.transformBrowserToClient(this.endMark.xPos,
this.endMark.yPos);
return {
start: { xPos: startPos.x, yPos: startPos.y },
end: { xPos: endPos.x, yPos: endPos.y },
};
*/
return {
start: { xPos: this.startMark.xPos, yPos: this.startMark.yPos },
end: { xPos: this.endMark.xPos, yPos: this.endMark.yPos },
};
},
markerDragStart: function markerDragStart(aMarker) {
let json = this._getMarkerBaseMessage();
json.change = aMarker.tag;
this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionMoveStart", json);
},
markerDragStop: function markerDragStop(aMarker) {
//aMarker.show();
let json = this._getMarkerBaseMessage();
json.change = aMarker.tag;
this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionMoveEnd", json);
},
markerDragMove: function markerDragMove(aMarker) {
let json = this._getMarkerBaseMessage();
json.change = aMarker.tag;
this._popupState._target.messageManager.sendAsyncMessage("Browser:SelectionMove", json);
},
showToast: function showToast(aString) {
let toaster =
Cc["@mozilla.org/toaster-alerts-service;1"]
.getService(Ci.nsIAlertsService);
toaster.showAlertNotification(null, aString, "", false, "", null);
},
};

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

@ -0,0 +1,83 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var SharingUI = {
_dialog: null,
show: function show(aURL, aTitle) {
try {
this.showSharingUI(aURL, aTitle);
} catch (ex) {
this.showFallback(aURL, aTitle);
}
},
showSharingUI: function showSharingUI(aURL, aTitle) {
let sharingSvc = Cc["@mozilla.org/uriloader/external-sharing-app-service;1"].getService(Ci.nsIExternalSharingAppService);
sharingSvc.shareWithDefault(aURL, "text/plain", aTitle);
},
showFallback: function showFallback(aURL, aTitle) {
this._dialog = DialogUI.importModal(window, "chrome://browser/content/prompt/share.xul", null);
document.getElementById("share-title").value = aTitle || aURL;
DialogUI.pushPopup(this, this._dialog);
let bbox = document.getElementById("share-buttons-box");
this._handlers.forEach(function(handler) {
let button = document.createElement("button");
button.className = "action-button";
button.setAttribute("label", handler.name);
button.addEventListener("command", function() {
SharingUI.hide();
handler.callback(aURL || "", aTitle || "");
}, false);
bbox.appendChild(button);
});
this._dialog.waitForClose();
DialogUI.popPopup(this);
},
hide: function hide() {
this._dialog.close();
this._dialog = null;
},
_handlers: [
{
name: "Email",
callback: function callback(aURL, aTitle) {
let url = "mailto:?subject=" + encodeURIComponent(aTitle) +
"&body=" + encodeURIComponent(aURL);
let uri = Services.io.newURI(url, null, null);
let extProtocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService);
extProtocolSvc.loadUrl(uri);
}
},
{
name: "Twitter",
callback: function callback(aURL, aTitle) {
let url = "http://twitter.com/home?status=" + encodeURIComponent((aTitle ? aTitle+": " : "")+aURL);
BrowserUI.newTab(url, Browser.selectedTab);
}
},
{
name: "Google Reader",
callback: function callback(aURL, aTitle) {
let url = "http://www.google.com/reader/link?url=" + encodeURIComponent(aURL) +
"&title=" + encodeURIComponent(aTitle);
BrowserUI.newTab(url, Browser.selectedTab);
}
},
{
name: "Facebook",
callback: function callback(aURL, aTitle) {
let url = "http://www.facebook.com/share.php?u=" + encodeURIComponent(aURL);
BrowserUI.newTab(url, Browser.selectedTab);
}
}
]
};

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

@ -0,0 +1,90 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
// TODO <jwilde>: Observe changes in history with nsINavHistoryObserver
function HistoryView(aSet) {
this._set = aSet;
this._set.controller = this;
//add observers here
}
HistoryView.prototype = {
_set:null,
handleItemClick: function tabview_handleItemClick(aItem) {
let url = aItem.getAttribute("value");
BrowserUI.goToURI(url);
},
populateGrid: function populateGrid() {
let query = gHistSvc.getNewQuery();
let options = gHistSvc.getNewQueryOptions();
options.excludeQueries = true;
options.queryType = options.QUERY_TYPE_HISTORY;
options.maxResults = StartUI.maxResultsPerSection;
options.resultType = options.RESULTS_AS_URI;
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
let result = gHistSvc.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let childCount = rootNode.childCount;
for (let i = 0; i < childCount; i++) {
let node = rootNode.getChild(i);
let uri = node.uri;
let title = node.title || uri;
let item = this._set.appendItem(title, uri);
item.setAttribute("iconURI", node.icon);
}
rootNode.containerOpen = false;
},
destruct: function destruct() {
},
};
let HistoryStartView = {
_view: null,
get _grid() { return document.getElementById("start-history-grid"); },
show: function show() {
this._grid.arrangeItems();
},
init: function init() {
this._view = new HistoryView(this._grid);
this._view.populateGrid();
},
uninit: function uninit() {
this._view.destruct();
}
};
let HistoryPanelView = {
_view: null,
get _grid() { return document.getElementById("history-list"); },
get visible() { return PanelUI.isPaneVisible("history-container"); },
show: function show() {
this._grid.arrangeItems();
},
init: function init() {
this._view = new HistoryView(this._grid);
this._view.populateGrid();
},
uninit: function uninit() {
this._view.destruct();
}
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,770 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html onclick="keepFocusInTextbox(event)">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>JavaScript Shell 1.4</title>
<script type="text/javascript">
var
histList = [""],
histPos = 0,
_scope = {},
_win, // a top-level context
question, // {String} the input command that's being evaluated. Accessed via
// |Shell.question| from the target window for evaluation.
_in, // {HTMLTextAreaElement} the textarea containing the input
_out, // {HTMLDivElement} the output is printed to this element
tooManyMatches = null,
lastError = null,
_jsVer = 0 // determines the way to execute the commands, see run()
;
function refocus()
{
_in.blur(); // Needed for Mozilla to scroll correctly.
_in.focus();
}
function init()
{
_in = document.getElementById("input");
_out = document.getElementById("output");
_win = window;
if (opener && !opener.closed)
{
println("Using bookmarklet version of shell: commands will run in opener's context.", "message");
_win = opener;
}
/* Run a series of (potentially async, but quick) tests to determine the
* way to run code in this browser (for run()). Sets window._jsVer based
* on the tests. */
_jsVer = 0;
for (var jsVerToTry=19; jsVerToTry>=15; jsVerToTry--) {
run(window, "if(_jsVer < " + jsVerToTry + ") { " +
"_jsVer=" + jsVerToTry + "; }", jsVerToTry);
}
initTarget();
recalculateInputHeight();
refocus();
}
/**
* Runs |code| in |_win|'s context.
* @param overridenJSVer {int} (optional) overrides the default (specified by _jsVer)
* way to run the code.
*/
function run(_win, code, overridenJSVer) {
var jsVerToUse = overridenJSVer ? overridenJSVer : _jsVer;
if (jsVerToUse <= 15) {
_win.location.href = "javascript:" + code + "; void 0";
} else {
var sc = _win.document.createElement("script");
sc.type="application/javascript;version=" + jsVerToUse/10;
sc.src="data:application/x-javascript," + code;
_win.document.body.appendChild(sc); // runs the script asynchronously
}
}
function initTarget()
{
_win.Shell = window;
_win.print = shellCommands.print;
}
// Unless the user is selected something, refocus the textbox.
// (requested by caillon, brendan, asa)
function keepFocusInTextbox(e)
{
var g = e.srcElement ? e.srcElement : e.target; // IE vs. standard
while (!g.tagName)
g = g.parentNode;
var t = g.tagName.toUpperCase();
if (t=="A" || t=="INPUT")
return;
if (window.getSelection) {
// Mozilla
if (String(window.getSelection()))
return;
}
else {
// IE
if ( document.selection.createRange().text )
return;
}
refocus();
}
function inputKeydown(e) {
// Use onkeydown because IE doesn't support onkeypress for arrow keys
//alert(e.keyCode + " ^ " + e.keycode);
if (e.shiftKey && e.keyCode == 13) { // shift-enter
// don't do anything; allow the shift-enter to insert a line break as normal
} else if (e.keyCode == 13) { // enter
// execute the input on enter
try { go(); } catch(er) { alert(er); };
setTimeout(function() { _in.value = ""; }, 0); // can't preventDefault on input, so clear it later
} else if (e.keyCode == 38) { // up
// go up in history if at top or ctrl-up
if (e.ctrlKey || caretInFirstLine(_in))
hist(true);
} else if (e.keyCode == 40) { // down
// go down in history if at end or ctrl-down
if (e.ctrlKey || caretInLastLine(_in))
hist(false);
} else if (e.keyCode == 9) { // tab
tabcomplete();
setTimeout(function() { refocus(); }, 0); // refocus because tab was hit
} else { }
setTimeout(recalculateInputHeight, 0);
//return true;
};
function caretInFirstLine(textbox)
{
// IE doesn't support selectionStart/selectionEnd
if (textbox.selectionStart == undefined)
return true;
var firstLineBreak = textbox.value.indexOf("\n");
return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
}
function caretInLastLine(textbox)
{
// IE doesn't support selectionStart/selectionEnd
if (textbox.selectionEnd == undefined)
return true;
var lastLineBreak = textbox.value.lastIndexOf("\n");
return (textbox.selectionEnd > lastLineBreak);
}
function recalculateInputHeight()
{
var rows = _in.value.split(/\n/).length
+ 1 // prevent scrollbar flickering in Mozilla
+ (window.opera ? 1 : 0); // leave room for scrollbar in Opera
if (_in.rows != rows) // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0.
_in.rows = rows;
}
function println(s, type)
{
if((s=String(s)))
{
var newdiv = document.createElement("div");
newdiv.appendChild(document.createTextNode(s));
newdiv.className = type;
_out.appendChild(newdiv);
return newdiv;
}
return false;
}
function printWithRunin(h, s, type)
{
var div = println(s, type);
if (div) {
var head = document.createElement("strong");
head.appendChild(document.createTextNode(h + ": "));
div.insertBefore(head, div.firstChild);
}
}
var shellCommands =
{
load : function load(url)
{
var s = _win.document.createElement("script");
s.type = "text/javascript";
s.src = url;
_win.document.getElementsByTagName("head")[0].appendChild(s);
println("Loading " + url + "...", "message");
},
clear : function clear()
{
var CHILDREN_TO_PRESERVE = 3;
while (_out.childNodes[CHILDREN_TO_PRESERVE])
_out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]);
},
print : function print(s) { println(s, "print"); },
// the normal function, "print", shouldn't return a value
// (suggested by brendan; later noticed it was a problem when showing others)
pr : function pr(s)
{
shellCommands.print(s); // need to specify shellCommands so it doesn't try window.print()!
return s;
},
props : function props(e, onePerLine)
{
if (e === null) {
println("props called with null argument", "error");
return;
}
if (e === undefined) {
println("props called with undefined argument", "error");
return;
}
var ns = ["Methods", "Fields", "Unreachables"];
var as = [[], [], []]; // array of (empty) arrays of arrays!
var p, j, i; // loop variables, several used multiple times
var protoLevels = 0;
for (p = e; p; p = p.__proto__)
{
for (i=0; i<ns.length; ++i)
as[i][protoLevels] = [];
++protoLevels;
}
for(var a in e)
{
// Shortcoming: doesn't check that VALUES are the same in object and prototype.
var protoLevel = -1;
try
{
for (p = e; p && (a in p); p = p.__proto__)
++protoLevel;
}
catch(er) { protoLevel = 0; } // "in" operator throws when param to props() is a string
var type = 1;
try
{
if ((typeof e[a]) == "function")
type = 0;
}
catch (er) { type = 2; }
as[type][protoLevel].push(a);
}
function times(s, n) { return n ? s + times(s, n-1) : ""; }
for (j=0; j<protoLevels; ++j)
for (i=0;i<ns.length;++i)
if (as[i][j].length)
printWithRunin(
ns[i] + times(" of prototype", j),
(onePerLine ? "\n\n" : "") + as[i][j].sort().join(onePerLine ? "\n" : ", ") + (onePerLine ? "\n\n" : ""),
"propList"
);
},
blink : function blink(node)
{
if (!node) throw("blink: argument is null or undefined.");
if (node.nodeType == null) throw("blink: argument must be a node.");
if (node.nodeType == 3) throw("blink: argument must not be a text node");
if (node.documentElement) throw("blink: argument must not be the document object");
function setOutline(o) {
return function() {
if (node.style.outline != node.style.bogusProperty) {
// browser supports outline (Firefox 1.1 and newer, CSS3, Opera 8).
node.style.outline = o;
}
else if (node.style.MozOutline != node.style.bogusProperty) {
// browser supports MozOutline (Firefox 1.0.x and older)
node.style.MozOutline = o;
}
else {
// browser only supports border (IE). border is a fallback because it moves things around.
node.style.border = o;
}
}
}
function focusIt(a) {
return function() {
a.focus();
}
}
if (node.ownerDocument) {
var windowToFocusNow = (node.ownerDocument.defaultView || node.ownerDocument.parentWindow); // Moz vs. IE
if (windowToFocusNow)
setTimeout(focusIt(windowToFocusNow.top), 0);
}
for(var i=1;i<7;++i)
setTimeout(setOutline((i%2)?'3px solid red':'none'), i*100);
setTimeout(focusIt(window), 800);
setTimeout(focusIt(_in), 810);
},
scope : function scope(sc)
{
if (!sc) sc = {};
_scope = sc;
println("Scope is now " + sc + ". If a variable is not found in this scope, window will also be searched. New variables will still go on window.", "message");
},
mathHelp : function mathHelp()
{
printWithRunin("Math constants", "E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2, SQRT2", "propList");
printWithRunin("Math methods", "abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random, round, sin, sqrt, tan", "propList");
},
ans : undefined
};
function hist(up)
{
// histList[0] = first command entered, [1] = second, etc.
// type something, press up --> thing typed is now in "limbo"
// (last item in histList) and should be reachable by pressing
// down again.
var L = histList.length;
if (L == 1)
return;
if (up)
{
if (histPos == L-1)
{
// Save this entry in case the user hits the down key.
histList[histPos] = _in.value;
}
if (histPos > 0)
{
histPos--;
// Use a timeout to prevent up from moving cursor within new text
// Set to nothing first for the same reason
setTimeout(
function() {
_in.value = '';
_in.value = histList[histPos];
var caretPos = _in.value.length;
if (_in.setSelectionRange)
_in.setSelectionRange(caretPos, caretPos);
},
0
);
}
}
else // down
{
if (histPos < L-1)
{
histPos++;
_in.value = histList[histPos];
}
else if (histPos == L-1)
{
// Already on the current entry: clear but save
if (_in.value)
{
histList[histPos] = _in.value;
++histPos;
_in.value = "";
}
}
}
}
function tabcomplete()
{
/*
* Working backwards from s[from], find the spot
* where this expression starts. It will scan
* until it hits a mismatched ( or a space,
* but it skips over quoted strings.
* If stopAtDot is true, stop at a '.'
*/
function findbeginning(s, from, stopAtDot)
{
/*
* Complicated function.
*
* Return true if s[i] == q BUT ONLY IF
* s[i-1] is not a backslash.
*/
function equalButNotEscaped(s,i,q)
{
if(s.charAt(i) != q) // not equal go no further
return false;
if(i==0) // beginning of string
return true;
if(s.charAt(i-1) == '\\') // escaped?
return false;
return true;
}
var nparens = 0;
var i;
for(i=from; i>=0; i--)
{
if(s.charAt(i) == ' ')
break;
if(stopAtDot && s.charAt(i) == '.')
break;
if(s.charAt(i) == ')')
nparens++;
else if(s.charAt(i) == '(')
nparens--;
if(nparens < 0)
break;
// skip quoted strings
if(s.charAt(i) == '\'' || s.charAt(i) == '\"')
{
//dump("skipping quoted chars: ");
var quot = s.charAt(i);
i--;
while(i >= 0 && !equalButNotEscaped(s,i,quot)) {
//dump(s.charAt(i));
i--;
}
//dump("\n");
}
}
return i;
}
// XXX should be used more consistently (instead of using selectionStart/selectionEnd throughout code)
// XXX doesn't work in IE, even though it contains IE-specific code
function getcaretpos(inp)
{
if(inp.selectionEnd != null)
return inp.selectionEnd;
if(inp.createTextRange)
{
var docrange = _win.Shell.document.selection.createRange();
var inprange = inp.createTextRange();
if (inprange.setEndPoint)
{
inprange.setEndPoint('EndToStart', docrange);
return inprange.text.length;
}
}
return inp.value.length; // sucks, punt
}
function setselectionto(inp,pos)
{
if(inp.selectionStart) {
inp.selectionStart = inp.selectionEnd = pos;
}
else if(inp.createTextRange) {
var docrange = _win.Shell.document.selection.createRange();
var inprange = inp.createTextRange();
inprange.move('character',pos);
inprange.select();
}
else { // err...
/*
inp.select();
if(_win.Shell.document.getSelection())
_win.Shell.document.getSelection() = "";
*/
}
}
// get position of cursor within the input box
var caret = getcaretpos(_in);
if(caret) {
//dump("----\n");
var dotpos, spacepos, complete, obj;
//dump("caret pos: " + caret + "\n");
// see if there's a dot before here
dotpos = findbeginning(_in.value, caret-1, true);
//dump("dot pos: " + dotpos + "\n");
if(dotpos == -1 || _in.value.charAt(dotpos) != '.') {
dotpos = caret;
//dump("changed dot pos: " + dotpos + "\n");
}
// look backwards for a non-variable-name character
spacepos = findbeginning(_in.value, dotpos-1, false);
//dump("space pos: " + spacepos + "\n");
// get the object we're trying to complete on
if(spacepos == dotpos || spacepos+1 == dotpos || dotpos == caret)
{
// try completing function args
if(_in.value.charAt(dotpos) == '(' ||
(_in.value.charAt(spacepos) == '(' && (spacepos+1) == dotpos))
{
var fn,fname;
var from = (_in.value.charAt(dotpos) == '(') ? dotpos : spacepos;
spacepos = findbeginning(_in.value, from-1, false);
fname = _in.value.substr(spacepos+1,from-(spacepos+1));
//dump("fname: " + fname + "\n");
try {
with(_win.Shell._scope)
with(_win)
with(Shell.shellCommands)
fn = eval(fname);
}
catch(er) {
//dump('fn is not a valid object\n');
return;
}
if(fn == undefined) {
//dump('fn is undefined');
return;
}
if(fn instanceof Function)
{
// Print function definition, including argument names, but not function body
if(!fn.toString().match(/function .+?\(\) +\{\n +\[native code\]\n\}/))
println(fn.toString().match(/function .+?\(.*?\)/), "tabcomplete");
}
return;
}
else
obj = _win;
}
else
{
var objname = _in.value.substr(spacepos+1,dotpos-(spacepos+1));
//dump("objname: |" + objname + "|\n");
try {
with(_win.Shell._scope)
with(_win)
obj = eval(objname);
}
catch(er) {
printError(er);
return;
}
if(obj == undefined) {
// sometimes this is tabcomplete's fault, so don't print it :(
// e.g. completing from "print(document.getElements"
// println("Can't complete from null or undefined expression " + objname, "error");
return;
}
}
//dump("obj: " + obj + "\n");
// get the thing we're trying to complete
if(dotpos == caret)
{
if(spacepos+1 == dotpos || spacepos == dotpos)
{
// nothing to complete
//dump("nothing to complete\n");
return;
}
complete = _in.value.substr(spacepos+1,dotpos-(spacepos+1));
}
else {
complete = _in.value.substr(dotpos+1,caret-(dotpos+1));
}
//dump("complete: " + complete + "\n");
// ok, now look at all the props/methods of this obj
// and find ones starting with 'complete'
var matches = [];
var bestmatch = null;
for(var a in obj)
{
//a = a.toString();
//XXX: making it lowercase could help some cases,
// but screws up my general logic.
if(a.substr(0,complete.length) == complete) {
matches.push(a);
////dump("match: " + a + "\n");
// if no best match, this is the best match
if(bestmatch == null)
{
bestmatch = a;
}
else {
// the best match is the longest common string
function min(a,b){ return ((a<b)?a:b); }
var i;
for(i=0; i< min(bestmatch.length, a.length); i++)
{
if(bestmatch.charAt(i) != a.charAt(i))
break;
}
bestmatch = bestmatch.substr(0,i);
////dump("bestmatch len: " + i + "\n");
}
////dump("bestmatch: " + bestmatch + "\n");
}
}
bestmatch = (bestmatch || "");
////dump("matches: " + matches + "\n");
var objAndComplete = (objname || obj) + "." + bestmatch;
//dump("matches.length: " + matches.length + ", tooManyMatches: " + tooManyMatches + ", objAndComplete: " + objAndComplete + "\n");
if(matches.length > 1 && (tooManyMatches == objAndComplete || matches.length <= 10)) {
printWithRunin("Matches: ", matches.join(', '), "tabcomplete");
tooManyMatches = null;
}
else if(matches.length > 10)
{
println(matches.length + " matches. Press tab again to see them all", "tabcomplete");
tooManyMatches = objAndComplete;
}
else {
tooManyMatches = null;
}
if(bestmatch != "")
{
var sstart;
if(dotpos == caret) {
sstart = spacepos+1;
}
else {
sstart = dotpos+1;
}
_in.value = _in.value.substr(0, sstart)
+ bestmatch
+ _in.value.substr(caret);
setselectionto(_in,caret + (bestmatch.length - complete.length));
}
}
}
function printQuestion(q)
{
println(q, "input");
}
function printAnswer(a)
{
if (a !== undefined) {
println(a, "normalOutput");
shellCommands.ans = a;
}
}
function printError(er)
{
var lineNumberString;
lastError = er; // for debugging the shell
if (er.name)
{
// lineNumberString should not be "", to avoid a very wacky bug in IE 6.
lineNumberString = (er.lineNumber != undefined) ? (" on line " + er.lineNumber + ": ") : ": ";
println(er.name + lineNumberString + er.message, "error"); // Because IE doesn't have error.toString.
}
else
println(er, "error"); // Because security errors in Moz /only/ have toString.
}
/**
* Evaluates |s| or current input (_in.value) in the previously set up context.
* @param {String} s - (optional) command to evaluate.
*/
function go(s)
{
// save the command to eval in |question|, so that the target window can access
// it when evaluating.
_in.value = question = s ? s : _in.value;
if (question == "")
return;
histList[histList.length-1] = question;
histList[histList.length] = "";
histPos = histList.length - 1;
// Unfortunately, this has to happen *before* the JavaScript is run, so that
// print() output will go in the right place.
_in.value='';
recalculateInputHeight();
printQuestion(question);
if (_win.closed) {
printError("Target window has been closed.");
return;
}
try { ("Shell" in _win) }
catch(er) {
printError("The JavaScript Shell cannot access variables in the target window. The most likely reason is that the target window now has a different page loaded and that page has a different hostname than the original page.");
return;
}
if (!("Shell" in _win))
initTarget(); // silent
// Evaluate Shell.question using _win's eval (this is why eval isn't in the |with|, IIRC).
run(_win, "try{ Shell.printAnswer(eval('with(Shell._scope) with(Shell.shellCommands) {' + Shell.question + String.fromCharCode(10) + '}')); } catch(er) { Shell.printError(er); }; setTimeout(Shell.refocus, 0);");
}
</script>
<!-- for http://ted.mielczarek.org/code/mozilla/extensiondev/ -->
<script type="text/javascript" src="chrome://extensiondev/content/chromeShellExtras.js"></script>
<style type="text/css">
body { background: white; color: black; }
#output {
/* Preserve line breaks, but wrap too if browser supports it */
white-space: pre;
white-space: -moz-pre-wrap;
white-space: pre-wrap;
}
h3 { margin-top: 0; margin-bottom: 0em; }
h3 + div { margin: 0; }
form { margin: 0; padding: 0; }
#input { width: 100%; border: none; padding: 0; overflow: auto; }
.input { color: blue; background: white; font: inherit; font-weight: bold; margin-top: .5em; /* background: #E6E6FF; */ }
.normalOutput { color: black; background: white; }
.print { color: brown; background: white; }
.error { color: red; background: white; }
.propList { color: green; background: white; }
.message { color: green; background: white; }
.tabcomplete { color: purple; background: white; }
</style>
</head>
<body onload="init()">
<div id="output"><h3>JavaScript Shell 1.4</h3><div>Features: autocompletion of property names with Tab, multiline input with Shift+Enter, input history with (Ctrl+) Up/Down, <a accesskey="M" href="javascript:go('scope(Math); mathHelp();');" title="Accesskey: M">Math</a>, <a accesskey="H" href="http://www.squarefree.com/shell/?ignoreReferrerFrom=shell1.4" title="Accesskey: H">help</a></div><div>Values and functions: ans, print(string), <a accesskey="P" href="javascript:go('props(ans)')" title="Accesskey: P">props(object)</a>, <a accesskey="B" href="javascript:go('blink(ans)')" title="Accesskey: B">blink(node)</a>, <a accesskey="C" href="javascript:go('clear()')" title="Accesskey: C">clear()</a>, load(scriptURL), scope(object)</div></div>
<div><textarea id="input" class="input" wrap="off" spellcheck="false" onkeydown="inputKeydown(event)" rows="1"></textarea></div>
</body>
</html>

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

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="jsshell"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
contenttitlesetting="true"
title="Debug JSShell"
width="600"
height="400"
persist="width height screenX screenY">
<browser id="content" flex="1" type="content-primary" src="chrome://browser/content/shell.html"/>
</window>

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

@ -0,0 +1,241 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % certerrorDTD
SYSTEM "chrome://browser/locale/aboutCertError.dtd">
%certerrorDTD;
]>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&certerror.pagetitle;</title>
<meta name="viewport" content="width=device-width; user-scalable=false" />
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
<!-- This page currently uses the same favicon as neterror.xhtml.
If the location of the favicon is changed for both pages, the
FAVICON_ERRORPAGE_URL symbol in toolkit/components/places/src/nsFaviconService.h
should be updated. If this page starts using a different favicon
than neterror, nsFaviconService->SetAndFetchFaviconForPage
should be updated to ignore this one as well. -->
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// about:certerror?e=error&u=url&d=desc
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getCSSClass()
{
var url = document.documentURI;
var matches = url.match(/s\=([^&]+)\&/);
// s is optional, if no match just return nothing
if (!matches || matches.length < 2)
return "";
// parenthetical match is the second entry
return decodeURIComponent(matches[1]);
}
function getDescription()
{
var url = document.documentURI;
var desc = url.search(/d\=/);
// desc == -1 if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (desc == -1)
return "";
return decodeURIComponent(url.slice(desc + 2));
}
function initPage()
{
// Replace the "#1" string in the intro with the hostname. Trickier
// than it might seem since we want to preserve the <b> tags, but
// not allow for any injection by just using innerHTML. Instead,
// just find the right target text node.
var intro = document.getElementById('introContentP1');
function replaceWithHost(node) {
if (node.textContent == "#1")
node.textContent = location.host;
else
for(var i = 0; i < node.childNodes.length; i++)
replaceWithHost(node.childNodes[i]);
};
replaceWithHost(intro);
if (getCSSClass() == "expertBadCert") {
toggle('technicalContent');
toggle('expertContent');
}
var tech = document.getElementById("technicalContentText");
if (tech)
tech.textContent = getDescription();
addDomainErrorLink();
}
/* In the case of SSL error pages about domain mismatch, see if
we can hyperlink the user to the correct site. We don't want
to do this generically since it allows MitM attacks to redirect
users to a site under attacker control, but in certain cases
it is safe (and helpful!) to do so. Bug 402210
*/
function addDomainErrorLink() {
// Rather than textContent, we need to treat description as HTML
var sd = document.getElementById("technicalContentText");
if (sd) {
var desc = getDescription();
// sanitize description text - see bug 441169
// First, find the index of the <a> tag we care about, being careful not to
// use an over-greedy regex
var re = /<a id="cert_domain_link" title="([^"]+)">/;
var result = re.exec(desc);
if(!result)
return;
// Remove sd's existing children
sd.textContent = "";
// Everything up to the link should be text content
sd.appendChild(document.createTextNode(desc.slice(0, result.index)));
// Now create the link itself
var anchorEl = document.createElement("a");
anchorEl.setAttribute("id", "cert_domain_link");
anchorEl.setAttribute("title", result[1]);
anchorEl.appendChild(document.createTextNode(result[1]));
sd.appendChild(anchorEl);
// Finally, append text for anything after the closing </a>
sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length)));
}
var link = document.getElementById('cert_domain_link');
if (!link)
return;
var okHost = link.getAttribute("title");
var thisHost = document.location.hostname;
var proto = document.location.protocol;
// If okHost is a wildcard domain ("*.example.com") let's
// use "www" instead. "*.example.com" isn't going to
// get anyone anywhere useful. bug 432491
okHost = okHost.replace(/^\*\./, "www.");
/* case #1:
* example.com uses an invalid security certificate.
*
* The certificate is only valid for www.example.com
*
* Make sure to include the "." ahead of thisHost so that
* a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
*
* We'd normally just use a RegExp here except that we lack a
* library function to escape them properly (bug 248062), and
* domain names are famous for having '.' characters in them,
* which would allow spurious and possibly hostile matches.
*/
if (endsWith(okHost, "." + thisHost))
link.href = proto + okHost;
/* case #2:
* browser.garage.maemo.org uses an invalid security certificate.
*
* The certificate is only valid for garage.maemo.org
*/
if (endsWith(thisHost, "." + okHost))
link.href = proto + okHost;
// If we set a link, meaning there's something helpful for
// the user here, expand the section by default
if (link.href && getCSSClass() != "expertBadCert")
toggle("technicalContent");
}
function endsWith(haystack, needle) {
return haystack.slice(-needle.length) == needle;
}
function toggle(id) {
var el = document.getElementById(id);
if (el.getAttribute("collapsed"))
el.setAttribute("collapsed", false);
else
el.setAttribute("collapsed", true);
}
]]></script>
</head>
<body id="errorPage" class="certerror" dir="&locale.dir;">
<!-- Error Title -->
<div id="errorTitle">
<h1 class="errorTitleText">&certerror.longpagetitle;</h1>
</div>
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<div id="introContent">
<p id="introContentP1">&certerror.introPara1;</p>
</div>
<div id="whatShouldIDoContent">
<h2>&certerror.whatShouldIDo.heading;</h2>
<div id="whatShouldIDoContentText">
<p>&certerror.whatShouldIDo.content;</p>
<xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='getMeOutOfHereButton' label='&certerror.getMeOutOfHere.label;'/>
</div>
</div>
<!-- The following sections can be unhidden by default by setting the
"browser.xul.error_pages.expert_bad_cert" pref to true -->
<div id="technicalContent" collapsed="true">
<h2 onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
<p id="technicalContentText"/>
</div>
<div id="expertContent" collapsed="true">
<h2 onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
<div>
<p>&certerror.expert.content;</p>
<p>&certerror.expert.contentPara2;</p>
<xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='temporaryExceptionButton' label='&certerror.addTemporaryException.label;'/>
<xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='permanentExceptionButton' label='&certerror.addPermanentException.label;'/>
</div>
</div>
</div>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

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

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % aboutRightsDTD SYSTEM "chrome://global/locale/aboutRights.dtd">
%aboutRightsDTD;
]>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Oh noooz! You crashed!</title>
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
</head>
<body id="your-rights" dir="&rights.locale-direction;" class="aboutPageWideContainer">
<h1>Oh noooz! You crashed!</h1>
<p>A crash report is being submitted as you read this, we hope! Check <a href="about:crashes">about:crashes</a> for crash reports.</p>
<p>(I'm know I'm boring to look at, hopefully someday I'll be pretty and useful and stuff!)</p>
<script type="application/javascript"><![CDATA[
]]></script>
</body>
</html>

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

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % aboutRightsDTD SYSTEM "chrome://global/locale/aboutRights.dtd">
%aboutRightsDTD;
]>
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&rights.pagetitle;</title>
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
</head>
<body id="your-rights" dir="&rights.locale-direction;" class="aboutPageWideContainer">
<h1>&rights.intro-header;</h1>
<p>&rights.intro;</p>
<ul>
<li>&rights.intro-point1a;<a href="http://www.mozilla.org/MPL/">&rights.intro-point1b;</a>&rights.intro-point1c;</li>
# Point 2 discusses Mozilla trademarks, and isn't needed when the build is unbranded.
# Point 3 discusses privacy policy, unbranded builds get a placeholder (for the vendor to replace)
# Point 4 discusses web service terms, unbranded builds gets a placeholder (for the vendor to replace)
<li>&rights.intro-point2-a;<a href="http://www.mozilla.org/foundation/trademarks/policy.html">&rights.intro-point2-b;</a>&rights.intro-point2-c;</li>
<li>&rights.intro-point2.5;</li>
<li>&rights2.intro-point3a;<a href="http://www.mozilla.com/legal/privacy/">&rights2.intro-point3b;</a>&rights.intro-point3c;</li>
<li>&rights2.intro-point4a;<a href="about:rights#webservices" onclick="showServices();">&rights.intro-point4b;</a>&rights.intro-point4c;</li>
</ul>
<div id="webservices-container">
<a name="webservices"/>
<h3>&rights2.webservices-header;</h3>
<p>&rights2.webservices-a;<a href="about:rights#disabling-webservices" onclick="showDisablingServices();">&rights2.webservices-b;</a>&rights2.webservices-c;</p>
<div id="disabling-webservices-container" style="margin-left:40px;">
<a name="disabling-webservices"/>
<!-- XXX Safe Browsing is not enabled in Firefox Mobile -->
<!--
<p><strong>&rights.safebrowsing-a;</strong>&rights.safebrowsing-b;</p>
<ul>
<li>&rights.safebrowsing-term1;</li>
<li>&rights.safebrowsing-term2;</li>
<li>&rights.safebrowsing-term3;</li>
<li>&rights.safebrowsing-term4;</li>
</ul>
-->
<p><strong>&rights.locationawarebrowsing-a;</strong>&rights.locationawarebrowsing-b;</p>
<ul>
<li>&rights.locationawarebrowsing-term1a;<code>&rights.locationawarebrowsing-term1b;</code></li>
<li>&rights.locationawarebrowsing-term2;</li>
<li>&rights.locationawarebrowsing-term3;</li>
<li>&rights.locationawarebrowsing-term4;</li>
</ul>
</div>
<ol>
# Terms only apply to official builds, unbranded builds get a placeholder.
<li>&rights2.webservices-term1;</li>
<li>&rights.webservices-term2;</li>
<li>&rights2.webservices-term3;</li>
<li><strong>&rights.webservices-term4;</strong></li>
<li><strong>&rights.webservices-term5;</strong></li>
<li>&rights.webservices-term6;</li>
<li>&rights.webservices-term7;</li>
</ol>
</div>
<script type="application/javascript"><![CDATA[
var servicesDiv = document.getElementById("webservices-container");
servicesDiv.style.display = "none";
function showServices() {
servicesDiv.style.display = "";
}
var disablingServicesDiv = document.getElementById("disabling-webservices-container");
disablingServicesDiv.style.display = "none";
function showDisablingServices() {
disablingServicesDiv.style.display = "";
}
]]></script>
</body>
</html>

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

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % blockedSiteDTD SYSTEM "chrome://browser/locale/phishing.dtd">
%blockedSiteDTD;
]>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml" class="blacklist">
<head>
<meta name="viewport" content="width=device-width; user-scalable=false" />
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/blacklist_favicon.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// about:blocked?e=error_code&u=url
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getErrorCode()
{
var url = document.documentURI;
var error = url.search(/e\=/);
var duffUrl = url.search(/\&u\=/);
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
function getURL()
{
var url = document.documentURI;
var match = url.match(/&u=([^&]+)&/);
// match == null if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (!match)
return "";
url = decodeURIComponent(match[1]);
// If this is a view-source page, then get then real URI of the page
if (/^view-source\:/.test(url))
url = url.slice(12);
return url;
}
/**
* Attempt to get the hostname via document.location. Fail back
* to getURL so that we always return something meaningful.
*/
function getHostString()
{
try {
return document.location.hostname;
} catch (e) {
return getURL();
}
}
function initPage()
{
// Handoff to the appropriate initializer, based on error code
switch (getErrorCode()) {
case "malwareBlocked" :
initPage_malware();
break;
case "phishingBlocked" :
initPage_phishing();
break;
}
}
/**
* Initialize custom strings and functionality for blocked malware case
*/
function initPage_malware()
{
// Remove phishing strings
var el = document.getElementById("errorTitleText_phishing");
el.parentNode.removeChild(el);
el = document.getElementById("errorShortDescText_phishing");
el.parentNode.removeChild(el);
el = document.getElementById("errorLongDescText_phishing");
el.parentNode.removeChild(el);
// Set sitename
document.getElementById("malware_sitename").textContent = getHostString();
document.title = document.getElementById("errorTitleText_malware")
.innerHTML;
}
/**
* Initialize custom strings and functionality for blocked phishing case
*/
function initPage_phishing()
{
// Remove malware strings
var el = document.getElementById("errorTitleText_malware");
el.parentNode.removeChild(el);
el = document.getElementById("errorShortDescText_malware");
el.parentNode.removeChild(el);
el = document.getElementById("errorLongDescText_malware");
el.parentNode.removeChild(el);
// Set sitename
document.getElementById("phishing_sitename").textContent = getHostString();
document.title = document.getElementById("errorTitleText_phishing")
.innerHTML;
}
]]></script>
<style type="text/css">
/* Style warning button to look like a small text link in the
bottom right. This is preferable to just using a text link
since there is already a mechanism in browser.js for trapping
oncommand events from unprivileged chrome pages (BrowserOnCommand).*/
#ignoreWarningButton {
-moz-appearance: none;
background: transparent;
border: none;
color: white; /* Hard coded because netError.css forces this page's background to dark red */
text-decoration: underline;
margin: 0;
padding: 0;
position: relative;
top: 23px;
left: 20px;
font-size: smaller;
}
#ignoreWarning {
text-align: right;
}
</style>
</head>
<body id="errorPage" class="blockedsite" dir="&locale.dir;">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText_phishing" class="errorTitleText">&safeb.blocked.phishingPage.title2;</h1>
<h1 id="errorTitleText_malware" class="errorTitleText">&safeb.blocked.malwarePage.title;</h1>
</div>
<div id="errorPageContainer">
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc2;</p>
<p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
</div>
<!-- Long Description -->
<div id="errorLongDesc">
<p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc2;</p>
<p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
</div>
<!-- Action buttons -->
<div id="buttons">
<!-- Commands handled in browser.js -->
<button id="getMeOutButton">&safeb.palm.accept.label;</button>
<button id="reportButton">&safeb.palm.reportPage.label;</button>
</div>
</div>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

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

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<!--
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<html xmlns="http://www.w3.org/1999/xhtml" accelerated="11">
<head>
<style type="text/css"><![CDATA[
html,
body,
video {
height: 100%;
}
body {
margin: 0;
background: black;
overflow: -moz-hidden-unscrollable;
}
video {
width: 100%;
max-height: 100%;
}
]]></style>
<script type="application/javascript;version=1.8"><![CDATA[
var TheaterTab = {
videoElement: null,
idleTimer: 0,
init: function init() {
this.videoElement = document.querySelector("video");
/*
* video events
*/
this.videoElement.addEventListener("loadeddata", TheaterTab.loadDataCallback, false);
this.videoElement.addEventListener("seeked", TheaterTab.seekComplete, false);
this.videoElement.addEventListener("pause", TheaterTab.pauseCallback, false);
this.videoElement.addEventListener("play", TheaterTab.playCallback, false);
this.videoElement.addEventListener("ended", TheaterTab.endedCallback, false);
/*
* window events
*/
window.addEventListener("click", function () {
TheaterTab.togglePlay();
TheaterTab.resetIdleTimer();
}, false);
window.addEventListener("unload", function () {
if (TheaterTab.videoElement.currentSrc) {
contentVideo.currentTime = TheaterTab.videoElement.currentTime;
contentVideo.volume = TheaterTab.videoElement.volume;
contentVideo.muted = TheaterTab.videoElement.muted;
if (!TheaterTab.videoElement.paused && !TheaterTab.videoElement.ended) {
TheaterTab.videoElement.pause();
}
}
}, false);
window.addEventListener("keypress", function (event) {
TheaterTab.resetIdleTimer();
}, false);
// Load the video up and play it
this.videoElement.mozLoadFrom(contentVideo);
},
/*
* Video element callbacks
*/
loadDataCallback: function loadDataCallback() {
dump("loadDataCallback()\n");
TheaterTab.videoElement.removeEventListener("loadeddata", arguments.callee, false);
TheaterTab.videoElement.volume = contentVideo.volume;
TheaterTab.videoElement.muted = contentVideo.muted;
TheaterTab.videoElement.poster = contentVideo.poster;
// If we are starting from mid stream, wait until we have
// seeked to the start location.
dump(contentVideo + "\n");
if (contentVideo.currentTime && !contentVideo.ended) {
// set up callback to play
TheaterTab.videoElement.addEventListener("seeked", function () {
TheaterTab.videoElement.removeEventListener("seeked", arguments.callee, false);
TheaterTab.seekComplete();
}, false);
// seek
TheaterTab.videoElement.currentTime = contentVideo.currentTime;
return;
}
TheaterTab.play();
},
seekComplete: function seekComplete() {
TheaterTab.play();
},
pauseCallback: function pauseCallback() {
},
playCallback: function playCallback() {
},
endedCallback: function endedCallback() {
},
/*
* Video control
*/
play: function play() {
this.videoElement.play();
},
pause: function pause() {
this.videoElement.pause();
},
togglePlay: function togglePlay() {
if (this.videoElement.paused) {
this.play();
} else {
this.pause();
}
},
};
Components.utils.import("resource:///modules/video.jsm");
// The video in the content tab we launched from
var contentVideo = Video.fullScreenSourceElement;
// ??
Video.fullScreenSourceElement = null;
]]></script>
</head>
<body onload="TheaterTab.init();">
<video controls="true"/>
</body>
</html>

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

@ -0,0 +1,365 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % netErrorDTD
SYSTEM "chrome://global/locale/netError.dtd">
%netErrorDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width; user-scalable=false;" />
<title>&loadError.label;</title>
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
<!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
toolkit/components/places/src/nsFaviconService.h should be updated. -->
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// moz-neterror:page?e=error&u=url&d=desc
//
// or optionally, to specify an alternate CSS class to allow for
// custom styling and favicon:
//
// moz-neterror:page?e=error&u=url&s=classname&d=desc
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getErrorCode()
{
var url = document.documentURI;
var error = url.search(/e\=/);
var duffUrl = url.search(/\&u\=/);
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
function getCSSClass()
{
var url = document.documentURI;
var matches = url.match(/s\=([^&]+)\&/);
// s is optional, if no match just return nothing
if (!matches || matches.length < 2)
return "";
// parenthetical match is the second entry
return decodeURIComponent(matches[1]);
}
function getDescription()
{
var url = document.documentURI;
var desc = url.search(/d\=/);
// desc == -1 if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (desc == -1)
return "";
return decodeURIComponent(url.slice(desc + 2));
}
function retryThis(buttonEl)
{
// Note: The application may wish to handle switching off "offline mode"
// before this event handler runs, but using a capturing event handler.
// Session history has the URL of the page that failed
// to load, not the one of the error page. So, just call
// reload(), which will also repost POST data correctly.
try {
location.reload();
} catch (e) {
// We probably tried to reload a URI that caused an exception to
// occur; e.g. a nonexistent file.
}
buttonEl.disabled = true;
}
function initPage()
{
var err = getErrorCode();
// if it's an unknown error or there's no title or description
// defined, get the generic message
var errTitle = document.getElementById("et_" + err);
var errDesc = document.getElementById("ed_" + err);
if (!errTitle || !errDesc)
{
errTitle = document.getElementById("et_generic");
errDesc = document.getElementById("ed_generic");
}
var title = document.getElementsByClassName("errorTitleText")[0];
if (title)
{
title.parentNode.replaceChild(errTitle, title);
// change id to the replaced child's id so styling works
errTitle.classList.add("errorTitleText");
}
var sd = document.getElementById("errorShortDescText");
if (sd)
sd.textContent = getDescription();
var ld = document.getElementById("errorLongDesc");
if (ld)
{
ld.parentNode.replaceChild(errDesc, ld);
// change id to the replaced child's id so styling works
errDesc.id = "errorLongDesc";
}
// remove undisplayed errors to avoid bug 39098
var errContainer = document.getElementById("errorContainer");
errContainer.parentNode.removeChild(errContainer);
var className = getCSSClass();
if (className && className != "expertBadCert") {
// Associate a CSS class with the root of the page, if one was passed in,
// to allow custom styling.
// Not "expertBadCert" though, don't want to deal with the favicon
document.documentElement.className = className;
// Also, if they specified a CSS class, they must supply their own
// favicon. In order to trigger the browser to repaint though, we
// need to remove/add the link element.
var favicon = document.getElementById("favicon");
var faviconParent = favicon.parentNode;
faviconParent.removeChild(favicon);
favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
faviconParent.appendChild(favicon);
}
if (className == "expertBadCert") {
showSecuritySection();
}
if (err == "remoteXUL") {
// Remove the "Try again" button for remote XUL errors given that
// it is useless.
document.getElementById("errorTryAgain").style.display = "none";
}
if (err == "cspFrameAncestorBlocked") {
// Remove the "Try again" button for CSP frame ancestors violation, since it's
// almost certainly useless. (Bug 553180)
document.getElementById("errorTryAgain").style.display = "none";
}
if (err == "nssBadCert") {
// Remove the "Try again" button for security exceptions, since it's
// almost certainly useless.
document.getElementById("errorTryAgain").style.display = "none";
document.getElementById("errorPage").setAttribute("class", "certerror");
addDomainErrorLink();
}
else {
// Remove the override block for non-certificate errors. CSS-hiding
// isn't good enough here, because of bug 39098
var secOverride = document.getElementById("securityOverrideDiv");
secOverride.parentNode.removeChild(secOverride);
}
}
function showSecuritySection() {
// Swap link out, content in
document.getElementById('securityOverrideContent').style.display = '';
document.getElementById('securityOverrideLink').style.display = 'none';
}
/* In the case of SSL error pages about domain mismatch, see if
we can hyperlink the user to the correct site. We don't want
to do this generically since it allows MitM attacks to redirect
users to a site under attacker control, but in certain cases
it is safe (and helpful!) to do so. Bug 402210
*/
function addDomainErrorLink() {
// Rather than textContent, we need to treat description as HTML
var sd = document.getElementById("errorShortDescText");
if (sd) {
var desc = getDescription();
// sanitize description text - see bug 441169
// First, find the index of the <a> tag we care about, being careful not to
// use an over-greedy regex
var re = /<a id="cert_domain_link" title="([^"]+)">/;
var result = re.exec(desc);
if(!result)
return;
// Remove sd's existing children
sd.textContent = "";
// Everything up to the link should be text content
sd.appendChild(document.createTextNode(desc.slice(0, result.index)));
// Now create the link itself
var anchorEl = document.createElement("a");
anchorEl.setAttribute("id", "cert_domain_link");
anchorEl.setAttribute("title", result[1]);
anchorEl.appendChild(document.createTextNode(result[1]));
sd.appendChild(anchorEl);
// Finally, append text for anything after the closing </a>
sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length)));
}
var link = document.getElementById('cert_domain_link');
if (!link)
return;
var okHost = link.getAttribute("title");
var thisHost = document.location.hostname;
var proto = document.location.protocol;
// If okHost is a wildcard domain ("*.example.com") let's
// use "www" instead. "*.example.com" isn't going to
// get anyone anywhere useful. bug 432491
okHost = okHost.replace(/^\*\./, "www.");
/* case #1:
* example.com uses an invalid security certificate.
*
* The certificate is only valid for www.example.com
*
* Make sure to include the "." ahead of thisHost so that
* a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
*
* We'd normally just use a RegExp here except that we lack a
* library function to escape them properly (bug 248062), and
* domain names are famous for having '.' characters in them,
* which would allow spurious and possibly hostile matches.
*/
if (endsWith(okHost, "." + thisHost))
link.href = proto + okHost;
/* case #2:
* browser.garage.maemo.org uses an invalid security certificate.
*
* The certificate is only valid for garage.maemo.org
*/
if (endsWith(thisHost, "." + okHost))
link.href = proto + okHost;
}
function endsWith(haystack, needle) {
return haystack.slice(-needle.length) == needle;
}
]]></script>
</head>
<body id="errorPage" dir="&locale.dir;">
<!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
<div id="errorContainer">
<div id="errorTitlesContainer">
<h1 id="et_generic">&generic.title;</h1>
<h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
<h1 id="et_fileNotFound">&fileNotFound.title;</h1>
<h1 id="et_malformedURI">&malformedURI.title;</h1>
<h1 id="et_protocolNotFound">&protocolNotFound.title;</h1>
<h1 id="et_connectionFailure">&connectionFailure.title;</h1>
<h1 id="et_netTimeout">&netTimeout.title;</h1>
<h1 id="et_redirectLoop">&redirectLoop.title;</h1>
<h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
<h1 id="et_netReset">&netReset.title;</h1>
<h1 id="et_notCached">&notCached.title;</h1>
<h1 id="et_netOffline">&netOffline.title;</h1>
<h1 id="et_netInterrupt">&netInterrupt.title;</h1>
<h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
<h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
<h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
<h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
<h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
<h1 id="et_nssBadCert">&nssBadCert.title;</h1>
<h1 id="et_cspFrameAncestorBlocked">&cspFrameAncestorBlocked.title;</h1>
<h1 id="et_remoteXUL">&remoteXUL.title;</h1>
<h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
</div>
<div id="errorDescriptionsContainer">
<div id="ed_generic">&generic.longDesc;</div>
<div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
<div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
<div id="ed_malformedURI">&malformedURI.longDesc;</div>
<div id="ed_protocolNotFound">&protocolNotFound.longDesc;</div>
<div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
<div id="ed_netTimeout">&netTimeout.longDesc;</div>
<div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
<div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
<div id="ed_netReset">&netReset.longDesc;</div>
<div id="ed_notCached">&notCached.longDesc;</div>
<div id="ed_netOffline">&netOffline.longDesc2;</div>
<div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
<div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
<div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
<div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
<div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
<div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
<div id="ed_nssFailure2">&nssFailure2.longDesc;</div>
<div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
<div id="ed_cspFrameAncestorBlocked">&cspFrameAncestorBlocked.longDesc;</div>
<div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
<div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div>
</div>
</div>
<!-- Error Title -->
<div id="errorTitle">
<h1 class="errorTitleText" />
</div>
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText" />
</div>
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
<div id="errorLongDesc" />
<!-- Override section - For ssl errors only. Removed on init for other
error types. -->
<div id="securityOverrideDiv">
<a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
<div id="securityOverrideContent" style="display: none;">&securityOverride.warningContent;</div>
</div>
</div>
<!-- Retry Button -->
<button id="errorTryAgain" onclick="retryThis(this);">&retry.label;</button>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

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

@ -0,0 +1,17 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var PreferencesPanelView = {
init: function pv_init() {
// Run some setup code the first time the panel is shown.
Elements.prefsFlyout.addEventListener("PopupChanged", function onShow(aEvent) {
if (aEvent.detail && aEvent.popup === Elements.prefsFlyout) {
Elements.prefsFlyout.removeEventListener("PopupChanged", onShow, false);
MasterPasswordUI.updatePreference();
WeaveGlue.init();
}
}, false);
}
};

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

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="capturepicker-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('capturepicker-dialog').CaptureDialog.init()"
script="chrome://browser/content/CaptureDialog.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('capturepicker-dialog').CaptureDialog.capture();"/>
<command id="cmd_cancel" oncommand="document.getElementById('capturepicker-dialog').CaptureDialog.cancel();"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="capturepicker-title" class="prompt-title" crop="center" flex="1"/>
<hbox flex="1" pack="center">
<vbox id="capturepicker-container" pack="center">
<video xmlns="http://www.w3.org/1999/xhtml" id="capturepicker-video"
width="320" height="240"
src="moz-device:?width=320&amp;height=240&amp;type=video/x-raw-yuv"
autoplay="true"> </video>
</vbox>
</hbox>
</vbox>
<hbox id="capturepicker-buttons-box" class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-alert-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('prompt-button-ok').focus()"
onclose="this.PromptHelper.onCloseAlert(this);"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-alert-dialog').close()"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-alert-dialog').close()"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-alert-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-alert-message"/>
</scrollbox>
<checkbox id="prompt-alert-checkbox" collapsed="true" pack="start" flex="1" />
</vbox>
<hbox class="prompt-buttons">
<button id="prompt-button-ok" class="prompt-button" label="&ok.label;" command="cmd_ok"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-confirm-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementsByAttribute('command', 'cmd_ok')[0].focus()"
onclose="this.PromptHelper.onCloseConfirm(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-confirm-dialog').PromptHelper.closeConfirm(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-confirm-dialog').PromptHelper.closeConfirm(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-confirm-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-confirm-message"/>
</scrollbox>
<checkbox id="prompt-confirm-checkbox" collapsed="true" pack="start" flex="1"/>
</vbox>
<hbox id="prompt-confirm-buttons-box" class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,45 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE dialog [
<!ENTITY % dialog SYSTEM "chrome://browser/locale/prompt.dtd">
<!ENTITY % changempDTD SYSTEM "chrome://mozapps/locale/preferences/changemp.dtd" >
%dialog;
%changempDTD;
]>
<dialog id="masterpassword-change" title="&setPassword.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="MasterPasswordUI.setPassword();"/>
<command id="cmd_cancel" oncommand="MasterPasswordUI.hide();"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="masterpassword-title" class="prompt-title" crop="center" flex="1">&setPassword.title;</description>
</vbox>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<label control="masterpassword-newpassword1" value="&setPassword.newPassword.label;"/>
<textbox id="masterpassword-newpassword1" type="password" oninput="MasterPasswordUI.checkPassword();" flex="1"/>
<label control="masterpassword-newpassword2" value="&setPassword.reenterPassword.label;"/>
<textbox id="masterpassword-newpassword2" type="password" oninput="MasterPasswordUI.checkPassword();" flex="1"/>
<separator/>
</scrollbox>
<hbox class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,72 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const PromptHelper = {
closeDialog: function(confirm, id) {
let dialog = document.getElementById(id);
if (typeof confirm == "boolean" && dialog.arguments && "defaultButton" in dialog.arguments)
// confirmEx always returns 1 when dismissed with "escape" (bug 345067).
dialog.arguments.result = confirm ? dialog.arguments.defaultButton : 1;
else
dialog.arguments.result = confirm;
dialog.close();
},
// Alert dialog
onCloseAlert: function(dialog) {
if (dialog.arguments)
dialog.arguments.value = document.getElementById("prompt-alert-checkbox").checked;
},
// Confirm dialog
closeConfirm: function(confirm) {
this.closeDialog(confirm, "prompt-confirm-dialog");
},
onCloseConfirm: function(dialog) {
if (dialog.arguments && ("checkbox" in dialog.arguments))
dialog.arguments.checkbox.value = document.getElementById("prompt-confirm-checkbox").checked;
},
// Prompt dialog
closePrompt: function(confirm) {
this.closeDialog(confirm, "prompt-prompt-dialog");
},
onClosePrompt: function(dialog) {
if (dialog.arguments) {
dialog.arguments.checkbox.value = document.getElementById("prompt-prompt-checkbox").checked;
dialog.arguments.value.value = document.getElementById("prompt-prompt-textbox").value;
}
},
// User / Password dialog
onLoadPassword: function onLoadPassword(dialog) {
let user = document.getElementById('prompt-password-user');
if (!user.value)
user.focus();
},
closePassword: function(confirm) {
this.closeDialog(confirm, "prompt-password-dialog");
},
onClosePassword: function(dialog) {
if (dialog.arguments) {
dialog.arguments.checkbox.value = document.getElementById("prompt-password-checkbox").checked;
dialog.arguments.user.value = document.getElementById("prompt-password-user").value;
dialog.arguments.password.value = document.getElementById("prompt-password-password").value;
}
},
// Select dialog
closeSelect: function(confirm) {
this.closeDialog(confirm, "prompt-select-dialog");
},
onCloseSelect: function(dialog) {
if (dialog.arguments)
dialog.arguments.selection.value = document.getElementById("prompt-select-list").selectedIndex;
}
};

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

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-prompt-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('prompt-prompt-textbox').focus()"
onclose="this.PromptHelper.onClosePrompt(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-prompt-dialog').PromptHelper.closePrompt(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-prompt-dialog').PromptHelper.closePrompt(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-prompt-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-prompt-message"/>
</scrollbox>
<textbox id="prompt-prompt-textbox" class="prompt-edit"/>
<button id="prompt-prompt-checkbox" collapsed="true" pack="start" flex="1"/>
</vbox>
<hbox class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,63 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE prompt [
<!ENTITY % promptDTD SYSTEM "chrome://browser/locale/prompt.dtd">
<!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd">
%promptDTD;
%commonDialogDTD;
]>
<dialog id="prompt-password-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="this.PromptHelper.onLoadPassword(this)"
onclose="this.PromptHelper.onClosePassword(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-password-dialog').PromptHelper.closePassword(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-password-dialog').PromptHelper.closePassword(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-password-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-password-message"/>
</scrollbox>
<grid class="prompt-message">
<columns>
<column flex="1"/>
<column flex="1"/>
</columns>
<rows>
<row align="center">
<label value="&editfield0.label;"/>
<textbox id="prompt-password-user" class="prompt-edit"/>
</row>
<row align="center">
<label value="&editfield1.label;"/>
<textbox type="password" id="prompt-password-password" class="prompt-edit"/>
</row>
</rows>
</grid>
<checkbox id="prompt-password-checkbox" collapsed="true" pack="start" flex="1"/>
</vbox>
<hbox class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE dialog [
<!ENTITY % dialog SYSTEM "chrome://browser/locale/prompt.dtd">
<!ENTITY % removempDTD SYSTEM "chrome://mozapps/locale/preferences/removemp.dtd" >
%dialog;
%removempDTD;
]>
<dialog id="masterpassword-remove" title="&removePassword.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="MasterPasswordUI.removePassword();"/>
<command id="cmd_cancel" oncommand="MasterPasswordUI.hide();"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="masterpassword-title" class="prompt-title" crop="center" flex="1">&removePassword.title;</description>
</vbox>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<label control="masterpassword-oldpassword" value="&setPassword.oldPassword.label;"/>
<textbox id="masterpassword-oldpassword" type="password" oninput="MasterPasswordUI.checkOldPassword();" flex="1"/>
<separator/>
</scrollbox>
<hbox class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" disabled="true" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="prompt-select-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="document.getElementById('prompt-select-list').focus()"
onclose="this.PromptHelper.onCloseSelect(this)"
script="chrome://browser/content/prompt/prompt.js">
<keyset>
<key keycode="VK_RETURN" command="cmd_ok"/>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_ok" oncommand="document.getElementById('prompt-select-dialog').PromptHelper.closeSelect(true)"/>
<command id="cmd_cancel" oncommand="document.getElementById('prompt-select-dialog').PromptHelper.closeSelect(false)"/>
</commandset>
<vbox class="prompt-inner">
<vbox class="prompt-header" flex="1">
<description id="prompt-select-title" class="prompt-title" crop="center" flex="1"/>
<scrollbox orient="vertical" class="prompt-message" flex="1">
<description id="prompt-select-message"/>
</scrollbox>
<menulist id="prompt-select-list"/>
</vbox>
<hbox class="prompt-buttons">
<button class="prompt-button" label="&ok.label;" command="cmd_ok"/>
<button class="prompt-button" label="&cancel.label;" command="cmd_cancel"/>
</hbox>
</vbox>
</dialog>

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

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE prompt SYSTEM "chrome://browser/locale/prompt.dtd">
<dialog id="share-dialog"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<keyset>
<key keycode="VK_ESCAPE" command="cmd_cancel"/>
</keyset>
<commandset>
<command id="cmd_cancel" oncommand="SharingUI.hide();"/>
</commandset>
<vbox class="prompt-inner">
<hbox class="prompt-header">
<label id="share-title" crop="center" flex="1"/>
</hbox>
<hbox id="share-buttons-box" class="prompt-buttons"/>
</vbox>
</dialog>

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

@ -0,0 +1,315 @@
// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function Sanitizer() {}
Sanitizer.prototype = {
// warning to the caller: this one may raise an exception (e.g. bug #265028)
clearItem: function (aItemName)
{
if (this.items[aItemName].canClear)
this.items[aItemName].clear();
},
canClearItem: function (aItemName)
{
return this.items[aItemName].canClear;
},
_prefDomain: "privacy.item.",
getNameFromPreference: function (aPreferenceName)
{
return aPreferenceName.substr(this._prefDomain.length);
},
/**
* Deletes privacy sensitive data in a batch, according to user preferences
*
* @returns null if everything's fine; an object in the form
* { itemName: error, ... } on (partial) failure
*/
sanitize: function ()
{
var branch = Services.prefs.getBranch(this._prefDomain);
var errors = null;
for (var itemName in this.items) {
var item = this.items[itemName];
if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) {
// Some of these clear() may raise exceptions (see bug #265028)
// to sanitize as much as possible, we catch and store them,
// rather than fail fast.
// Callers should check returned errors and give user feedback
// about items that could not be sanitized
try {
item.clear();
} catch(er) {
if (!errors)
errors = {};
errors[itemName] = er;
dump("Error sanitizing " + itemName + ": " + er + "\n");
}
}
}
return errors;
},
items: {
// Clear Sync account before passwords so that Sync still has access to the
// credentials to clean up device-specific records on the server. Also
// disable it before wiping history so we don't accidentally sync that.
syncAccount: {
clear: function ()
{
WeaveGlue.disconnect();
},
get canClear()
{
return (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED);
}
},
cache: {
clear: function ()
{
var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
try {
cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE);
} catch(er) {}
let imageCache = Cc["@mozilla.org/image/cache;1"].getService(Ci.imgICache);
try {
imageCache.clearCache(false); // true=chrome, false=content
} catch(er) {}
},
get canClear()
{
return true;
}
},
cookies: {
clear: function ()
{
var cookieMgr = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
cookieMgr.removeAll();
},
get canClear()
{
return true;
}
},
geolocation: {
clear: function ()
{
// clear any network geolocation provider sessions
try {
var branch = Services.prefs.getBranch("geo.wifi.access_token.");
branch.deleteBranch("");
branch = Services.prefs.getBranch("geo.request.remember.");
branch.deleteBranch("");
} catch (e) {dump(e);}
},
get canClear()
{
return true;
}
},
siteSettings: {
clear: function ()
{
// Clear site-specific permissions like "Allow this site to open popups"
Services.perms.removeAll();
// Clear site-specific settings like page-zoom level
var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService);
cps.removeGroupedPrefs();
// Clear "Never remember passwords for this site", which is not handled by
// the permission manager
var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
var hosts = pwmgr.getAllDisabledHosts({})
for each (var host in hosts) {
pwmgr.setLoginSavingEnabled(host, true);
}
},
get canClear()
{
return true;
}
},
offlineApps: {
clear: function ()
{
var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
try {
cacheService.evictEntries(Ci.nsICache.STORE_OFFLINE);
} catch(er) {}
},
get canClear()
{
return true;
}
},
history: {
clear: function ()
{
var globalHistory = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIBrowserHistory);
globalHistory.removeAllPages();
try {
Services.obs.notifyObservers(null, "browser:purge-session-history", "");
}
catch (e) { }
// Clear last URL of the Open Web Location dialog
try {
Services.prefs.clearUserPref("general.open_location.last_url");
}
catch (e) { }
},
get canClear()
{
// bug 347231: Always allow clearing history due to dependencies on
// the browser:purge-session-history notification. (like error console)
return true;
}
},
formdata: {
clear: function ()
{
//Clear undo history of all searchBars
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
var searchBar = windows.getNext().document.getElementById("searchbar");
if (searchBar) {
searchBar.value = "";
searchBar.textbox.editor.transactionManager.clear();
}
}
var formHistory = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
formHistory.removeAllEntries();
},
get canClear()
{
var formHistory = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
return formHistory.hasEntries;
}
},
downloads: {
clear: function ()
{
var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
dlMgr.cleanUp();
},
get canClear()
{
var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
return dlMgr.canCleanUp;
}
},
passwords: {
clear: function ()
{
var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
pwmgr.removeAllLogins();
},
get canClear()
{
var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
var count = pwmgr.countLogins("", "", ""); // count all logins
return (count > 0);
}
},
sessions: {
clear: function ()
{
// clear all auth tokens
var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
sdr.logoutAndTeardown();
// clear plain HTTP auth sessions
var authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].getService(Ci.nsIHttpAuthManager);
authMgr.clearAll();
},
get canClear()
{
return true;
}
}
}
};
// "Static" members
Sanitizer.prefDomain = "privacy.sanitize.";
Sanitizer.prefShutdown = "sanitizeOnShutdown";
Sanitizer.prefDidShutdown = "didShutdownSanitize";
Sanitizer._prefs = null;
Sanitizer.__defineGetter__("prefs", function()
{
return Sanitizer._prefs ? Sanitizer._prefs
: Sanitizer._prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService)
.getBranch(Sanitizer.prefDomain);
});
/**
* Deletes privacy sensitive data in a batch, optionally showing the
* sanitize UI, according to user preferences
*
* @returns null if everything's fine
* an object in the form { itemName: error, ... } on (partial) failure
*/
Sanitizer.sanitize = function()
{
return new Sanitizer().sanitize();
};
Sanitizer.onStartup = function()
{
// we check for unclean exit with pending sanitization
Sanitizer._checkAndSanitize();
};
Sanitizer.onShutdown = function()
{
// we check if sanitization is needed and perform it
Sanitizer._checkAndSanitize();
};
// this is called on startup and shutdown, to perform pending sanitizations
Sanitizer._checkAndSanitize = function()
{
const prefs = Sanitizer.prefs;
if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
!prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
// this is a shutdown or a startup after an unclean exit
Sanitizer.sanitize() || // sanitize() returns null on full success
prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
}
};

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

@ -0,0 +1,678 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let WeaveGlue = {
setupData: null,
_boundOnEngineSync: null, // Needed to unhook the observers in close().
_boundOnServiceSync: null,
jpake: null,
_bundle: null,
_loginError: false,
_progressBar: null,
_progressValue: 0,
_progressMax: null,
init: function init() {
if (this._bundle) {
return;
}
let service = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
if (service.ready) {
this._init();
return;
}
Services.obs.addObserver(function onReady() {
Services.obs.removeObserver(onReady, "weave:service:ready");
this._init();
}.bind(this), "weave:service:ready", false);
service.ensureLoaded();
},
_init: function () {
this._bundle = Services.strings.createBundle("chrome://browser/locale/sync.properties");
this._msg = document.getElementById("prefs-messages");
this._addListeners();
this.setupData = { account: "", password: "" , synckey: "", serverURL: "" };
if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
// Put the settings UI into a state of "connecting..." if we are going to auto-connect
this._elements.connect.firstChild.disabled = true;
this._elements.connect.setAttribute("title", this._bundle.GetStringFromName("connecting.label"));
try {
this._elements.device.value = Services.prefs.getCharPref("services.sync.client.name");
} catch(e) {}
} else if (Weave.Status.login != Weave.LOGIN_FAILED_NO_USERNAME) {
this.loadSetupData();
}
this._boundOnEngineSync = this.onEngineSync.bind(this);
this._boundOnServiceSync = this.onServiceSync.bind(this);
this._progressBar = document.getElementById("syncsetup-progressbar");
},
abortEasySetup: function abortEasySetup() {
document.getElementById("syncsetup-code1").value = "....";
document.getElementById("syncsetup-code2").value = "....";
document.getElementById("syncsetup-code3").value = "....";
if (!this.jpake)
return;
this.jpake.abort();
this.jpake = null;
},
_resetScrollPosition: function _resetScrollPosition() {
let scrollboxes = document.getElementsByClassName("syncsetup-scrollbox");
for (let i = 0; i < scrollboxes.length; i++) {
let sbo = scrollboxes[i].boxObject.QueryInterface(Ci.nsIScrollBoxObject);
try {
sbo.scrollTo(0, 0);
} catch(e) {}
}
},
open: function open() {
let container = document.getElementById("syncsetup-container");
if (!container.hidden)
return;
// Services.io.offline is lying to us, so we use the NetworkLinkService instead
let nls = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService);
if (!nls.isLinkUp) {
Services.obs.notifyObservers(null, "browser:sync:setup:networkerror", "");
Services.prompt.alert(window,
this._bundle.GetStringFromName("sync.setup.error.title"),
this._bundle.GetStringFromName("sync.setup.error.network"));
return;
}
// Clear up any previous JPAKE codes
this.abortEasySetup();
// Show the connect UI
container.hidden = false;
document.getElementById("syncsetup-simple").hidden = false;
document.getElementById("syncsetup-waiting").hidden = true;
document.getElementById("syncsetup-fallback").hidden = true;
DialogUI.pushDialog(this);
let self = this;
this.jpake = new Weave.JPAKEClient({
displayPIN: function displayPIN(aPin) {
document.getElementById("syncsetup-code1").value = aPin.slice(0, 4);
document.getElementById("syncsetup-code2").value = aPin.slice(4, 8);
document.getElementById("syncsetup-code3").value = aPin.slice(8);
},
onPairingStart: function onPairingStart() {
document.getElementById("syncsetup-simple").hidden = true;
document.getElementById("syncsetup-waiting").hidden = false;
},
onComplete: function onComplete(aCredentials) {
self.jpake = null;
self._progressBar.mode = "determined";
document.getElementById("syncsetup-waiting-desc").hidden = true;
document.getElementById("syncsetup-waiting-cancel").hidden = true;
document.getElementById("syncsetup-waitingdownload-desc").hidden = false;
document.getElementById("syncsetup-waiting-close").hidden = false;
Services.obs.addObserver(self._boundOnEngineSync, "weave:engine:sync:finish", false);
Services.obs.addObserver(self._boundOnEngineSync, "weave:engine:sync:error", false);
Services.obs.addObserver(self._boundOnServiceSync, "weave:service:sync:finish", false);
Services.obs.addObserver(self._boundOnServiceSync, "weave:service:sync:error", false);
self.setupData = aCredentials;
self.connect();
},
onAbort: function onAbort(aError) {
self.jpake = null;
if (aError == "jpake.error.userabort" || container.hidden) {
Services.obs.notifyObservers(null, "browser:sync:setup:userabort", "");
return;
}
// Automatically go to manual setup if we couldn't acquire a channel.
let brandShortName = Strings.brand.GetStringFromName("brandShortName");
let tryAgain = self._bundle.GetStringFromName("sync.setup.tryagain");
let manualSetup = self._bundle.GetStringFromName("sync.setup.manual");
let buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
(Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
(Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
(Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_2);
let button = Services.prompt.confirmEx(window,
self._bundle.GetStringFromName("sync.setup.error.title"),
self._bundle.formatStringFromName("sync.setup.error.nodata", [brandShortName], 1),
buttonFlags, tryAgain, manualSetup, null, "", {});
switch (button) {
case 0:
// we have to build a new JPAKEClient here rather than reuse the old one
container.hidden = true;
self.open();
break;
case 1:
self.openManual();
break;
case 2:
default:
self.close();
break;
}
}
});
this.jpake.receiveNoPIN();
},
openManual: function openManual() {
this.abortEasySetup();
// Reset the scroll since the previous page might have been scrolled
this._resetScrollPosition();
document.getElementById("syncsetup-simple").hidden = true;
document.getElementById("syncsetup-waiting").hidden = true;
document.getElementById("syncsetup-fallback").hidden = false;
// Push the current setup data into the UI
if (this.setupData && "account" in this.setupData) {
this._elements.account.value = this.setupData.account;
this._elements.password.value = this.setupData.password;
let pp = this.setupData.synckey;
if (Weave.Utils.isPassphrase(pp))
pp = Weave.Utils.hyphenatePassphrase(pp);
this._elements.synckey.value = pp;
if (this.setupData.serverURL && this.setupData.serverURL.length) {
this._elements.usecustomserver.checked = true;
this._elements.customserver.disabled = false;
this._elements.customserver.value = this.setupData.serverURL;
} else {
this._elements.usecustomserver.checked = false;
this._elements.customserver.disabled = true;
this._elements.customserver.value = "";
}
}
this.canConnect();
},
onEngineSync: function onEngineSync(subject, topic, data) {
// The Clients engine syncs first. At this point we don't necessarily know
// yet how many engines will be enabled, so we'll ignore the Clients engine
// and evaluate how many engines are enabled when the first "real" engine
// syncs.
if (data == 'clients') {
return;
}
if (this._progressMax == null) {
this._progressMax = Weave.Service.engineManager.getEnabled().length;
this._progressBar.max = this._progressMax;
}
this._progressValue += 1;
this._progressBar.setAttribute("value", this._progressValue);
},
onServiceSync: function onServiceSync() {
this.close();
},
close: function close() {
try {
Services.obs.removeObserver(this._boundOnEngineSync, "weave:engine:sync:finish");
Services.obs.removeObserver(this._boundOnEngineSync, "weave:engine:sync:error");
Services.obs.removeObserver(this._boundOnServiceSync, "weave:service:sync:finish");
Services.obs.removeObserver(this._boundOnServiceSync, "weave:service:sync:error");
}
catch(e) {
// Observers weren't registered because we never got as far as onComplete.
}
if (this.jpake)
this.abortEasySetup();
// Reset the scroll since the previous page might have been scrolled
this._resetScrollPosition();
// Save current setup data
this.setupData = {
account: this._elements.account.value.trim(),
password: this._elements.password.value.trim(),
synckey: Weave.Utils.normalizePassphrase(this._elements.synckey.value.trim()),
serverURL: this._validateServer(this._elements.customserver.value.trim())
};
// Clear the UI so it's ready for next time
this._elements.account.value = "";
this._elements.password.value = "";
this._elements.synckey.value = "";
this._elements.usecustomserver.checked = false;
this._elements.customserver.disabled = true;
this._elements.customserver.value = "";
document.getElementById("syncsetup-waiting-desc").hidden = false;
document.getElementById("syncsetup-waiting-cancel").hidden = false;
document.getElementById("syncsetup-waitingdownload-desc").hidden = true;
document.getElementById("syncsetup-waiting-close").hidden = true;
this._progressMax = null;
this._progressValue = 0;
this._progressBar.max = 0;
this._progressBar.value = 0;
this._progressBar.mode = "undetermined";
// Close the connect UI
document.getElementById("syncsetup-container").hidden = true;
DialogUI.popDialog();
},
toggleCustomServer: function toggleCustomServer() {
let useCustomServer = this._elements.usecustomserver.checked;
this._elements.customserver.disabled = !useCustomServer;
if (!useCustomServer)
this._elements.customserver.value = "";
},
canConnect: function canConnect() {
let account = this._elements.account.value;
let password = this._elements.password.value;
let synckey = this._elements.synckey.value;
let disabled = !(account && password && synckey);
document.getElementById("syncsetup-button-connect").disabled = disabled;
},
showDetails: function showDetails() {
// Show the connect UI detail settings
let show = this._elements.sync.collapsed;
this._elements.details.checked = show;
this._elements.sync.collapsed = !show;
this._elements.device.collapsed = !show;
this._elements.disconnect.collapsed = !show;
},
tryConnect: function login() {
// If Sync is not configured, simply show the setup dialog
if (this._loginError || Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED) {
this.open();
return;
}
// No setup data, do nothing
if (!this.setupData)
return;
if (this.setupData.serverURL && this.setupData.serverURL.length)
Weave.Service.serverURL = this.setupData.serverURL;
// We might still be in the middle of a sync from before Sync was disabled, so
// let's force the UI into a state that the Sync code feels comfortable
this.observe(null, "", "");
// Now try to re-connect. If successful, this will reset the UI into the
// correct state automatically.
Weave.Service.login(Weave.Service.identity.username, this.setupData.password, this.setupData.synckey);
},
connect: function connect(aSetupData) {
// Use setup data to pre-configure manual fields
if (aSetupData)
this.setupData = aSetupData;
// Cause the Sync system to reset internals if we seem to be switching accounts
if (this.setupData.account != Weave.Service.identity.account)
Weave.Service.startOver();
// Remove any leftover connection error string
this._elements.connect.removeAttribute("desc");
// Reset the custom server URL, if we have one
if (this.setupData.serverURL && this.setupData.serverURL.length)
Weave.Service.serverURL = this.setupData.serverURL;
// Sync will use the account value and munge it into a username, as needed
Weave.Service.identity.account = this.setupData.account;
Weave.Service.identity.basicPassword = this.setupData.password;
Weave.Service.identity.syncKey = this.setupData.synckey;
Weave.Service.persistLogin();
Weave.Svc.Obs.notify("weave:service:setup-complete");
setTimeout(function () { Weave.Service.sync(); }, 0);
},
disconnect: function disconnect() {
// Save credentials for undo
let undoData = this.setupData;
// Remove all credentials
this.setupData = null;
Weave.Service.startOver();
let message = this._bundle.GetStringFromName("notificationDisconnect.label");
let button = this._bundle.GetStringFromName("notificationDisconnect.button");
let buttons = [ {
label: button,
accessKey: "",
callback: function() { WeaveGlue.connect(undoData); }
} ];
this.showMessage(message, "undo-disconnect", buttons);
// Hide the notification when the panel is changed or closed.
let panel = document.getElementById("prefs-container");
panel.addEventListener("ToolPanelHidden", function onHide(aEvent) {
panel.removeEventListener(aEvent.type, onHide, false);
let notification = WeaveGlue._msg.getNotificationWithValue("undo-disconnect");
if (notification)
notification.close();
}, false);
Weave.Service.logout();
},
sync: function sync() {
Weave.Service.sync();
},
_addListeners: function _addListeners() {
let topics = ["weave:service:setup-complete",
"weave:service:sync:start", "weave:service:sync:finish",
"weave:service:sync:error", "weave:service:login:start",
"weave:service:login:finish", "weave:service:login:error",
"weave:ui:login:error",
"weave:service:logout:finish"];
// For each topic, add WeaveGlue the observer
topics.forEach(function(topic) {
Services.obs.addObserver(WeaveGlue, topic, false);
});
// Remove them on unload
addEventListener("unload", function() {
topics.forEach(function(topic) {
Services.obs.removeObserver(WeaveGlue, topic, false);
});
}, false);
},
get _elements() {
// Do a quick test to see if the options exist yet
let syncButton = document.getElementById("sync-syncButton");
if (syncButton == null)
return null;
// Get all the setting nodes from the add-ons display
let elements = {};
let setupids = ["account", "password", "synckey", "usecustomserver", "customserver"];
setupids.forEach(function(id) {
elements[id] = document.getElementById("syncsetup-" + id);
});
let settingids = ["device", "connect", "connected", "disconnect", "sync", "details", "pairdevice"];
settingids.forEach(function(id) {
elements[id] = document.getElementById("sync-" + id);
});
// Replace the getter with the collection of settings
delete this._elements;
return this._elements = elements;
},
observe: function observe(aSubject, aTopic, aData) {
// Make sure we're online when connecting/syncing
Util.forceOnline();
// Can't do anything before settings are loaded
if (this._elements == null)
return;
// Make some aliases
let connect = this._elements.connect;
let connected = this._elements.connected;
let details = this._elements.details;
let device = this._elements.device;
let disconnect = this._elements.disconnect;
let sync = this._elements.sync;
let pairdevice = this._elements.pairdevice;
// Show what went wrong with login if necessary
if (aTopic == "weave:ui:login:error") {
this._loginError = true;
connect.setAttribute("desc", Weave.Utils.getErrorString(Weave.Status.login));
} else {
connect.removeAttribute("desc");
}
if (aTopic == "weave:service:login:finish") {
this._loginError = false;
// Init the setup data if we just logged in
if (!this.setupData)
this.loadSetupData();
}
let isConfigured = (!this._loginError && Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED);
connect.collapsed = isConfigured;
connected.collapsed = !isConfigured;
if (!isConfigured) {
connect.setAttribute("title", this._bundle.GetStringFromName("notconnected.label"));
connect.firstChild.disabled = false;
details.checked = false;
sync.collapsed = true;
device.collapsed = true;
disconnect.collapsed = true;
}
// Check the lock on a timeout because it's set just after notifying
setTimeout(function(self) {
// Prevent certain actions when the service is locked
if (Weave.Service.locked) {
connect.firstChild.disabled = true;
sync.firstChild.disabled = true;
if (aTopic == "weave:service:login:start")
connect.setAttribute("title", self._bundle.GetStringFromName("connecting.label"));
if (aTopic == "weave:service:sync:start")
sync.setAttribute("title", self._bundle.GetStringFromName("lastSyncInProgress2.label"));
} else {
connect.firstChild.disabled = false;
sync.firstChild.disabled = false;
}
}, 0, this);
// Dynamically generate some strings
let accountStr = this._bundle.formatStringFromName("account.label", [Weave.Service.identity.account], 1);
disconnect.setAttribute("title", accountStr);
// Show the day-of-week and time (HH:MM) of last sync
let lastSync = Weave.Svc.Prefs.get("lastSync");
if (lastSync != null) {
let syncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
let dateStr = this._bundle.formatStringFromName("lastSync2.label", [syncDate], 1);
sync.setAttribute("title", dateStr);
}
// Check for a storage format update, update the user and load the Sync update page
if (aTopic =="weave:service:sync:error") {
let clientOutdated = false, remoteOutdated = false;
if (Weave.Status.sync == Weave.VERSION_OUT_OF_DATE) {
clientOutdated = true;
} else if (Weave.Status.sync == Weave.DESKTOP_VERSION_OUT_OF_DATE) {
remoteOutdated = true;
} else if (Weave.Status.service == Weave.SYNC_FAILED_PARTIAL) {
// Some engines failed, check for per-engine compat
for (let [engine, reason] in Iterator(Weave.Status.engines)) {
clientOutdated = clientOutdated || reason == Weave.VERSION_OUT_OF_DATE;
remoteOutdated = remoteOutdated || reason == Weave.DESKTOP_VERSION_OUT_OF_DATE;
}
}
if (clientOutdated || remoteOutdated) {
let brand = Services.strings.createBundle("chrome://branding/locale/brand.properties");
let brandName = brand.GetStringFromName("brandShortName");
let type = clientOutdated ? "client" : "remote";
let message = this._bundle.GetStringFromName("sync.update." + type);
message = message.replace("#1", brandName);
message = message.replace("#2", Services.appinfo.version);
let title = this._bundle.GetStringFromName("sync.update.title")
let button = this._bundle.GetStringFromName("sync.update.button")
let close = this._bundle.GetStringFromName("sync.update.close")
let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING;
let choice = Services.prompt.confirmEx(window, title, message, flags, button, close, null, null, {});
if (choice == 0)
Browser.addTab("https://services.mozilla.com/update/", true, Browser.selectedTab);
}
}
device.value = Weave.Service.clientsEngine.localName || "";
},
changeName: function changeName(aInput) {
// Make sure to update to a modified name, e.g., empty-string -> default
Weave.Service.clientsEngine.localName = aInput.value;
aInput.value = Weave.Service.clientsEngine.localName;
},
showMessage: function showMessage(aMsg, aValue, aButtons) {
let notification = this._msg.getNotificationWithValue(aValue);
if (notification)
return;
this._msg.appendNotification(aMsg, aValue, "", this._msg.PRIORITY_WARNING_LOW, aButtons);
},
_validateServer: function _validateServer(aURL) {
let uri = Weave.Utils.makeURI(aURL);
if (!uri && aURL)
uri = Weave.Utils.makeURI("https://" + aURL);
if (!uri)
return "";
return uri.spec;
},
openTutorial: function _openTutorial() {
WeaveGlue.close();
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
let url = formatter.formatURLPref("app.sync.tutorialURL");
BrowserUI.newTab(url, Browser.selectedTab);
},
loadSetupData: function _loadSetupData() {
this.setupData = {};
this.setupData.account = Weave.Service.identity.account || "";
this.setupData.password = Weave.Service.identity.basicPassword || "";
this.setupData.synckey = Weave.Service.identity.syncKey || "";
let serverURL = Weave.Service.serverURL;
let defaultPrefs = Services.prefs.getDefaultBranch(null);
if (serverURL == defaultPrefs.getCharPref("services.sync.serverURL"))
serverURL = "";
this.setupData.serverURL = serverURL;
}
};
const PIN_PART_LENGTH = 4;
let SyncPairDevice = {
jpake: null,
open: function open() {
this.code1.setAttribute("maxlength", PIN_PART_LENGTH);
this.code2.setAttribute("maxlength", PIN_PART_LENGTH);
this.code3.setAttribute("maxlength", PIN_PART_LENGTH);
this.nextFocusEl = {code1: this.code2,
code2: this.code3,
code3: this.connectbutton};
document.getElementById("syncpair-container").hidden = false;
DialogUI.pushDialog(this);
this.code1.focus();
// Kick off a sync. That way the server will have the most recent data from
// this computer and it will show up immediately on the new device.
Weave.SyncScheduler.scheduleNextSync(0);
},
close: function close() {
this.code1.value = this.code2.value = this.code3.value = "";
this.code1.disabled = this.code2.disabled = this.code3.disabled = false;
this.connectbutton.disabled = true;
if (this.jpake) {
this.jpake.abort();
this.jpake = null;
}
document.getElementById("syncpair-container").hidden = true;
DialogUI.popDialog();
},
onTextBoxInput: function onTextBoxInput(textbox) {
if (textbox && textbox.value.length == PIN_PART_LENGTH) {
let name = textbox.id.split("-")[1];
this.nextFocusEl[name].focus();
}
this.connectbutton.disabled =
!(this.code1.value.length == PIN_PART_LENGTH &&
this.code2.value.length == PIN_PART_LENGTH &&
this.code3.value.length == PIN_PART_LENGTH);
},
connect: function connect() {
let self = this;
let jpake = this.jpake = new Weave.JPAKEClient({
onPaired: function onPaired() {
let credentials = {account: Weave.Service.identity.account,
password: Weave.Service.identity.basicPassword,
synckey: Weave.Service.identity.syncKey,
serverURL: Weave.Service.serverURL};
jpake.sendAndComplete(credentials);
},
onComplete: function onComplete() {
self.jpake = null;
self.close();
// Schedule a Sync for soonish to fetch the data uploaded by the
// device with which we just paired.
Weave.SyncScheduler.scheduleNextSync(Weave.SyncScheduler.activeInterval);
},
onAbort: function onAbort(error) {
self.jpake = null;
// Aborted by user, ignore.
if (error == Weave.JPAKE_ERROR_USERABORT) {
return;
}
self.code1.value = self.code2.value = self.code3.value = "";
self.code1.disabled = self.code2.disabled = self.code3.disabled = false;
self.code1.focus();
}
});
this.code1.disabled = this.code2.disabled = this.code3.disabled = true;
this.connectbutton.disabled = true;
let pin = this.code1.value + this.code2.value + this.code3.value;
let expectDelay = false;
jpake.pairWithPIN(pin, expectDelay);
}
};
["code1", "code2", "code3", "connectbutton"].forEach(function (id) {
XPCOMUtils.defineLazyGetter(SyncPairDevice, id, function() {
return document.getElementById("syncpair-" + id);
});
});

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

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var FullScreenVideo = {
_tab: null,
init: function fsv_init() {
// These come in from content.js, currently we only use Start.
messageManager.addMessageListener("Browser:FullScreenVideo:Start", this.show.bind(this));
messageManager.addMessageListener("Browser:FullScreenVideo:Close", this.hide.bind(this));
messageManager.addMessageListener("Browser:FullScreenVideo:Play", this.play.bind(this));
messageManager.addMessageListener("Browser:FullScreenVideo:Pause", this.pause.bind(this));
// If the screen supports brightness locks, we will utilize that, see checkBrightnessLocking()
try {
this.screen = null;
let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
this.screen = screenManager.primaryScreen;
}
catch (e) {} // The screen does not support brightness locks
},
play: function() {
this.playing = true;
this.checkBrightnessLocking();
},
pause: function() {
this.playing = false;
this.checkBrightnessLocking();
},
checkBrightnessLocking: function() {
// screen manager support for metro: bug 776113
var shouldLock = !!this.screen && !!window.fullScreen && !!this.playing;
var locking = !!this.brightnessLocked;
if (shouldLock == locking)
return;
if (shouldLock)
this.screen.lockMinimumBrightness(this.screen.BRIGHTNESS_FULL);
else
this.screen.unlockMinimumBrightness(this.screen.BRIGHTNESS_FULL);
this.brightnessLocked = shouldLock;
},
show: function fsv_show() {
this.createTab();
this.checkBrightnessLocking();
},
hide: function fsv_hide() {
this.checkBrightnessLocking();
this.destroyTab();
},
createTab: function fsv_createBrowser() {
this._tab = BrowserUI.newTab("chrome://browser/content/fullscreen-video.xhtml");
},
destroyTab: function fsv_destroyBrowser() {
Browser.closeTab(this._tab);
this._tab = null;
}
};

104
browser/metro/base/jar.mn Normal file
Просмотреть файл

@ -0,0 +1,104 @@
#filter substitution
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
chrome.jar:
% content browser %content/
content/aboutCertError.xhtml (content/pages/aboutCertError.xhtml)
* content/aboutRights.xhtml (content/pages/aboutRights.xhtml)
content/aboutCrash.xhtml (content/pages/aboutCrash.xhtml)
content/blockedSite.xhtml (content/pages/blockedSite.xhtml)
content/fullscreen-video.xhtml (content/pages/fullscreen-video.xhtml)
content/netError.xhtml (content/pages/netError.xhtml)
* content/bindings/bindings.xml (content/bindings/bindings.xml)
content/bindings/tabs.xml (content/bindings/tabs.xml)
content/bindings/toggleswitch.xml (content/bindings/toggleswitch.xml)
* content/bindings/browser.xml (content/bindings/browser.xml)
content/bindings/browser.js (content/bindings/browser.js)
content/bindings/downloads.xml (content/bindings/downloads.xml)
content/bindings/console.xml (content/bindings/console.xml)
content/bindings/dialog.xml (content/bindings/dialog.xml)
content/bindings/pageaction.xml (content/bindings/pageaction.xml)
content/bindings/arrowbox.xml (content/bindings/arrowbox.xml)
content/bindings/grid.xml (content/bindings/grid.xml)
content/bindings/autocomplete.xml (content/bindings/autocomplete.xml)
content/bindings/appbar.xml (content/bindings/appbar.xml)
content/bindings/flyoutpanel.xml (content/bindings/flyoutpanel.xml)
content/bindings/selectionoverlay.xml (content/bindings/selectionoverlay.xml)
content/prompt/CaptureDialog.xul (content/prompt/CaptureDialog.xul)
content/prompt/alert.xul (content/prompt/alert.xul)
content/prompt/confirm.xul (content/prompt/confirm.xul)
content/prompt/prompt.xul (content/prompt/prompt.xul)
content/prompt/promptPassword.xul (content/prompt/promptPassword.xul)
content/prompt/select.xul (content/prompt/select.xul)
content/prompt/prompt.js (content/prompt/prompt.js)
content/prompt/share.xul (content/prompt/share.xul)
content/prompt/masterPassword.xul (content/prompt/masterPassword.xul)
content/prompt/removeMasterPassword.xul (content/prompt/removeMasterPassword.xul)
* content/helperui/AlertsHelper.js (content/helperui/AlertsHelper.js)
content/helperui/CaptureDialog.js (content/helperui/CaptureDialog.js)
content/helperui/CapturePickerUI.js (content/helperui/CapturePickerUI.js)
content/helperui/CharsetMenu.js (content/helperui/CharsetMenu.js)
content/helperui/IdentityUI.js (content/helperui/IdentityUI.js)
content/helperui/IndexedDB.js (content/helperui/IndexedDB.js)
content/helperui/MasterPasswordUI.js (content/helperui/MasterPasswordUI.js)
content/helperui/MenuUI.js (content/helperui/MenuUI.js)
content/helperui/OfflineApps.js (content/helperui/OfflineApps.js)
content/helperui/SelectHelperUI.js (content/helperui/SelectHelperUI.js)
content/helperui/SelectionHelperUI.js (content/helperui/SelectionHelperUI.js)
content/helperui/SharingUI.js (content/helperui/SharingUI.js)
content/helperui/FormHelperUI.js (content/helperui/FormHelperUI.js)
content/helperui/FindHelperUI.js (content/helperui/FindHelperUI.js)
content/contenthandlers/ContextMenuHandler.js (content/contenthandlers/ContextMenuHandler.js)
content/contenthandlers/PluginCTPHandler.js (content/contenthandlers/PluginCTPHandler.js)
content/contenthandlers/SelectionHandler.js (content/contenthandlers/SelectionHandler.js)
content/contenthandlers/FormHelper.js (content/contenthandlers/FormHelper.js)
content/contenthandlers/FindHandler.js (content/contenthandlers/FindHandler.js)
content/contenthandlers/ViewportHandler.js (content/contenthandlers/ViewportHandler.js)
content/contenthandlers/ConsoleAPIObserver.js (content/contenthandlers/ConsoleAPIObserver.js)
* content/contenthandlers/Content.js (content/contenthandlers/Content.js)
content/BrowserTouchHandler.js (content/BrowserTouchHandler.js)
* content/WebProgress.js (content/WebProgress.js)
content/config.xul (content/config.xul)
content/config.js (content/config.js)
* content/browser.xul (content/browser.xul)
* content/browser.js (content/browser.js)
* content/browser-ui.js (content/browser-ui.js)
* content/browser-scripts.js (content/browser-scripts.js)
* content/ContextCommands.js (content/ContextCommands.js)
* content/PageActions.js (content/PageActions.js)
content/commandUtil.js (content/commandUtil.js)
content/appbar.js (content/appbar.js)
content/shell.xul (content/jsshell/shell.xul)
content/shell.html (content/jsshell/shell.html)
content/browser.css (content/browser.css)
content/cursor.css (content/cursor.css)
% content branding %content/branding/
content/sanitize.js (content/sanitize.js)
* content/input.js (content/input.js)
* content/Util.js (content/Util.js)
content/bookmarks.js (content/bookmarks.js)
* content/preferences.js (content/preferences.js)
content/exceptions.js (content/exceptions.js)
* content/downloads.js (content/downloads.js)
content/history.js (content/history.js)
content/TopSites.js (content/TopSites.js)
content/console.js (content/console.js)
content/AnimatedZoom.js (content/AnimatedZoom.js)
content/LoginManagerChild.js (content/LoginManagerChild.js)
content/video.js (content/video.js)
#ifdef MOZ_SERVICES_SYNC
content/sync.js (content/sync.js)
content/RemoteTabs.js (content/RemoteTabs.js)
#endif
% override chrome://global/content/config.xul chrome://browser/content/config.xul
% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml

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

@ -0,0 +1,29 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_BROWSER_FILES = \
head.js \
browser_test.js \
browser_canonizeURL.js \
browser_context_ui.js \
browser_onscreen_keyboard.js \
browser_onscreen_keyboard.html \
browser_remotetabs.js \
browser_downloads.js \
browser_plugin_input.html \
browser_plugin_input_mouse.js \
browser_plugin_input_keyboard.js \
$(NULL)
libs:: $(_BROWSER_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/

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

@ -0,0 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function install(data, reason) {}
function startup(data, reason) {}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

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

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:updateURL>http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf</em:updateURL>
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Install Tests</em:name>
</Description>
</RDF>

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

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon2@tests.mozilla.org</em:id>
<em:version>2.0</em:version>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Install Tests 2</em:name>
</Description>
</RDF>

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

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon1@tests.mozilla.org</em:id>
<em:version>3.0</em:version>
<em:updateURL>http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf</em:updateURL>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Install Tests</em:name>
</Description>
</RDF>

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

@ -0,0 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function install(data, reason) {}
function startup(data, reason) {}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

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

@ -0,0 +1,4 @@
locale mozapps te-st chrome # locale
locale browser te-st chrome # duplicate locale
locale browser te-st-a chrome # second locale
locale branding te-st-3 chrome # wrong component

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

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>locale1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:type>8</em:type>
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Test Locale</em:name>
</Description>
</RDF>

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

@ -0,0 +1,21 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function test() {
let testcases = [
["example", {}, "example"],
["example", {ctrlKey: true}, "http://www.example.com/"],
["example.org", {ctrlKey: true}, "example.org"],
["example", {shiftKey: true}, "http://www.example.net/"],
["example", {shiftKey: true, ctrlKey: true}, "http://www.example.org/"],
[" example ", {ctrlKey: true}, "http://www.example.com/"],
[" example/a ", {ctrlKey: true}, "http://www.example.com/a"]
];
for (let [input, modifiers, result] of testcases) {
is(BrowserUI._canonizeURL(input, modifiers), result, input + " -> " + result);
}
}

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

@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function test() {
runTests();
}
gTests.push({
desc: "Context UI on about:start",
run: function testAboutStart() {
yield addTab("about:start");
is(StartUI.isVisible, true, "Start UI is displayed on about:start");
is(ContextUI.isVisible, true, "Toolbar is displayed on about:start");
is(ContextUI.isExpanded, false, "Tab bar is not displayed initially");
is(Elements.appbar.isShowing, false, "Appbar is not displayed initially");
// toggle on
doEdgeUIGesture();
is(ContextUI.isVisible, true, "Toolbar is still visible after one swipe");
is(ContextUI.isExpanded, true, "Tab bar is visible after one swipe");
is(Elements.appbar.isShowing, true, "Appbar is visible after one swipe");
// toggle off
doEdgeUIGesture();
is(ContextUI.isVisible, true, "Toolbar is still visible after second swipe");
is(ContextUI.isExpanded, false, "Tab bar is hidden after second swipe");
is(Elements.appbar.isShowing, false, "Appbar is hidden after second swipe");
// sanity check - toggle on again
doEdgeUIGesture();
is(ContextUI.isVisible, true, "Toolbar is still visible after third swipe");
is(ContextUI.isExpanded, true, "Tab bar is visible after third swipe");
is(Elements.appbar.isShowing, true, "Appbar is visible after third swipe");
is(StartUI.isVisible, true, "Start UI is still visible");
}
});
gTests.push({
desc: "Context UI on a web page (about:)",
run: function testAbout() {
yield addTab("about:");
ContextUI.dismiss();
is(StartUI.isVisible, false, "Start UI is not visible on about:");
is(ContextUI.isVisible, false, "Toolbar is not initially visible on about:");
is(ContextUI.isExpanded, false, "Tab bar is not initially visible on about:");
is(Elements.appbar.isShowing, false, "Appbar is not initially visible on about on about::");
doEdgeUIGesture();
is(ContextUI.isVisible, true, "Toolbar is visible after one swipe");
is(ContextUI.isExpanded, true, "Tab bar is visble after one swipe");
is(Elements.appbar.isShowing, true, "Appbar is visible after one swipe");
doEdgeUIGesture();
is(ContextUI.isVisible, false, "Toolbar is not visible after second swipe");
is(ContextUI.isExpanded, false, "Tab bar is not visible after second swipe");
is(Elements.appbar.isShowing, false, "Appbar is hidden after second swipe");
is(StartUI.isVisible, false, "Start UI is still not visible");
}
});
gTests.push({
desc: "Control-L keyboard shortcut",
run: function testAbout() {
let tab = yield addTab("about:");
ContextUI.dismiss();
is(ContextUI.isVisible, false, "Navbar is not initially visible");
is(ContextUI.isExpanded, false, "Tab bar is not initially visible");
EventUtils.synthesizeKey('l', { accelKey: true });
is(ContextUI.isVisible, true, "Navbar is visible");
is(ContextUI.isExpanded, false, "Tab bar is not visible");
let edit = document.getElementById("urlbar-edit");
is(edit.value, "about:", "Location field contains the page URL");
ok(document.commandDispatcher.focusedElement, edit.inputField, "Location field is focused");
is(edit.selectionStart, 0, "Location field is selected");
is(edit.selectionEnd, edit.value.length, "Location field is selected");
Browser.closeTab(tab);
}
});
function doEdgeUIGesture() {
let event = document.createEvent("Events");
event.initEvent("MozEdgeUIGesture", true, false);
window.dispatchEvent(event);
}

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

@ -0,0 +1,450 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Provides infrastructure for automated download components tests.
* (adapted from browser/component/downloads test's head.js)
*/
////////////////////////////////////////////////////////////////////////////////
//// Globals
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
const nsIDM = Ci.nsIDownloadManager;
////////////////////////////////////////////////////////////////////////////////
// Test Helpers
var { spawn } = Task;
function equalStrings(){
let ref = ""+arguments[0];
for (let i=1; i<arguments.length; i++){
if (ref !== ""+arguments[i]) {
info("equalStrings failure: " + ref + " != " + arguments[i]);
return false
}
}
return true;
}
function equalNumbers(){
let ref = Number(arguments[0]);
for (let i=1; i<arguments.length; i++){
if (ref !== Number(arguments[i])) return false;
if (ref !== Number(arguments[i])) {
info("equalNumbers failure: " + ref + " != " + Number(arguments[i]));
return false
}
}
return true;
}
function waitForMs(aDelay) {
let deferred = Promise.defer();
let timerID = setTimeout(function(){
deferred.resolve(true);
}, aDelay || 0);
return deferred.promise;
}
function getPromisedDbResult(aStatement) {
let dbConnection = Downloads.manager.DBConnection;
let statement = ("string" == typeof aStatement) ?
dbConnection.createAsyncStatement(
aStatement
) : aStatement;
let deferred = Promise.defer(),
resultRows = [],
err = null;
try {
statement.executeAsync({
handleResult: function(aResultSet) {
let row;
if(!aResultSet) {
return;
}
while ((row = aResultSet.getNextRow())){
resultRows.push(row);
}
},
handleError: function(aError) {
Cu.reportError(aError);
err = aError;
},
handleCompletion: function(){
if (err) {
deferred.reject(err);
} else {
deferred.resolve(resultRows);
}
}
});
} finally {
statement.finalize();
}
return deferred.promise;
}
let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
registerCleanupFunction(function () {
gTestTargetFile.remove(false);
PanelUI.hide();
});
/**
* This object contains a property for each column in the downloads table.
*/
let gDownloadRowTemplate = {
name: "test-download.txt",
source: "http://www.example.com/test-download.txt",
target: NetUtil.newURI(gTestTargetFile).spec,
startTime: 1180493839859230,
endTime: 1180493839859234,
state: nsIDM.DOWNLOAD_FINISHED,
currBytes: 0,
maxBytes: -1,
preferredAction: 0,
autoResume: 0
};
////////////////////////////////////////////////////////////////////////////////
// Test Infrastructure
function test() {
PanelUI.show("downloads-container");
runTests();
}
/////////////////////////////////////
// shared test setup
function resetDownloads(){
var defd = Promise.defer();
// do the reset, resolve the defd when done
// TODO (sfoster) clear out downloads db, reset relevant state
let promisedResult = getPromisedDbResult(
"DELETE FROM moz_downloads"
);
return promisedResult.then(function(aResult){
// // Reset any prefs that might have been changed.
// Services.prefs.clearUserPref("browser.download.panel.shown");
// Ensure that data is unloaded.
let dlMgr = Downloads.manager;
let dlsToRemove = [];
// Clear all completed/cancelled downloads
dlMgr.cleanUp();
dlMgr.cleanUpPrivate();
// Queue up all active ones as well
for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) {
while (dlsEnum.hasMoreElements()) {
dlsToRemove.push(dlsEnum.next());
}
}
// Remove any queued up active downloads
dlsToRemove.forEach(function (dl) {
dl.remove();
});
});
}
function addDownloadRow(aDataRow) {
let deferredInsert = Promise.defer();
let dataRow = aDataRow;
let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
let db = dm.DBConnection;
let columnNames = Object.keys(gDownloadRowTemplate).join(", ");
let parameterNames = Object.keys(gDownloadRowTemplate)
.map(function(n) ":" + n)
.join(", ");
let statement = db.createAsyncStatement(
"INSERT INTO moz_downloads (" + columnNames +
", guid) VALUES(" + parameterNames + ", GENERATE_GUID())");
// Populate insert parameters from the provided data.
for (let columnName in gDownloadRowTemplate) {
if (!(columnName in dataRow)) {
// Update the provided row object with data from the global template,
// for columns whose value is not provided explicitly.
dataRow[columnName] = gDownloadRowTemplate[columnName];
}
statement.params[columnName] = dataRow[columnName];
}
// Run the statement asynchronously and wait.
let promisedDownloads = getPromisedDbResult(
statement
);
yield promisedDownloads.then(function(){
let newItemId = db.lastInsertRowID;
let download = dm.getDownload(newItemId);
deferredInsert.resolve(download);
});
}
function gen_addDownloadRows(aDataRows){
if (!aDataRows.length) {
yield;
}
try {
// Add each of the provided downloads in reverse.
for (let i = aDataRows.length - 1; i >= 0; i--) {
let dataRow = aDataRows[i];
let download = yield addDownloadRow(dataRow);
// At each iteration, ensure that the start and end time in the global
// template is distinct, as these column are used to sort each download
// in its category.
gDownloadRowTemplate.startTime++;
gDownloadRowTemplate.endTime++;
}
} finally {
info("gen_addDownloadRows, finally");
}
}
/////////////////////////////////////
// Test implementations
/**
* Test that:
* view can represent all possible download states
* clearDownloads does in fact empty the view
* addDownload adds an item, to the right place
* removeDownload removes an item, leaving the view in the correct state
*/
gTests.push({
desc: "UI sanity check",
run: function(){
ok(document.getElementById('downloads-list'), "Downloads panel grid is present");
ok(DownloadsPanelView, "Downloads panel object is present");
PanelUI.show('downloads-container');
ok(DownloadsPanelView.visible, "Downloads panel is visible after being shown");
}
});
gTests.push({
desc: "zero downloads",
run: function () {
yield resetDownloads();
let downloadslist = document.getElementById("downloads-list");
// wait for the richgrid to announce its readiness
// .. fail a test if the timeout is exceeded
let isReady = waitForEvent(downloadslist, "DownloadsReady", 2000);
// tickle the view to cause it to refresh itself
DownloadsPanelView._view.getDownloads();
yield isReady;
if (!isReady || isReady instanceof Error){
ok(false, "DownloadsReady event never fired");
}
let count = downloadslist.children.length;
is(count, 0, "Zero items in grid view with empty downloads db");
}
});
/**
* Make sure the downloads panel can display items in the right order and
* contains the expected data.
*/
gTests.push({
desc: "Show downloads",
run: function(){
// Display one of each download state.
let DownloadData = [
{ endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED },
{ endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING },
{ endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED },
{ endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING },
{ endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED },
{ endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED },
{ endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED },
{ endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
{ endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
{ endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
{ endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
];
yield resetDownloads();
DownloadsPanelView._view.getDownloads();
// NB: beware display limits which might cause mismatch btw. rendered item and db rows
try {
// Populate the downloads database with the data required by this test.
// we're going to add stuff to the downloads db.
yield spawn( gen_addDownloadRows( DownloadData ) );
// Test item data and count. This also tests the ordering of the display.
let downloadslist = document.getElementById("downloads-list");
// wait for the richgrid to announce its readiness
// .. fail a test if the timeout is exceeded
let isReady = waitForEvent(downloadslist, "DownloadsReady", 2000);
// tickle the view to cause it to refresh itself
DownloadsPanelView._view.getDownloads();
yield isReady;
if (!isReady || isReady instanceof Error){
ok(false, "DownloadsReady event never fired");
}
is(downloadslist.children.length, DownloadData.length,
"There is the correct number of richlistitems");
for (let i = 0; i < downloadslist.children.length; i++) {
let element = downloadslist.children[i];
let id = element.getAttribute("downloadId");
let dataItem = Downloads.manager.getDownload(id); // nsIDownload object
ok( equalStrings(
element.getAttribute("name"),
dataItem.displayName,
DownloadData[i].name
), "Download names match up");
ok( equalNumbers(
element.getAttribute("state"),
dataItem.state,
DownloadData[i].state
), "Download states match up");
ok( equalStrings(
element.getAttribute("target"),
dataItem.target.spec,
DownloadData[i].target
), "Download targets match up");
if (DownloadData[i].source && dataItem.referrer){
ok( equalStrings(
dataItem.referrer.spec,
DownloadData[i].source
), "Download sources match up");
}
}
} catch(e) {
info("Show downloads, some error: " + e);
}
finally {
// Clean up when the test finishes.
DownloadsPanelView._view.clearDownloads();
yield resetDownloads();
}
}
});
/**
* Make sure the downloads can be removed with the expected result on the panel and its listing
*/
gTests.push({
desc: "Remove downloads",
run: function(){
// Push a few items into the downloads db.
let DownloadData = [
{ endTime: 1180493839859239, state: nsIDM.DOWNLOAD_FINISHED },
{ endTime: 1180493839859238, state: nsIDM.DOWNLOAD_FINISHED },
{ endTime: 1180493839859237, state: nsIDM.DOWNLOAD_FINISHED }
];
yield resetDownloads();
DownloadsPanelView._view.getDownloads();
try {
// Populate the downloads database with the data required by this test.
yield spawn( gen_addDownloadRows( DownloadData ) );
// Test item data and count. This also tests the ordering of the display.
let downloadslist = document.getElementById("downloads-list");
// wait for the richgrid to announce its readiness
// .. fail a test if the timeout is exceeded
let isReady = waitForEvent(downloadslist, "DownloadsReady", 2000);
// tickle the view to cause it to refresh itself
DownloadsPanelView._view.getDownloads();
yield isReady;
if (!isReady || isReady instanceof Error){
is(false, "DownloadsReady event never fired");
}
let downloadRows = null,
promisedDownloads;
// get all the downloads from the db
promisedDownloads = getPromisedDbResult(
"SELECT guid "
+ "FROM moz_downloads "
+ "ORDER BY startTime DESC"
).then(function(aRows){
downloadRows = aRows;
}, function(aError){
throw aError;
});
yield promisedDownloads;
is(downloadRows.length, 3, "Correct number of downloads in the db before removal");
// remove the first one
let itemNode = downloadslist.children[0];
let id = itemNode.getAttribute("downloadId");
// check the file exists
let download = Downloads.manager.getDownload( id );
let file = download.targetFile;
ok(file && file.exists());
Downloads.manager.removeDownload( id );
// remove is async(?), wait a bit
yield waitForMs(0);
// get all the downloads from the db
downloadRows = null;
promisedDownloads = getPromisedDbResult(
"SELECT guid "
+ "FROM moz_downloads "
+ "ORDER BY startTime DESC"
).then(function(aRows){
downloadRows = aRows;
}, function(aError){
throw aError;
});
yield promisedDownloads;
is(downloadRows.length, 2, "Correct number of downloads in the db after removal");
is(2, downloadslist.children.length,
"Removing a download updates the items list");
ok(file && file.exists(), "File still exists after download removal");
} catch(e) {
info("Remove downloads, some error: " + e);
}
finally {
// Clean up when the test finishes.
DownloadsPanelView._view.clearDownloads();
yield resetDownloads();
}
}
});

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

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>On-Screen Keyboard Test</title>
<style>
#text { position: absolute; left: 1em; bottom: 1em; }
</style>
</head>
<body>
<input id="text">
</body>
</head>

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

@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
runTests();
}
gTests.push({
desc: "Onscreen keyboard tests",
run: function() {
// By design, Metro apps can't show the keyboard programmatically, so we
// can't use the real keyboard in this test:
// http://msdn.microsoft.com/en-us/library/windows/apps/hh465404.aspx#user-driven_invocation
//
// Instead, we will use this mock object to simulate keyboard changes.
let originalUtils = window.MetroUtils;
window.MetroUtils = {
keyboardHeight: 0,
keyboardVisible: false
};
registerCleanupFunction(function() {
window.MetroUtils = originalUtils;
});
let tab = yield addTab(chromeRoot + "browser_onscreen_keyboard.html");
// Explicitly dismiss the toolbar at the start, because it messes with the
// keyboard and sizing if it's dismissed later.
ContextUI.dismiss();
let doc = tab.browser.contentDocument;
let text = doc.getElementById("text")
let rect0 = text.getBoundingClientRect();
text.focus();
// "Show" the keyboard.
MetroUtils.keyboardHeight = 100;
MetroUtils.keyboardVisible = true;
Services.obs.notifyObservers(null, "metro_softkeyboard_shown", null);
let rect1 = text.getBoundingClientRect();
is(rect1.top, rect0.top - 100, "text field moves up by 100px");
// "Hide" the keyboard.
MetroUtils.keyboardHeight = 0;
MetroUtils.keyboardVisible = false;
Services.obs.notifyObservers(null, "metro_softkeyboard_hidden", null);
let rect2 = text.getBoundingClientRect();
is(rect2.top, rect0.top, "text field moves back to the original position");
finish();
}
});

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше