46
48
XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
47
Cu.import("resource://gre/modules/utils.js");
49
Cu.import("resource://gre/modules/PlacesUtils.jsm");
48
50
return PlacesUtils;
53
XPCOMUtils.defineLazyServiceGetter(window, "gHistSvc", "@mozilla.org/browser/nav-history-service;1", "nsINavHistoryService", "nsIBrowserHistory");
54
XPCOMUtils.defineLazyServiceGetter(window, "gURIFixup", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
55
XPCOMUtils.defineLazyServiceGetter(window, "gFaviconService", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
56
XPCOMUtils.defineLazyServiceGetter(window, "gFocusManager", "@mozilla.org/focus-manager;1", "nsIFocusManager");
59
["AllPagesList", "popup_autocomplete", "cmd_openLocation"],
60
["HistoryList", "history-items", "cmd_history"],
61
["BookmarkList", "bookmarks-items", "cmd_bookmarks"],
62
#ifdef MOZ_SERVICES_SYNC
63
["RemoteTabsList", "remotetabs-items", "cmd_remoteTabs"]
65
].forEach(function(aPanel) {
66
let [name, id, command] = aPanel;
67
XPCOMUtils.defineLazyGetter(window, name, function() {
68
return new AwesomePanel(id, command);
73
* Cache of commonly used elements.
78
["browserBundle", "bundle_browser"],
79
["contentShowing", "bcast_contentShowing"],
80
["urlbarState", "bcast_urlbarState"],
82
["tabs", "tabs-container"],
83
["controls", "browser-controls"],
84
["panelUI", "panel-container"],
85
["toolbarContainer", "toolbar-container"],
86
["browsers", "browsers"],
87
["contentViewport", "content-viewport"],
88
["contentNavigator", "content-navigator"]
89
].forEach(function (aElementGlobal) {
90
let [name, id] = aElementGlobal;
91
XPCOMUtils.defineLazyGetter(Elements, name, function() {
92
return document.getElementById(id);
51
96
const TOOLBARSTATE_LOADING = 1;
52
97
const TOOLBARSTATE_LOADED = 2;
57
"@mozilla.org/browser/nav-history-service;1",
58
[Ci.nsINavHistoryService, Ci.nsIBrowserHistory]
62
"@mozilla.org/browser/favicon-service;1",
63
[Ci.nsIFaviconService]
67
"@mozilla.org/network/io-service;1",
72
"@mozilla.org/docshell/urifixup;1",
77
"@mozilla.org/preferences-service;1",
82
"@mozilla.org/focus-manager;1",
87
"@mozilla.org/appshell/window-mediator;1",
88
[Ci.nsIWindowMediator]
92
"@mozilla.org/observer-service;1",
93
[Ci.nsIObserverService]
95
].forEach(function (service) {
96
let [name, contract, ifaces] = service;
97
window.__defineGetter__(name, function () {
99
window[name] = Cc[contract].getService(ifaces.splice(0, 1)[0]);
101
ifaces.forEach(function (i) { return window[name].QueryInterface(i); });
112
_domWillOpenModalDialog: function(e) {
106
_domWillOpenModalDialog: function(aBrowser) {
116
107
// We're about to open a modal dialog, make sure the opening
117
108
// tab is brought to the front.
119
let window = e.target.top;
120
for (let i = 0; i < Browser._tabs.length; i++) {
121
if (Browser._tabs[i].browser.contentWindow == window) {
122
Browser.selectedTab = Browser._tabs[i];
110
for (let i = 0; i < Browser.tabs.length; i++) {
111
if (Browser._tabs[i].browser == aBrowser) {
112
Browser.selectedTab = Browser.tabs[i];
128
_titleChanged : function(aDocument) {
129
var browser = Browser.selectedBrowser;
130
if (browser && aDocument != browser.contentDocument)
118
_titleChanged: function(aBrowser) {
119
let browser = Browser.selectedBrowser;
120
if (browser && aBrowser != browser)
133
var url = this.getDisplayURI(browser);
134
var caption = aDocument.title || url;
123
let url = this.getDisplayURI(browser);
124
let caption = browser.contentTitle || url;
136
if (Util.isURLEmpty(url))
126
if (browser.contentTitle == "" && !Util.isURLEmpty(browser.userTypedValue))
127
caption = browser.userTypedValue;
128
else if (Util.isURLEmpty(url))
139
this._setURI(caption);
132
this._title.value = caption;
133
this._title.classList.remove("placeholder");
135
this._title.value = this._title.getAttribute("placeholder");
136
this._title.classList.add("placeholder");
143
141
* Dispatched by window.close() to allow us to turn window closes into tabs
146
_domWindowClose: function (aEvent) {
147
if (!aEvent.isTrusted)
150
// Find the relevant tab, and close it.
151
let browsers = Browser.browsers;
152
for (let i = 0; i < browsers.length; i++) {
153
if (browsers[i].contentWindow == aEvent.target) {
144
_domWindowClose: function(aBrowser) {
145
// Find the relevant tab, and close it.
146
let browsers = Browser.browsers;
147
for (let i = 0; i < browsers.length; i++) {
148
if (browsers[i] == aBrowser) {
154
149
Browser.closeTab(Browser.getTabAtIndex(i));
155
aEvent.preventDefault();
161
_linkAdded : function(aEvent) {
162
let link = aEvent.originalTarget;
163
if (!link || !link.href)
166
if (/\bicon\b/i(link.rel)) {
167
// Must have an owner document and not be in a frame
168
var ownerDoc = link.ownerDocument;
169
if (!ownerDoc || ownerDoc.defaultView.frameElement)
172
let tab = Browser.getTabForDocument(ownerDoc);
173
tab.setIcon(link.href);
174
tab.updateViewportMetadata(); // XXX Hack - See bug 568261
176
// If the link changes after pageloading, update it right away.
177
// otherwise we wait until the pageload finishes
178
if ((tab.browser == Browser.selectedBrowser) && !tab.isLoading())
179
this._updateIcon(tab.browser.mIconURL);
181
else if (/\bsearch\b/i(link.rel)) {
182
var type = link.type && link.type.toLowerCase();
183
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
184
if (type == "application/opensearchdescription+xml" && link.title && /^(?:https?|ftp):/i.test(link.href)) {
185
var engine = { title: link.title, href: link.href };
187
BrowserSearch.addPageSearchEngine(engine, link.ownerDocument);
192
_updateButtons : function(aBrowser) {
150
return { preventDefault: true };
155
_updateButtons: function(aBrowser) {
193
156
let back = document.getElementById("cmd_back");
194
157
let forward = document.getElementById("cmd_forward");
543
592
// Give the new page lots of room
544
593
Browser.hideSidebars();
545
this.closeAutoComplete(true);
594
this.closeAutoComplete();
547
596
this._edit.value = aURI;
549
var flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
550
getBrowser().loadURIWithFlags(aURI, flags, null, null);
552
gHistSvc.markPageAsTyped(gURIFixup.createFixupURI(aURI, 0));
555
showAutoComplete : function showAutoComplete() {
556
if (this.isAutoCompleteOpen())
559
BrowserSearch.updateSearchButtons();
561
this._edit.showHistoryPopup();
564
closeAutoComplete: function closeAutoComplete(aResetInput) {
565
if (!this.isAutoCompleteOpen())
569
this._edit.popup.close();
599
aURI = Browser.getShortcutOrURI(aURI, postData);
600
Browser.loadURI(aURI, { flags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP, postData: postData });
602
// Delay doing the fixup so the raw URI is passed to loadURIWithFlags
603
// and the proper third-party fixup can be done
604
let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
605
let uri = gURIFixup.createFixupURI(aURI, fixupFlags);
606
gHistSvc.markPageAsTyped(uri);
608
this._titleChanged(Browser.selectedBrowser);
611
showAutoComplete: function showAutoComplete() {
612
if (this.isAutoCompleteOpen())
617
this.activePanel = AllPagesList;
620
closeAutoComplete: function closeAutoComplete() {
621
if (this.isAutoCompleteOpen())
571
622
this._edit.popup.closePopup();
624
this.activePanel = null;
574
627
isAutoCompleteOpen: function isAutoCompleteOpen() {
575
return this._edit.popup.popupOpen;
628
return this.activePanel == AllPagesList;
578
doButtonSearch : function(button) {
579
if (!("engine" in button) || !button.engine)
582
// We don't want the button to look pressed for now
583
button.parentNode.selectedItem = null;
631
doOpenSearch: function doOpenSearch(aName) {
632
// save the current value of the urlbar
633
let searchValue = this._edit.value;
585
635
// Give the new page lots of room
586
636
Browser.hideSidebars();
587
this.closeAutoComplete(false);
637
this.closeAutoComplete();
589
639
// Make sure we're online before attempting to load
590
640
Util.forceOnline();
592
let submission = button.engine.getSubmission(this._edit.value, null);
593
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
594
getBrowser().loadURIWithFlags(submission.uri.spec, flags, null, null, submission.postData);
597
updateStar : function() {
642
let engine = Services.search.getEngineByName(aName);
643
let submission = engine.getSubmission(searchValue, null);
644
Browser.loadURI(submission.uri.spec, { postData: submission.postData });
647
updateUIFocus: function _updateUIFocus() {
648
if (Elements.contentShowing.getAttribute("disabled") == "true")
649
Browser.selectedBrowser.messageManager.sendAsyncMessage("Browser:Blur", { });
652
updateStar: function() {
598
653
if (PlacesUtils.getMostRecentBookmarkForURI(Browser.selectedBrowser.currentURI) != -1)
599
654
this.starButton.setAttribute("starred", "true");
601
656
this.starButton.removeAttribute("starred");
604
newTab : function newTab(aURI) {
659
newTab: function newTab(aURI, aOwner) {
605
660
aURI = aURI || "about:blank";
606
let tab = Browser.addTab(aURI, true);
661
let tab = Browser.addTab(aURI, true, aOwner);
608
663
this.hidePanel();
610
665
if (aURI == "about:blank") {
611
666
// Display awesomebar UI
612
this.showToolbar(true);
613
667
this.showAutoComplete();
616
670
// Give the new page lots of room
617
671
Browser.hideSidebars();
618
this.closeAutoComplete(true);
672
this.closeAutoComplete();
624
closeTab : function closeTab(aTab) {
678
newOrSelectTab: function newOrSelectTab(aURI, aOwner) {
679
let tabs = Browser.tabs;
680
for (let i = 0; i < tabs.length; i++) {
681
if (tabs[i].browser.currentURI.spec == aURI) {
682
Browser.selectedTab = tabs[i];
686
this.newTab(aURI, aOwner);
689
closeTab: function closeTab(aTab) {
625
690
// If no tab is passed in, assume the current tab
626
691
Browser.closeTab(aTab || Browser.selectedTab);
629
selectTab : function selectTab(aTab) {
694
selectTab: function selectTab(aTab) {
695
this.activePanel = null;
630
696
Browser.selectedTab = aTab;
699
undoCloseTab: function undoCloseTab(aIndex) {
701
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
702
if (ss.getClosedTabCount(window) > (aIndex || 0)) {
703
let chromeTab = ss.undoCloseTab(window, aIndex || 0);
704
tab = Browser.getTabFromChrome(chromeTab);
633
709
isTabsVisible: function isTabsVisible() {
634
710
// The _1, _2 and _3 are to make the js2 emacs mode happy
635
711
let [leftvis,_1,_2,_3] = Browser.computeSidebarVisibility();
876
1130
case "cmd_lockscreen":
878
let locked = gPrefService.getBoolPref("toolkit.screen.lock");
879
gPrefService.setBoolPref("toolkit.screen.lock", !locked);
1132
let locked = Services.prefs.getBoolPref("toolkit.screen.lock");
1133
Services.prefs.setBoolPref("toolkit.screen.lock", !locked);
1135
let strings = Elements.browserBundle;
1136
let alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
1137
alerts.showAlertNotification(null, strings.getString("alertLockScreen"),
1138
strings.getString("alertLockScreen." + (!locked ? "locked" : "unlocked")), false, "", null);
1145
var TapHighlightHelper = {
1147
if (Browser.selectedTab)
1148
return Browser.selectedTab.overlay;
1152
show: function show(aRects) {
1153
let overlay = this._overlay;
1157
let browser = getBrowser();
1158
let scroll = browser.getPosition();
1160
let canvasArea = aRects.reduce(function(a, b) {
1161
return a.expandToContain(b);
1162
}, new Rect(0, 0, 0, 0)).map(function(val) val * browser.scale)
1163
.translate(-scroll.x, -scroll.y);
1165
overlay.setAttribute("width", canvasArea.width);
1166
overlay.setAttribute("height", canvasArea.height);
1168
let ctx = overlay.getContext("2d");
1170
ctx.translate(-canvasArea.left, -canvasArea.top);
1171
ctx.scale(browser.scale, browser.scale);
1173
overlay.setAttribute("left", canvasArea.left);
1174
overlay.setAttribute("top", canvasArea.top);
1175
ctx.clearRect(0, 0, canvasArea.width, canvasArea.height);
1176
ctx.fillStyle = "rgba(0, 145, 255, .5)";
1177
for (let i = aRects.length - 1; i >= 0; i--) {
1178
let rect = aRects[i];
1179
ctx.fillRect(rect.left - scroll.x / browser.scale, rect.top - scroll.y / browser.scale, rect.width, rect.height);
1182
overlay.style.display = "block";
1184
addEventListener("MozBeforePaint", this, false);
1185
mozRequestAnimationFrame();
1189
* Hide the highlight. aGuaranteeShowMsecs specifies how many milliseconds the
1190
* highlight should be shown before it disappears.
1192
hide: function hide(aGuaranteeShowMsecs) {
1193
if (!this._overlay || this._overlay.style.display == "none")
1196
this._guaranteeShow = Math.max(0, aGuaranteeShowMsecs);
1197
if (this._guaranteeShow) {
1198
// _shownAt is set once highlight has been painted
1200
setTimeout(this._hide.bind(this),
1201
Math.max(0, this._guaranteeShow - (mozAnimationStartTime - this._shownAt)));
1207
/** Helper function that hides popup immediately. */
1208
_hide: function _hide() {
1210
this._guaranteeShow = 0;
1211
this._overlay.style.display = "none";
1214
handleEvent: function handleEvent(ev) {
1215
removeEventListener("MozBeforePaint", this, false);
1216
this._shownAt = ev.timeStamp;
1217
// hide has been called, so hide the tap highlight after it has
1218
// been shown for a moment.
1219
if (this._guaranteeShow)
1220
this.hide(this._guaranteeShow);
886
1224
var PageActions = {
887
get _permissionManager() {
888
delete this._permissionManager;
889
return this._permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
1225
init: function init() {
1226
document.getElementById("pageactions-container").addEventListener("click", this, false);
1228
this.register("pageaction-reset", this.updatePagePermissions, this);
1229
this.register("pageaction-password", this.updateForgetPassword, this);
1231
this.register("pageaction-saveas", this.updatePageSaveAs, this);
1233
this.register("pageaction-share", this.updateShare, this);
1234
this.register("pageaction-search", BrowserSearch.updatePageSearchEngines, BrowserSearch);
1237
handleEvent: function handleEvent(aEvent) {
1238
switch (aEvent.type) {
1240
getIdentityHandler().hide();
1246
* @param aId id of a pageaction element
1247
* @param aCallback function that takes an element and returns true if it should be visible
1248
* @param aThisObj (optional) scope object for aCallback
1250
register: function register(aId, aCallback, aThisObj) {
1251
this._handlers.push({id: aId, callback: aCallback, obj: aThisObj});
1256
updateSiteMenu: function updateSiteMenu() {
1257
this._handlers.forEach(function(action) {
1258
let node = document.getElementById(action.id);
1259
node.hidden = !action.callback.call(action.obj, node);
1261
this._updateAttributes();
892
1264
get _loginManager() {
977
1336
let lm = this._loginManager;
978
1337
if (!lm.getLoginSavingEnabled(host.prePath))
979
1338
lm.setLoginSavingEnabled(host.prePath, true);
1340
this.hideItem(aEvent.target);
1341
aEvent.stopPropagation(); // Don't hide the site menu.
982
_savePageAsPDF: function saveAsPDF() {
983
let contentWindow = Browser.selectedBrowser.contentWindow;
1344
savePageAsPDF: function saveAsPDF() {
1345
let browser = Browser.selectedBrowser;
1346
let fileName = getDefaultFileName(browser.contentTitle, browser.documentURI, null, null);
1347
fileName = fileName.trim() + ".pdf";
1348
#ifdef MOZ_PLATFORM_MAEMO
1349
fileName = fileName.replace(/[\*\:\?]+/g, " ");
1352
let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
1353
let downloadsDir = dm.defaultDownloadsDirectory;
1356
// Create the final destination file location
1357
let file = downloadsDir.clone();
1358
file.append(fileName);
1359
// The filename is used below to save the file to a temp location in
1360
// the content process. Make sure it's up to date.
1361
file.createUnique(file.NORMAL_FILE_TYPE, 0x666);
1362
fileName = file.leafName;
984
1364
let strings = Elements.browserBundle;
985
1365
let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
986
1366
picker.init(window, strings.getString("pageactions.saveas.pdf"), Ci.nsIFilePicker.modeSave);
987
1367
picker.appendFilter("PDF", "*.pdf");
988
1368
picker.defaultExtension = "pdf";
990
let fileName = getDefaultFileName(null, null, contentWindow.document, null);
991
#ifdef MOZ_PLATFORM_MAEMO
992
fileName = fileName.replace(/[\*\:\?]+/g, " ");
994
picker.defaultString = fileName + ".pdf";
996
let dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
997
picker.displayDirectory = dnldMgr.defaultDownloadsDirectory;
1370
picker.defaultString = fileName;
1372
picker.displayDirectory = downloadsDir;
999
1373
let rv = picker.show();
1000
1374
if (rv == Ci.nsIFilePicker.returnCancel)
1003
let printSettings = Cc["@mozilla.org/gfx/printsettings-service;1"]
1004
.getService(Ci.nsIPrintSettingsService)
1006
printSettings.printSilent = true;
1007
printSettings.showPrintProgress = false;
1008
printSettings.printBGImages = true;
1009
printSettings.printBGColors = true;
1010
printSettings.printToFile = true;
1011
printSettings.toFileName = picker.file.path;
1012
printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs;
1013
printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
1015
//XXX we probably need a preference here, the header can be useful
1016
printSettings.footerStrCenter = '';
1017
printSettings.footerStrLeft = '';
1018
printSettings.footerStrRight = '';
1019
printSettings.headerStrCenter = '';
1020
printSettings.headerStrLeft = '';
1021
printSettings.headerStrRight = '';
1023
let webBrowserPrint = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
1024
.getInterface(Ci.nsIWebBrowserPrint);
1025
webBrowserPrint.print(printSettings, null);
1028
updatePageSaveAs: function updatePageSaveAs() {
1029
this.removeItems("saveas");
1030
if (Browser.selectedBrowser.contentDocument instanceof XULDocument)
1033
let strings = Elements.browserBundle;
1034
let node = this.appendItem("saveas", strings.getString("pageactions.saveas.pdf"), "");
1035
node.onclick = function(event) {
1036
PageActions._savePageAsPDF();
1040
appendItem: function appendItem(aType, aTitle, aDesc) {
1041
let container = document.getElementById("pageactions-container");
1042
let item = document.createElement("pageaction");
1043
item.setAttribute("title", aTitle);
1044
item.setAttribute("description", aDesc);
1045
item.setAttribute("type", aType);
1046
container.appendChild(item);
1048
let identityContainer = document.getElementById("identity-container");
1049
identityContainer.setAttribute("hasmenu", "true");
1053
removeItem: function removeItem(aItem) {
1054
let container = document.getElementById("pageactions-container");
1055
container.removeChild(aItem);
1057
let identityContainer = document.getElementById("identity-container");
1058
identityContainer.setAttribute("hasmenu", container.hasChildNodes() ? "true" : "false");
1061
removeItems: function removeItems(aType) {
1062
let container = document.getElementById("pageactions-container");
1063
let count = container.childNodes.length;
1064
for (let i = count - 1; i >= 0; i--) {
1065
if (aType == "" || container.childNodes[i].getAttribute("type") == aType)
1066
this.removeItem(container.childNodes[i]);
1377
let file = picker.file;
1380
// We must manually add this to the download system
1381
let db = dm.DBConnection;
1383
let stmt = db.createStatement(
1384
"INSERT INTO moz_downloads (name, source, target, startTime, endTime, state, referrer) " +
1385
"VALUES (:name, :source, :target, :startTime, :endTime, :state, :referrer)"
1388
let current = browser.currentURI.spec;
1389
stmt.params.name = file.leafName;
1390
stmt.params.source = current;
1391
stmt.params.target = Services.io.newFileURI(file).spec;
1392
stmt.params.startTime = Date.now() * 1000;
1393
stmt.params.endTime = Date.now() * 1000;
1394
stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED;
1395
stmt.params.referrer = current;
1399
let newItemId = db.lastInsertRowID;
1400
let download = dm.getDownload(newItemId);
1402
DownloadsView.downloadStarted(download);
1405
Services.obs.notifyObservers(download, "dl-start", null);
1408
let tmpDir = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
1410
file = tmpDir.clone();
1411
file.append(fileName);
1416
type: Ci.nsIPrintSettings.kOutputFormatPDF,
1422
Browser.selectedBrowser.messageManager.sendAsyncMessage("Browser:SaveAs", data);
1425
updatePageSaveAs: function updatePageSaveAs(aNode) {
1426
// Check for local XUL content
1427
let contentWindow = Browser.selectedBrowser.contentWindow;
1428
return !(contentWindow && contentWindow.document instanceof XULDocument);
1431
updateShare: function updateShare(aNode) {
1432
return Util.isShareableScheme(Browser.selectedBrowser.currentURI.scheme);
1435
hideItem: function hideItem(aNode) {
1436
aNode.hidden = true;
1437
this._updateAttributes();
1440
_updateAttributes: function _updateAttributes() {
1441
let container = document.getElementById("pageactions-container");
1442
let visibleNodes = container.querySelectorAll("pageaction:not([hidden=true])");
1443
let visibleCount = visibleNodes.length;
1445
for (let i = 0; i < visibleCount; i++)
1446
visibleNodes[i].classList.remove("odd-last-child");
1448
if (visibleCount % 2)
1449
visibleNodes[visibleCount - 1].classList.add("odd-last-child");
1071
1453
var NewTabPopup = {
1217
1673
this._editor = null;
1219
1675
this._panel.hidden = true;
1220
BrowserUI.popPopup();
1224
var BookmarkList = {
1227
_manageButtton: null,
1230
let items = PlacesUtils.annotations.getItemsWithAnnotation("mobile/bookmarksRoot", {});
1232
throw "Couldn't find mobile bookmarks root!";
1234
delete this.mobileRoot;
1235
return this.mobileRoot = items[0];
1239
this._panel = document.getElementById("bookmarklist-container");
1240
this._panel.width = window.innerWidth;
1241
this._panel.height = window.innerHeight;
1242
this._panel.hidden = false;
1243
BrowserUI.pushDialog(this);
1245
this._bookmarks = document.getElementById("bookmark-items");
1246
this._bookmarks.addEventListener("BookmarkRemove", this, true);
1247
this._bookmarks.manageUI = false;
1248
this._bookmarks.openFolder();
1250
this._manageButton = document.getElementById("tool-bookmarks-manage");
1251
this._manageButton.disabled = (this._bookmarks.items.length == 0);
1676
BrowserUI.popPopup(this);
1679
removeBookmarksForURI: function BH_removeBookmarksForURI(aURI) {
1680
//XXX blargle xpconnect! might not matter, but a method on
1681
// nsINavBookmarksService that takes an array of items to
1682
// delete would be faster. better yet, a method that takes a URI!
1683
let itemIds = PlacesUtils.getBookmarksForURI(aURI);
1684
itemIds.forEach(PlacesUtils.bookmarks.removeItem);
1255
1686
BrowserUI.updateStar();
1257
if (this._bookmarks.manageUI)
1258
this.toggleManage();
1259
this._bookmarks.blur();
1260
this._bookmarks.removeEventListener("BookmarkRemove", this, true);
1262
this._panel.hidden = true;
1263
BrowserUI.popDialog();
1266
toggleManage: function() {
1267
this._bookmarks.manageUI = !(this._bookmarks.manageUI);
1268
this._manageButton.checked = this._bookmarks.manageUI;
1271
openBookmark: function() {
1272
let item = this._bookmarks.activeItem;
1275
BrowserUI.goToURI(item.spec);
1279
handleEvent: function(aEvent) {
1280
if (aEvent.type == "BookmarkRemove") {
1281
if (this._bookmarks.isRootFolder && this._bookmarks.items.length == 1) {
1282
this._manageButton.disabled = true;
1283
this.toggleManage();
1690
var FindHelperUI = {
1693
next: "cmd_findNext",
1694
previous: "cmd_findPrevious",
1695
close: "cmd_findClose"
1294
delete this._container;
1295
return this._container = document.getElementById("form-helper-container");
1298
get _helperSpacer() {
1299
delete this._helperSpacer;
1300
return this._helperSpacer = document.getElementById("form-helper-spacer");
1303
get _selectContainer() {
1304
delete this._selectContainer;
1305
return this._selectContainer = document.getElementById("select-container");
1308
get _autofillContainer() {
1309
delete this._autofillContainer;
1310
return this._autofillContainer = document.getElementById("form-helper-autofill");
1313
_getRectForElement: function formHelper_getRectForElement(aElement) {
1314
const kDistanceMax = 100;
1315
let elRect = Browser.getBoundingContentRect(aElement);
1316
let bv = Browser._browserView;
1318
let labels = this.getLabelsFor(aElement);
1319
for (let i=0; i<labels.length; i++) {
1320
let labelRect = Browser.getBoundingContentRect(labels[i]);
1321
if (labelRect.left < elRect.left) {
1322
let isClose = Math.abs(labelRect.left - elRect.left) - labelRect.width < kDistanceMax &&
1323
Math.abs(labelRect.top - elRect.top) - labelRect.height < kDistanceMax;
1325
let width = labelRect.width + elRect.width + (elRect.left - labelRect.left - labelRect.width);
1326
return new Rect(labelRect.left, labelRect.top, width, elRect.height).expandToIntegers();
1333
_update: function(aPreviousElement, aNewElement) {
1334
this._updateSelect(aPreviousElement, aNewElement);
1336
// Setup autofill UI
1337
if (aNewElement instanceof HTMLInputElement && aNewElement.type == "text") {
1338
let suggestions = this._getSuggestions();
1339
this._setSuggestions(suggestions);
1702
return this._status;
1706
if (val != this._status) {
1708
this._textbox.setAttribute("status", val);
1709
this.updateCommands(this._textbox.value);
1713
init: function findHelperInit() {
1714
this._textbox = document.getElementById("find-helper-textbox");
1715
this._container = document.getElementById("content-navigator");
1717
this._cmdPrevious = document.getElementById(this.commands.previous);
1718
this._cmdNext = document.getElementById(this.commands.next);
1720
// Listen for form assistant messages from content
1721
messageManager.addMessageListener("FindAssist:Show", this);
1722
messageManager.addMessageListener("FindAssist:Hide", this);
1724
// Listen for events where form assistant should be closed
1725
document.getElementById("tabs").addEventListener("TabSelect", this, true);
1726
Elements.browsers.addEventListener("URLChanged", this, true);
1729
receiveMessage: function findHelperReceiveMessage(aMessage) {
1730
let json = aMessage.json;
1731
switch(aMessage.name) {
1732
case "FindAssist:Show":
1733
this.status = json.result;
1735
this._zoom(Rect.fromRect(json.rect));
1738
case "FindAssist:Hide":
1739
if (this._container.getAttribute("type") == this.type)
1745
handleEvent: function findHelperHandleEvent(aEvent) {
1746
if (aEvent.type == "TabSelect" || (aEvent.type == "URLChanged" && aEvent.target == Browser.selectedBrowser))
1750
show: function findHelperShow() {
1751
this._container.show(this);
1752
this.search(this._textbox.value);
1753
this._textbox.select();
1754
this._textbox.focus();
1757
// Prevent the view to scroll automatically while searching
1758
Browser.selectedBrowser.scrollSync = false;
1761
hide: function findHelperHide() {
1765
this._textbox.value = "";
1767
this._textbox.blur();
1768
this._container.hide(this);
1771
// Restore the scroll synchronisation
1772
Browser.selectedBrowser.scrollSync = true;
1775
goToPrevious: function findHelperGoToPrevious() {
1776
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Previous", { });
1779
goToNext: function findHelperGoToNext() {
1780
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Next", { });
1783
search: function findHelperSearch(aValue) {
1784
this.updateCommands(aValue);
1786
// Don't bother searching if the value is empty
1790
Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Find", { searchString: aValue });
1793
updateCommands: function findHelperUpdateCommands(aValue) {
1794
let disabled = (this._status == Ci.nsITypeAheadFind.FIND_NOTFOUND) || (aValue == "");
1795
this._cmdPrevious.setAttribute("disabled", disabled);
1796
this._cmdNext.setAttribute("disabled", disabled);
1799
_zoom: function _findHelperZoom(aElementRect) {
1800
// Zoom to a specified Rect
1801
if (aElementRect && Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("findhelper.autozoom")) {
1802
let zoomLevel = Browser._getZoomLevelForRect(aElementRect);
1803
zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
1804
zoomLevel = Browser.selectedTab.clampZoomLevel(zoomLevel);
1806
let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
1807
Browser.animatedZoomTo(zoomRect);
1813
* Responsible for navigating forms and filling in information.
1814
* - Navigating forms is handled by next and previous commands.
1815
* - When an element is focused, the browser view zooms in to the control.
1816
* - The caret positionning and the view are sync to keep the type
1817
* in text into view for input fields (text/textarea).
1818
* - Provides autocomplete box for input fields.
1820
var FormHelperUI = {
1823
next: "cmd_formNext",
1824
previous: "cmd_formPrevious",
1825
close: "cmd_formClose"
1829
return Services.prefs.getBoolPref("formhelper.enabled");
1832
_visibleScreenArea: null,
1833
get visibleScreenArea() {
1834
let visibleRect = Rect.fromRect(this._currentBrowser.getBoundingClientRect());
1835
let visibleScreenArea = visibleRect;
1836
if (this._visibleScreenArea) {
1837
visibleScreenArea = this._visibleScreenArea.clone();
1838
visibleScreenArea.x = visibleRect.x;
1839
visibleScreenArea.y = visibleRect.y;
1840
visibleScreenArea.width = visibleRect.width;
1841
visibleScreenArea.height = visibleRect.height - this._container.getBoundingClientRect().height;
1843
return visibleScreenArea;
1846
init: function formHelperInit() {
1847
this._container = document.getElementById("content-navigator");
1848
this._autofillContainer = document.getElementById("form-helper-autofill");
1849
this._cmdPrevious = document.getElementById(this.commands.previous);
1850
this._cmdNext = document.getElementById(this.commands.next);
1851
this._visibleScreenArea = new Rect(0, 0, 0, 0);
1853
// Listen for form assistant messages from content
1854
messageManager.addMessageListener("FormAssist:Show", this);
1855
messageManager.addMessageListener("FormAssist:Hide", this);
1856
messageManager.addMessageListener("FormAssist:Update", this);
1857
messageManager.addMessageListener("FormAssist:AutoComplete", this);
1859
// Listen for events where form assistant should be closed or updated
1860
let tabs = document.getElementById("tabs");
1861
tabs.addEventListener("TabSelect", this, true);
1862
tabs.addEventListener("TabClose", this, true);
1863
Elements.browsers.addEventListener("URLChanged", this, true);
1864
window.addEventListener("resize", this, true);
1866
// Listen for modal dialog to show/hide the UI
1867
messageManager.addMessageListener("DOMWillOpenModalDialog", this);
1868
messageManager.addMessageListener("DOMModalDialogClosed", this);
1870
Services.obs.addObserver(this, "softkb-change", false);
1873
uninit: function formHelperUninit() {
1874
Services.obs.removeObserver(this, "softkb-change");
1877
_currentBrowser: null,
1878
show: function formHelperShow(aElement, aHasPrevious, aHasNext) {
1879
this._currentBrowser = Browser.selectedBrowser;
1881
// Update the next/previous commands
1882
this._cmdPrevious.setAttribute("disabled", !aHasPrevious);
1883
this._cmdNext.setAttribute("disabled", !aHasNext);
1886
let lastElement = this._currentElement || null;
1887
this._currentElement = {
1889
name: aElement.name,
1890
value: aElement.value,
1891
maxLength: aElement.maxLength,
1892
type: aElement.type,
1893
isAutocomplete: aElement.isAutocomplete,
1894
list: aElement.choices
1897
this._updateContainer(lastElement, this._currentElement);
1898
this._zoom(Rect.fromRect(aElement.rect), Rect.fromRect(aElement.caretRect));
1900
// Prevent the view to scroll automatically while typing
1901
this._currentBrowser.scrollSync = false;
1904
hide: function formHelperHide() {
1908
// Restore the scroll synchonisation
1909
this._currentBrowser.scrollSync = true;
1911
// reset current Element and Caret Rect
1912
this._currentElementRect = null;
1913
this._currentCaretRect = null;
1915
this._updateContainerForSelect(this._currentElement, null);
1917
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Closed", { });
1921
handleEvent: function formHelperHandleEvent(aEvent) {
1925
switch (aEvent.type) {
1932
if (aEvent.target == Browser.selectedBrowser)
1937
setTimeout(function(self) {
1938
SelectHelperUI.resize();
1939
self._container.contentHasChanged();
1945
receiveMessage: function formHelperReceiveMessage(aMessage) {
1946
let json = aMessage.json;
1947
switch (aMessage.name) {
1948
case "FormAssist:Show":
1949
// if the user has manually disabled the Form Assistant UI we still
1950
// want to show a UI for <select /> element but not managed by
1952
this.enabled ? this.show(json.current, json.hasPrevious, json.hasNext)
1953
: SelectHelperUI.show(json.current.choices);
1956
case "FormAssist:Hide":
1960
case "FormAssist:AutoComplete":
1961
this._updateAutocompleteFor(json.current);
1962
this._container.contentHasChanged();
1965
case "FormAssist:Update":
1966
Browser.hideSidebars();
1967
Browser.hideTitlebar();
1968
this._zoom(null, Rect.fromRect(json.caretRect));
1971
case "DOMWillOpenModalDialog":
1972
if (this._open && aMessage.target == Browser.selectedBrowser) {
1973
this._container.style.display = "none";
1974
this._container._spacer.hidden = true;
1978
case "DOMModalDialogClosed":
1979
if (this._open && aMessage.target == Browser.selectedBrowser) {
1980
this._container.style.display = "-moz-box";
1981
this._container._spacer.hidden = false;
1987
// for VKB that does not resize the window
1988
_currentCaretRect: null,
1989
_currentElementRect: null,
1990
observe: function formHelperObserve(aSubject, aTopic, aData) {
1991
let rect = Rect.fromRect(JSON.parse(aData));
1992
rect.height = rect.bottom - rect.top;
1993
rect.width = rect.right - rect.left;
1995
this._visibleScreenArea = rect;
1996
BrowserUI.sizeControls(rect.width, rect.height);
1998
this._zoom(this._currentElementRect, this._currentCaretRect);
2001
goToPrevious: function formHelperGoToPrevious() {
2002
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { });
2005
goToNext: function formHelperGoToNext() {
2006
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Next", { });
2009
doAutoComplete: function formHelperDoAutoComplete(aElement) {
2010
// Suggestions are only in <label>s. Ignore the rest.
2011
if (aElement instanceof Ci.nsIDOMXULLabelElement)
2012
this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.value });
2016
return (this._container.getAttribute("type") == this.type);
2020
if (aVal == this._open)
2023
this._container.hidden = !aVal;
2024
this._container.contentHasChanged();
2028
this._container.show(this);
1341
this._autofillContainer.collapsed = true;
1344
let height = Math.floor(this._container.getBoundingClientRect().height);
1345
this._container.top = window.innerHeight - height;
1347
document.getElementById("form-helper-previous").disabled = !this._getPrevious();
1348
document.getElementById("form-helper-next").disabled = !this._getNext();
1351
_updateSelect: function(aPreviousElement, aNewElement) {
1352
let previousIsSelect = this._isValidSelectElement(aPreviousElement);
1353
let currentIsSelect = this._isValidSelectElement(aNewElement);
1355
if (currentIsSelect && !previousIsSelect) {
1356
this._selectContainer.style.maxHeight = (window.innerHeight / 1.8) + "px";
1358
let rootNode = this._container;
1359
rootNode.insertBefore(this._selectContainer, rootNode.lastChild);
1361
SelectHelper.show(aNewElement);
1363
else if (currentIsSelect && previousIsSelect) {
1364
SelectHelper.reset();
1365
SelectHelper.show(aNewElement);
1367
else if (!currentIsSelect && previousIsSelect) {
1368
let rootNode = this._container.parentNode;
1369
rootNode.insertBefore(this._selectContainer, rootNode.lastChild);
1371
SelectHelper.close();
1375
_isValidElement: function(aElement) {
1376
if (aElement.disabled)
1379
if (aElement.getAttribute("role") == "button" && aElement.hasAttribute("tabindex"))
1380
return this._isElementVisible(aElement);
1382
if (this._isValidSelectElement(aElement) || aElement instanceof HTMLTextAreaElement)
1383
return this._isElementVisible(aElement);
1385
if (aElement instanceof HTMLInputElement || aElement instanceof HTMLButtonElement) {
1386
if (aElement.type == "hidden")
1388
return this._isElementVisible(aElement);
1394
_isValidSelectElement: function(aElement) {
1395
return SelectHelper.canShowUIFor(aElement);
1398
_isElementVisible: function(aElement) {
1399
let style = aElement.ownerDocument.defaultView.getComputedStyle(aElement, null);
1403
let isVisible = (style.getPropertyValue("visibility") != "hidden");
1404
let isOpaque = (style.getPropertyValue("opacity") != 0);
1406
let rect = aElement.getBoundingClientRect();
1407
return isVisible && isOpaque && (rect.height != 0 || rect.width != 0);
1410
_getAll: function() {
1413
// get all the documents
1414
let documents = [getBrowser().contentDocument];
1415
let iframes = getBrowser().contentDocument.querySelectorAll("iframe, frame");
1416
for (let i = 0; i < iframes.length; i++)
1417
documents.push(iframes[i].contentDocument);
1419
for (let i = 0; i < documents.length; i++) {
1420
let nodes = documents[i].querySelectorAll("input, button, select, textarea, [role=button]");
1421
nodes = this._filterRadioButtons(nodes).filter(this._isValidElement, this);
1422
elements = elements.concat(nodes);
1425
function orderByTabIndex(a, b) {
1426
// for an explanation on tabbing navigation see
1427
// http://www.w3.org/TR/html401/interact/forms.html#h-17.11.1
1428
// In resume tab index navigation order is 1, 2, 3, ..., 32767, 0
1429
if (a.tabIndex == 0 || b.tabIndex == 0)
1432
return a.tabIndex > b.tabIndex;
1434
return elements.sort(orderByTabIndex);
1438
* For each radio button group, remove all but the checked button
1439
* if there is one, or the first button otherwise.
1441
_filterRadioButtons: function(nodes) {
1442
// First pass: Find the checked or first element in each group.
1443
let chosenRadios = {};
1444
for (let i=0; i < nodes.length; i++) {
1445
let node = nodes[i];
1446
if (node.type == "radio" && (!chosenRadios.hasOwnProperty(node.name) || node.checked))
1447
chosenRadios[node.name] = node;
1450
// Second pass: Exclude all other radio buttons from the list.
1452
for (let i=0; i < nodes.length; i++) {
1453
let node = nodes[i];
1454
if (node.type == "radio" && chosenRadios[node.name] != node)
1461
_getPrevious: function() {
1462
let index = this._nodes.indexOf(this._currentElement);
1463
let node = (index != -1 ? this._nodes[--index] : null);
1464
while (node && !this._isElementVisible(node))
1465
node = this._nodes[--index];
1469
_getNext: function() {
1470
let index = this._nodes.indexOf(this._currentElement);
1471
let node = (index != -1 ? this._nodes[++index] : null);
1472
while (node && !this._isElementVisible(node))
1473
node = this._nodes[++index];
1477
_fac: Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete),
1478
_getSuggestions: function() {
1479
let suggestions = [];
1480
let currentValue = this._currentElement.value;
1481
let results = this._fac.autoCompleteSearch(this._currentElement.name, currentValue, this._currentElement, null);
1482
if (results.matchCount > 0) {
1483
for (let i = 0; i < results.matchCount; i++) {
1484
let value = results.getValueAt(i);
1485
suggestions.push(value);
1492
_setSuggestions: function(aSuggestions) {
2031
this._currentElement = null;
2032
this._container.hide(this);
2035
let evt = document.createEvent("UIEvents");
2036
evt.initUIEvent("FormUI", true, true, window, aVal);
2037
this._container.dispatchEvent(evt);
2040
_updateAutocompleteFor: function _formHelperUpdateAutocompleteFor(aElement) {
2041
let suggestions = this._getAutocompleteSuggestions(aElement);
2042
this._displaySuggestions(suggestions);
2045
_displaySuggestions: function _formHelperDisplaySuggestions(aSuggestions) {
1493
2046
let autofill = this._autofillContainer;
1494
2047
while (autofill.hasChildNodes())
1495
2048
autofill.removeChild(autofill.lastChild);
1499
2052
let value = aSuggestions[i];
1500
2053
let button = document.createElement("label");
1501
2054
button.setAttribute("value", value);
2055
button.className = "form-helper-autofill-label";
1502
2056
fragment.appendChild(button);
1504
2058
autofill.appendChild(fragment);
1505
2059
autofill.collapsed = !aSuggestions.length;
1508
doAutoFill: function formHelperDoAutoFill(aElement) {
1509
if (!this._currentElement)
1512
// Suggestions are only in <label>s. Ignore the rest.
1513
if (aElement instanceof Ci.nsIDOMXULLabelElement)
1514
this._currentElement.value = aElement.value;
1517
getLabelsFor: function(aElement) {
1518
let associatedLabels = [];
1519
if (this._isValidElement(aElement)) {
1520
let labels = aElement.ownerDocument.getElementsByTagName("label");
1521
for (let i=0; i<labels.length; i++) {
1522
if (labels[i].getAttribute("for") == aElement.id)
1523
associatedLabels.push(labels[i]);
1527
if (aElement.parentNode instanceof HTMLLabelElement)
1528
associatedLabels.push(aElement.parentNode);
1530
return associatedLabels.filter(this._isElementVisible);
1533
_currentElement: null,
1534
getCurrentElement: function() {
1535
return this._currentElement;
1538
setCurrentElement: function(aElement) {
1542
let previousElement = this._currentElement;
1543
this._currentElement = aElement;
1544
this._utils = aElement.ownerDocument.defaultView
1545
.QueryInterface(Ci.nsIInterfaceRequestor)
1546
.getInterface(Ci.nsIDOMWindowUtils);
1548
this._utils.QueryInterface(Ci.nsIDOMWindowUtils_1_9_2);
1552
this._update(previousElement, aElement);
1554
let containerHeight = this._container.getBoundingClientRect().height;
1555
this._helperSpacer.setAttribute("height", containerHeight);
1557
this.zoom(aElement);
1558
gFocusManager.setFocus(aElement, Ci.nsIFocusManager.FLAG_NOSCROLL);
1561
goToPrevious: function formHelperGoToPrevious() {
1562
let previous = this._getPrevious();
1563
this.setCurrentElement(previous);
1566
goToNext: function formHelperGoToNext() {
1567
let next = this._getNext();
1568
this.setCurrentElement(next);
1571
open: function formHelperOpen(aElement) {
1572
if (this._open == true && aElement == this._currentElement &&
1573
gFocusManager.focusedElement == this._currentElement)
1577
this._restore = { zoom: Browser._browserView.getZoomLevel(),
1578
scroll: Browser.getScrollboxPosition(Browser.contentScrollboxScroller)
1581
window.addEventListener("keyup", this, false);
1582
let bv = Browser._browserView;
1583
bv.ignorePageScroll(true);
1585
this._container.hidden = false;
1586
this._helperSpacer.hidden = false;
1588
this._nodes = this._getAll();
1589
this.setCurrentElement(aElement);
1591
let evt = document.createEvent("UIEvents");
1592
evt.initUIEvent("FormUI", true, true, window, this._open);
1593
this._container.dispatchEvent(evt);
1598
close: function formHelperHide() {
1602
this._updateSelect(this._currentElement, null);
1604
this._helperSpacer.hidden = true;
1606
// give the form spacer area back to the content
1607
let bv = Browser._browserView;
1608
Browser.forceChromeReflow();
1609
Browser.contentScrollboxScroller.scrollBy(0, 0);
1610
bv.onAfterVisibleMove();
1612
bv.ignorePageScroll(false);
1614
window.removeEventListener("keyup", this, false);
1615
this._container.hidden = true;
1616
this._currentElement = null;
1619
if (gPrefService.getBoolPref("formhelper.restore") && this._restore) {
1620
bv.setZoomLevel(this._restore.zoom);
1621
Browser.contentScrollboxScroller.scrollTo(this._restore.scroll.x, this._restore.scroll.y);
1622
bv.onAfterVisibleMove();
1623
this._restore = null;
1628
let evt = document.createEvent("UIEvents");
1629
evt.initUIEvent("FormUI", true, true, window, this._open);
1630
this._container.dispatchEvent(evt);
1633
handleEvent: function formHelperHandleEvent(aEvent) {
1634
let isChromeFocused = gFocusManager.getFocusedElementForWindow(window, false, {}) == gFocusManager.focusedElement;
1635
if (isChromeFocused)
1638
let currentElement = this.getCurrentElement();
1639
switch (aEvent.keyCode) {
1640
case aEvent.DOM_VK_DOWN:
1641
if (currentElement instanceof HTMLTextAreaElement) {
1642
let existSelection = currentElement.selectionEnd - currentElement.selectionStart;
1643
let isEnd = (currentElement.textLength == currentElement.selectionEnd);
1644
if (!isEnd || existSelection)
1651
case aEvent.DOM_VK_UP:
1652
if (currentElement instanceof HTMLTextAreaElement) {
1653
let existSelection = currentElement.selectionEnd - currentElement.selectionStart;
1654
let isStart = (currentElement.selectionEnd == 0);
1655
if (!isStart || existSelection)
1659
this.goToPrevious();
1662
case aEvent.DOM_VK_RETURN:
1666
let caret = this._getRectForCaret();
1668
let bv = Browser._browserView;
1670
// If the caret is not into view we need to scroll to it
1671
let visible = bv.getVisibleRect();
1672
caret = bv.browserToViewportRect(caret);
1674
let [deltaX, deltaY] = this._getOffsetForCaret(caret, visible);
1676
// Scroll by the delta if we need to
1677
if (deltaX != 0 || deltaY != 0) {
1678
Browser.contentScrollboxScroller.scrollBy(deltaX, deltaY);
1679
bv.onAfterVisibleMove();
1683
let target = aEvent.target;
1684
if (currentElement instanceof HTMLInputElement && currentElement.type == "text") {
1685
let suggestions = this._getSuggestions();
1686
this._setSuggestions(suggestions);
1688
let height = Math.floor(this._container.getBoundingClientRect().height);
1689
this._container.top = window.innerHeight - height;
1690
this._helperSpacer.setAttribute("height", height);
1692
// XXX if we are at the bottom of the page we need to give back the content
1693
// area by refreshing it
1694
if (suggestions.length == 0) {
1695
let bv = Browser._browserView;
1696
Browser.forceChromeReflow();
1697
Browser.contentScrollboxScroller.scrollBy(0, 0);
1698
bv.onAfterVisibleMove();
1700
} else if (currentElement == target && this._isValidSelectElement(target)) {
1701
SelectHelper.unselectAll();
1702
SelectHelper.selectByIndex(target.selectedIndex);
1708
zoom: function formHelperZoom(aElement) {
1709
let bv = Browser._browserView;
1713
let zoomLevel = bv.getZoomLevel();
1714
if (gPrefService.getBoolPref("formhelper.autozoom")) {
1715
zoomLevel = Browser._getZoomLevelForElement(aElement);
1716
zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
1719
let elRect = this._getRectForElement(aElement);
1720
let zoomRect = Browser._getZoomRectForPoint(elRect.center().x, elRect.y, zoomLevel);
1722
let caretRect = this._getRectForCaret();
1724
caretRect = Browser._browserView.browserToViewportRect(caretRect);
1725
if (!zoomRect.contains(caretRect)) {
1726
let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
1727
zoomRect.translate(deltaX, deltaY);
1731
Browser.setVisibleRect(zoomRect);
1734
_getRectForCaret: function formHelper_getRectForCaret() {
1735
let currentElement = this.getCurrentElement();
1736
if ((currentElement instanceof HTMLTextAreaElement ||
1737
(currentElement instanceof HTMLInputElement && currentElement.type == "text")) &&
1738
gFocusManager.focusedElement == this._currentElement) {
1740
let rect = this._utils.sendQueryContentEvent(this._utils.QUERY_CARET_RECT, currentElement.selectionEnd, 0, 0, 0);
1744
let bv = Browser._browserView;
1745
let scroll = BrowserView.Util.getContentScrollOffset(Browser.selectedBrowser);
1746
let caret = new Rect(scroll.x + rect.left, scroll.y + rect.top, rect.width, rect.height);
1753
_getOffsetForCaret: function formHelper_getOffsetForCaret(aCaretRect, aRect) {
2062
/** Retrieve the autocomplete list from the autocomplete service for an element */
2063
_getAutocompleteSuggestions: function _formHelperGetAutocompleteSuggestions(aElement) {
2064
if (!aElement.isAutocomplete)
2067
let suggestions = [];
2069
let autocompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete);
2070
let results = autocompleteService.autoCompleteSearch(aElement.name, aElement.value, aElement, null);
2071
if (results.matchCount > 0) {
2072
for (let i = 0; i < results.matchCount; i++) {
2073
let value = results.getValueAt(i);
2074
suggestions.push(value);
2081
/** Update the form helper container to reflect new element user is editing. */
2082
_updateContainer: function _formHelperUpdateContainer(aLastElement, aCurrentElement) {
2083
this._updateContainerForSelect(aLastElement, aCurrentElement);
2085
// Setup autofill UI
2086
this._updateAutocompleteFor(aCurrentElement);
2087
this._container.contentHasChanged();
2090
/** Helper for _updateContainer that handles the case where the new element is a select. */
2091
_updateContainerForSelect: function _formHelperUpdateContainerForSelect(aLastElement, aCurrentElement) {
2092
let lastHasChoices = aLastElement && (aLastElement.list != null);
2093
let currentHasChoices = aCurrentElement && (aCurrentElement.list != null);
2095
if (!lastHasChoices && currentHasChoices) {
2096
SelectHelperUI.dock(this._container);
2097
SelectHelperUI.show(aCurrentElement.list);
2098
} else if (lastHasChoices && currentHasChoices) {
2099
SelectHelperUI.reset();
2100
SelectHelperUI.show(aCurrentElement.list);
2101
} else if (lastHasChoices && !currentHasChoices) {
2102
SelectHelperUI.hide();
2106
/** Zoom and move viewport so that element is legible and touchable. */
2107
_zoom: function _formHelperZoom(aElementRect, aCaretRect) {
2108
let zoomRect = this.visibleScreenArea;
2109
let browser = getBrowser();
2111
// Zoom to a specified Rect
2112
if (aElementRect && Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom")) {
2113
this._currentElementRect = aElementRect;
2114
// Zoom to an element by keeping the caret into view
2115
let zoomLevel = Browser.selectedTab.clampZoomLevel(this._getZoomLevelForRect(aElementRect));
2117
zoomRect = this._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
2118
Browser.animatedZoomTo(zoomRect);
2121
this._ensureCaretVisible(aCaretRect);
2124
_ensureCaretVisible: function _ensureCaretVisible(aCaretRect) {
2128
// the scrollX/scrollY position can change because of the animated zoom so
2129
// delay the caret adjustment
2130
if (AnimatedZoom.isZooming()) {
2132
window.addEventListener("AnimatedZoomEnd", function() {
2133
window.removeEventListener("AnimatedZoomEnd", arguments.callee, true);
2134
self._ensureCaretVisible(aCaretRect);
2139
let browser = getBrowser();
2140
let zoomRect = this.visibleScreenArea;
2142
this._currentCaretRect = aCaretRect;
2143
let caretRect = aCaretRect.scale(browser.scale, browser.scale);
2145
let scroll = browser.getPosition();
2146
zoomRect = new Rect(scroll.x, scroll.y, zoomRect.width, zoomRect.height);
2147
if (zoomRect.contains(caretRect))
2150
let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
2151
if (deltaX != 0 || deltaY != 0)
2152
browser.scrollBy(deltaX, deltaY);
2155
/* Store the current zoom level, and scroll positions to restore them if needed */
2156
_zoomStart: function _formHelperZoomStart() {
2157
if (!Services.prefs.getBoolPref("formhelper.restore"))
2161
scale: getBrowser().scale,
2162
contentScrollOffset: Browser.getScrollboxPosition(Browser.contentScrollboxScroller),
2163
pageScrollOffset: Browser.getScrollboxPosition(Browser.pageScrollboxScroller)
2167
/** Element is no longer selected. Restore zoom level if setting is enabled. */
2168
_zoomFinish: function _formHelperZoomFinish() {
2169
if(!Services.prefs.getBoolPref("formhelper.restore"))
2172
let restore = this._restore;
2173
getBrowser().scale = restore.scale;
2174
Browser.contentScrollboxScroller.scrollTo(restore.contentScrollOffset.x, restore.contentScrollOffset.y);
2175
Browser.pageScrollboxScroller.scrollTo(restore.pageScrollOffset.x, restore.pageScrollOffset.y);
2178
_getZoomRectForPoint: function _getZoomRectForPoint(x, y, zoomLevel) {
2179
let browser = getBrowser();
2180
x = x * browser.scale;
2181
y = y * browser.scale;
2183
let vis = this.visibleScreenArea
2184
zoomLevel = Math.min(ZoomManager.MAX, zoomLevel);
2185
let oldScale = browser.scale;
2186
let zoomRatio = zoomLevel / oldScale;
2187
let newVisW = vis.width / zoomRatio, newVisH = vis.height / zoomRatio;
2188
let result = new Rect(x - newVisW / 2, y - newVisH / 2, newVisW, newVisH);
2190
// Make sure rectangle doesn't poke out of viewport
2191
return result.translateInside(new Rect(0, 0, browser.contentDocumentWidth * oldScale,
2192
browser.contentDocumentHeight * oldScale));
2195
_getZoomLevelForRect: function _getZoomLevelForRect(aRect) {
2197
let zoomLevel = this.visibleScreenArea.width / (aRect.width + margin);
2198
return Util.clamp(zoomLevel, kBrowserFormZoomLevelMin, kBrowserFormZoomLevelMax);
2201
_getOffsetForCaret: function _formHelperGetOffsetForCaret(aCaretRect, aRect) {
1754
2202
// Determine if we need to move left or right to bring the caret into view
1755
2203
let deltaX = 0;
1756
2204
if (aCaretRect.right > aRect.right)
1757
2205
deltaX = aCaretRect.right - aRect.right;
1758
2206
if (aCaretRect.left < aRect.left)
1759
2207
deltaX = aCaretRect.left - aRect.left;
1761
2209
// Determine if we need to move up or down to bring the caret into view
1762
2210
let deltaY = 0;
1763
2211
if (aCaretRect.bottom > aRect.bottom)
1766
2214
deltaY = aCaretRect.top - aRect.top;
1768
2216
return [deltaX, deltaY];
1771
canShowUIFor: function(aElement) {
1775
// Some forms elements are valid in the sense that we want the Form
1776
// Assistant to stop on it, but we don't want it to display when
1777
// the user clicks on it
1778
let formExceptions = {button: true, checkbox: true, file: true, image: true, radio: true, reset: true, submit: true};
1779
if (aElement instanceof HTMLInputElement && formExceptions[aElement.type])
1782
if (aElement instanceof HTMLButtonElement || (aElement.getAttribute("role") == "button" && aElement.hasAttribute("tabindex")))
1785
return this._isValidElement(aElement);
1789
function SelectWrapper(aControl) {
1790
this._control = aControl;
1793
SelectWrapper.prototype = {
1794
get selectedIndex() { return this._control.selectedIndex; },
1795
get multiple() { return this._control.multiple; },
1796
get options() { return this._control.options; },
1797
get children() { return this._control.children; },
1799
getText: function(aChild) { return aChild.text; },
1800
isOption: function(aChild) { return aChild instanceof HTMLOptionElement; },
1801
isGroup: function(aChild) { return aChild instanceof HTMLOptGroupElement; },
1802
select: function(aIndex, aSelected, aClearAll) {
1803
let selectElement = this._control.QueryInterface(Ci.nsISelectElement);
1804
selectElement.setOptionsSelectedByIndex(aIndex, aIndex, aSelected, aClearAll, false, true);
1806
focus: function() { this._control.focus(); },
1807
fireOnChange: function() {
1808
let control = this._control;
1809
let evt = document.createEvent("Events");
1810
evt.initEvent("change", true, true, window, 0,
1812
false, false, null);
1813
setTimeout(function() {
1814
control.dispatchEvent(evt)
1819
function MenulistWrapper(aControl) {
1820
this._control = aControl;
1823
// Use wrappedJSObject when control is in content for extra protection
1826
MenulistWrapper.prototype = {
1827
get selectedIndex() {
1828
let control = this._control.wrappedJSObject || this._control;
1829
let result = control.selectedIndex;
1830
return (typeof result == "number" && !isNaN(result) ? result : -1);
1832
get multiple() { return false; },
1834
let control = this._control.wrappedJSObject || this._control;
1835
return control.menupopup.children;
1838
let control = this._control.wrappedJSObject || this._control;
1839
return control.menupopup.children;
1842
getText: function(aChild) { return aChild.label; },
1843
isOption: function(aChild) { return aChild instanceof Ci.nsIDOMXULSelectControlItemElement; },
1844
isGroup: function(aChild) { return false },
1845
select: function(aIndex, aSelected, aClearAll) {
1846
let control = this._control.wrappedJSObject || this._control;
1847
control.selectedIndex = aIndex;
1849
focus: function() { this._control.focus(); },
1850
fireOnChange: function() {
1851
let control = this._control;
1852
let evt = document.createEvent("XULCommandEvent");
1853
evt.initCommandEvent("command", true, true, window, 0,
1855
false, false, null);
1856
setTimeout(function() {
1857
control.dispatchEvent(evt)
1862
var SelectHelper = {
2221
* SelectHelperUI: Provides an interface for making a choice in a list.
2222
* Supports simultaneous selection of choices and group headers.
2224
var SelectHelperUI = {
1866
_selectedIndexes: [],
2226
_selectedIndexes: null,
2230
return this._panel = document.getElementById("select-container");
2234
delete this._textbox;
2235
return this._textbox = document.getElementById("select-helper-textbox");
2238
show: function(aList) {
2239
this.showFilter = false;
2240
this._textbox.blur();
2243
this._container = document.getElementById("select-list");
2244
this._container.setAttribute("multiple", aList.multiple ? "true" : "false");
2246
this._selectedIndexes = this._getSelectedIndexes();
2247
let firstSelected = null;
2249
let choices = aList.choices;
2250
for (let i = 0; i < choices.length; i++) {
2251
let choice = choices[i];
2252
let item = document.createElement("option");
2253
item.className = "chrome-select-option";
2254
item.setAttribute("label", choice.text);
2255
choice.disabled ? item.setAttribute("disabled", choice.disabled)
2256
: item.removeAttribute("disabled");
2257
this._container.appendChild(item);
2260
item.classList.add("optgroup");
2264
item.optionIndex = choice.optionIndex;
2265
item.choiceIndex = i;
2268
item.classList.add("in-optgroup");
2270
if (choice.selected) {
2271
item.setAttribute("selected", "true");
2272
firstSelected = firstSelected || item;
2276
this._panel.hidden = false;
2277
this._panel.height = this._panel.getBoundingClientRect().height;
2280
BrowserUI.pushPopup(this, this._panel);
2282
this._scrollElementIntoView(firstSelected);
2284
this._container.addEventListener("click", this, false);
2285
this._panel.addEventListener("overflow", this, true);
2290
return this._showFilter;
2293
set showFilter(val) {
2294
this._showFilter = val;
2295
if (!this._panel.hidden)
2296
this._textbox.hidden = !val;
2299
dock: function dock(aContainer) {
2300
aContainer.insertBefore(this._panel, aContainer.lastChild);
2302
this._docked = true;
2305
undock: function undock() {
2306
let rootNode = Elements.stack;
2307
rootNode.insertBefore(this._panel, rootNode.lastChild);
2308
this._panel.style.maxHeight = "";
2309
this._docked = false;
2313
this._updateControl();
2314
let empty = this._container.cloneNode(false);
2315
this._container.parentNode.replaceChild(empty, this._container);
2316
this._container = empty;
2318
this._selectedIndexes = null;
2319
this._panel.height = "";
2320
this._textbox.value = "";
2323
resize: function resize() {
2324
this._panel.style.maxHeight = (window.innerHeight / 1.8) + "px";
2328
this.showFilter = false;
2329
this._container.removeEventListener("click", this, false);
2330
this._panel.removeEventListener("overflow", this, true);
2332
this._panel.hidden = true;
2337
BrowserUI.popPopup(this);
2342
filter: function(aValue) {
2343
let reg = new RegExp(aValue, "gi");
2344
let options = this._container.childNodes;
2345
for (let i = 0; i < options.length; i++) {
2346
let option = options[i];
2347
option.getAttribute("label").match(reg) ? option.removeAttribute("filtered")
2348
: option.setAttribute("filtered", "true");
2352
unselectAll: function() {
2356
let choices = this._list.choices;
2357
this._forEachOption(function(aItem, aIndex) {
2358
aItem.selected = false;
2359
choices[aIndex].selected = false;
2363
selectByIndex: function(aIndex) {
2367
let choices = this._list.choices;
2368
for (let i = 0; i < this._container.childNodes.length; i++) {
2369
let option = this._container.childNodes[i];
2370
if (option.optionIndex == aIndex) {
2371
option.selected = true;
2372
this._choices[i].selected = true;
2373
this._scrollElementIntoView(option);
1868
2379
_getSelectedIndexes: function() {
1869
2380
let indexes = [];
1870
let control = this._control;
1872
if (control.multiple) {
1873
for (let i = 0; i < control.options.length; i++) {
1874
if (control.options[i].selected)
1879
indexes.push(control.selectedIndex);
2384
let choices = this._list.choices;
2385
let choiceLength = choices.length;
2386
for (let i = 0; i < choiceLength; i++) {
2387
let choice = choices[i];
2388
if (choice.selected)
2389
indexes.push(choice.optionIndex);
1882
2391
return indexes;
1885
show: function(aControl) {
1889
if (aControl instanceof HTMLSelectElement)
1890
this._control = new SelectWrapper(aControl);
1891
else if (aControl instanceof Ci.nsIDOMXULMenuListElement)
1892
this._control = new MenulistWrapper(aControl);
1894
throw "Unknown list element";
1896
this._selectedIndexes = this._getSelectedIndexes();
1898
this._list = document.getElementById("select-list");
1899
this._list.setAttribute("multiple", this._control.multiple ? "true" : "false");
1901
let firstSelected = null;
1903
let optionIndex = 0;
1904
let children = this._control.children;
1905
for (let i=0; i<children.length; i++) {
1906
let child = children[i];
1907
if (this._control.isGroup(child)) {
1908
let group = document.createElement("option");
1909
group.setAttribute("label", child.label);
1910
this._list.appendChild(group);
1911
group.className = "optgroup";
1913
let subchildren = child.children;
1914
for (let ii=0; ii<subchildren.length; ii++) {
1915
let subchild = subchildren[ii];
1916
let item = document.createElement("option");
1917
item.setAttribute("label", this._control.getText(subchild));
1918
this._list.appendChild(item);
1919
item.className = "in-optgroup";
1920
item.optionIndex = optionIndex++;
1921
if (subchild.selected) {
1922
item.setAttribute("selected", "true");
1923
firstSelected = firstSelected ? firstSelected : item;
1926
} else if (this._control.isOption(child)) {
1927
let item = document.createElement("option");
1928
item.setAttribute("label", this._control.getText(child));
1929
this._list.appendChild(item);
1930
item.optionIndex = optionIndex++;
1931
if (child.selected) {
1932
item.setAttribute("selected", "true");
1933
firstSelected = firstSelected ? firstSelected : item;
1938
this._panel = document.getElementById("select-container");
1939
this._panel.hidden = false;
1941
this._scrollElementIntoView(firstSelected);
1943
this._list.addEventListener("click", this, false);
1946
2394
_scrollElementIntoView: function(aElement) {
2043
2453
let item = aEvent.target;
2044
2454
if (item && item.hasOwnProperty("optionIndex")) {
2045
if (this._control.multiple) {
2455
if (this._list.multiple) {
2046
2456
// Toggle the item state
2047
2457
item.selected = !item.selected;
2048
this._control.select(item.optionIndex, item.selected, false);
2051
2460
this.unselectAll();
2053
2462
// Select the new one and update the control
2054
2463
item.selected = true;
2055
this._control.select(item.optionIndex, true, true);
2465
this.onSelect(item.optionIndex, item.selected, !this._list.multiple);
2469
if (!this._textbox.value)
2470
this.showFilter = true;
2475
onSelect: function(aIndex, aSelected, aClearAll) {
2478
selected: aSelected,
2481
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", json);
2483
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-button-element.html#the-select-element
2484
if (!this._list.multiple) {
2485
this._updateControl();
2486
// Update the selectedIndex so the field will fire a new change event if
2488
this._selectedIndexes = [aIndex];
2063
const kXLinkNamespace = "http://www.w3.org/1999/xlink";
2494
var MenuListHelperUI = {
2496
delete this._container;
2497
return this._container = document.getElementById("menulist-container");
2502
return this._popup = document.getElementById("menulist-popup");
2507
return this._title = document.getElementById("menulist-title");
2510
_firePopupEvent: function firePopupEvent(aEventName) {
2511
let menupopup = this._currentList.menupopup;
2512
if (menupopup.hasAttribute(aEventName)) {
2513
let func = new Function("event", menupopup.getAttribute(aEventName));
2519
show: function mn_show(aMenulist) {
2520
this._currentList = aMenulist;
2521
this._container.setAttribute("for", aMenulist.id);
2522
this._title.value = aMenulist.title || "";
2523
this._firePopupEvent("onpopupshowing");
2525
let container = this._container;
2526
let listbox = this._popup.lastChild;
2527
while (listbox.firstChild)
2528
listbox.removeChild(listbox.firstChild);
2530
let children = this._currentList.menupopup.children;
2531
for (let i = 0; i < children.length; i++) {
2532
let child = children[i];
2533
let item = document.createElement("richlistitem");
2535
item.setAttribute("disabled", "true");
2537
item.setAttribute("hidden", "true");
2539
// Add selected as a class name instead of an attribute to not being overidden
2540
// by the richlistbox behavior (it sets the "current" and "selected" attribute
2541
item.setAttribute("class", "menulist-command prompt-button" + (child.selected ? " selected" : ""));
2543
let image = document.createElement("image");
2544
image.setAttribute("src", child.image || "");
2545
item.appendChild(image);
2547
let label = document.createElement("label");
2548
label.setAttribute("value", child.label);
2549
item.appendChild(label);
2551
listbox.appendChild(item);
2554
window.addEventListener("resize", this, true);
2555
container.hidden = false;
2556
this.sizeToContent();
2557
BrowserUI.pushPopup(this, [this._popup]);
2560
hide: function mn_hide() {
2561
this._currentList = null;
2562
this._container.removeAttribute("for");
2563
this._container.hidden = true;
2564
window.removeEventListener("resize", this, true);
2565
BrowserUI.popPopup(this);
2568
selectByIndex: function mn_selectByIndex(aIndex) {
2569
this._currentList.selectedIndex = aIndex;
2571
// Dispatch a xul command event to the attached menulist
2572
if (this._currentList.dispatchEvent) {
2573
let evt = document.createEvent("XULCommandEvent");
2574
evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
2575
this._currentList.dispatchEvent(evt);
2581
sizeToContent: function sizeToContent() {
2582
this._popup.maxWidth = window.innerWidth * 0.75;
2585
handleEvent: function handleEvent(aEvent) {
2586
this.sizeToContent();
2065
2590
var ContextHelper = {
2068
onSaveableLink: false,
2071
onLoadedImage: false,
2076
_clearState: function ch_clearState() {
2077
this.popupNode = null;
2078
this.onLink = false;
2079
this.onSaveableLink = false;
2080
this.onVoiceLink = false;
2081
this.onImage = false;
2082
this.onLoadedImage = false;
2084
this.linkProtocol = null;
2088
_getLinkURL: function ch_getLinkURL(aLink) {
2089
let href = aLink.href;
2093
href = aLink.getAttributeNS(kXLinkNamespace, "href");
2094
if (!href || !href.match(/\S/)) {
2095
// Without this we try to save as the current doc,
2096
// for example, HTML case also throws if empty
2100
return Util.makeURLAbsolute(aLink.baseURI, href);
2103
_getURI: function ch_getURI(aURL) {
2105
return makeURI(aURL);
2111
_getProtocol: function ch_getProtocol(aURI) {
2117
_isSaveable: function ch_isSaveable(aProtocol) {
2118
// We don't do the Right Thing for news/snews yet, so turn them off until we do
2119
return aProtocol && !(aProtocol == "mailto" || aProtocol == "javascript" || aProtocol == "news" || aProtocol == "snews");
2122
_isVoice: function ch_isVoice(aProtocol) {
2123
// Collection of protocols related to voice or data links
2124
return aProtocol && (aProtocol == "tel" || aProtocol == "callto" || aProtocol == "sip" || aProtocol == "voipto");
2127
handleEvent: function ch_handleEvent(aEvent) {
2130
let [elementX, elementY] = Browser.transformClientToBrowser(aEvent.clientX, aEvent.clientY);
2131
this.popupNode = Browser.elementFromPoint(elementX, elementY);
2133
// Do checks for nodes that never have children.
2134
if (this.popupNode.nodeType == Node.ELEMENT_NODE) {
2135
// See if the user clicked on an image.
2136
if (this.popupNode instanceof Ci.nsIImageLoadingContent && this.popupNode.currentURI) {
2137
this.onImage = true;
2138
this.mediaURL = this.popupNode.currentURI.spec;
2140
let request = this.popupNode.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
2141
if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
2142
this.onLoadedImage = true;
2146
let elem = this.popupNode;
2148
if (elem.nodeType == Node.ELEMENT_NODE) {
2151
((elem instanceof HTMLAnchorElement && elem.href) ||
2152
(elem instanceof HTMLAreaElement && elem.href) ||
2153
elem instanceof HTMLLinkElement ||
2154
elem.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
2156
// Target is a link or a descendant of a link.
2157
this.linkURL = this._getLinkURL(elem);
2158
this.linkProtocol = this._getProtocol(this._getURI(this.linkURL));
2160
this.onSaveableLink = this._isSaveable(this.linkProtocol);
2161
this.onVoiceLink = this._isVoice(this.linkProtocol);
2165
elem = elem.parentNode;
2168
let first = last = null;
2595
return this._panel = document.getElementById("context-container");
2600
return this._popup = document.getElementById("context-popup");
2603
showPopup: function ch_showPopup(aMessage) {
2604
this.popupState = aMessage.json;
2605
this.popupState.target = aMessage.target;
2169
2609
let commands = document.getElementById("context-commands");
2170
2610
for (let i=0; i<commands.childElementCount; i++) {
2171
2611
let command = commands.children[i];
2172
let types = command.getAttribute("type").split(/\s+/);
2173
2612
command.removeAttribute("selector");
2174
if (types.indexOf("image") != -1 && this.onImage) {
2175
first = (first ? first : command);
2177
command.hidden = false;
2179
} else if (types.indexOf("image-loaded") != -1 && this.onLoadedImage) {
2180
first = (first ? first : command);
2182
command.hidden = false;
2184
} else if (types.indexOf("link") != -1 && this.onSaveableLink) {
2185
first = (first ? first : command);
2187
command.hidden = false;
2189
} else if (types.indexOf("callto") != -1 && this.onVoiceLink) {
2190
first = (first ? first : command);
2192
command.hidden = false;
2194
} else if (types.indexOf("mailto") != -1 && this.onLink && this.linkProtocol == "mailto") {
2195
first = (first ? first : command);
2197
command.hidden = false;
2200
2613
command.hidden = true;
2615
let types = command.getAttribute("type").split(/\s+/);
2616
for (let i=0; i<types.length; i++) {
2617
if (this.popupState.types.indexOf(types[i]) != -1) {
2618
first = first || command;
2620
command.hidden = false;
2627
this.popupState = null;
2631
// Allow the first and last *non-hidden* elements to be selected in CSS.
2208
2632
first.setAttribute("selector", "first-child");
2209
2633
last.setAttribute("selector", "last-child");
2211
2635
let label = document.getElementById("context-hint");
2213
label.value = this.mediaURL;
2215
label.value = this.linkURL;
2217
let container = document.getElementById("context-popup");
2218
container.hidden = false;
2220
// Make sure the container is at least sized to the content
2221
let preferredHeight = 0;
2222
for (let i=0; i<container.childElementCount; i++) {
2223
preferredHeight += container.children[i].getBoundingClientRect().height;
2226
let rect = container.getBoundingClientRect();
2227
let height = Math.min(preferredHeight, 0.75 * window.innerWidth);
2228
let width = Math.min(rect.width, 0.75 * window.innerWidth);
2230
container.height = height;
2231
container.width = width;
2232
container.top = (window.innerHeight - height) / 2;
2233
container.left = (window.innerWidth - width) / 2;
2235
BrowserUI.pushPopup(this, [container]);
2636
label.value = this.popupState.label || "";
2638
this._panel.hidden = false;
2639
window.addEventListener("resize", this, true);
2640
window.addEventListener("keypress", this, true);
2642
this.sizeToContent();
2643
BrowserUI.pushPopup(this, [this._popup]);
2238
2647
hide: function ch_hide() {
2241
let container = document.getElementById("context-popup");
2242
container.hidden = true;
2244
BrowserUI.popPopup();
2648
if (this._panel.hidden)
2650
this.popupState = null;
2651
this._panel.hidden = true;
2652
window.removeEventListener("resize", this, true);
2653
window.removeEventListener("keypress", this, true);
2655
BrowserUI.popPopup(this);
2658
sizeToContent: function sizeToContent() {
2659
this._popup.maxWidth = window.innerWidth * 0.75;
2662
handleEvent: function handleEvent(aEvent) {
2663
switch (aEvent.type) {
2665
this.sizeToContent();
2668
// Hide the context menu so you can't type behind it.
2669
aEvent.stopPropagation();
2670
aEvent.preventDefault();
2671
if (aEvent.keyCode != aEvent.DOM_VK_ESCAPE)
2248
2678
var ContextCommands = {
2249
openInNewTab: function cc_openInNewTab(aEvent) {
2250
Browser.addTab(ContextHelper.linkURL, false);
2253
saveImage: function cc_saveImage(aEvent) {
2254
let doc = ContextHelper.popupNode.ownerDocument;
2255
saveImageURL(ContextHelper.mediaURL, null, "SaveImageTitle", false, false, doc.documentURIObject);
2259
function removeBookmarksForURI(aURI) {
2260
//XXX blargle xpconnect! might not matter, but a method on
2261
// nsINavBookmarksService that takes an array of items to
2262
// delete would be faster. better yet, a method that takes a URI!
2263
let itemIds = PlacesUtils.getBookmarksForURI(aURI);
2264
itemIds.forEach(PlacesUtils.bookmarks.removeItem);
2266
BrowserUI.updateStar();
2679
copy: function cc_copy() {
2680
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
2681
clipboard.copyString(ContextHelper.popupState.string);
2683
let target = ContextHelper.popupState.target;
2689
selectInput: function cc_selectInput() {
2690
let imePicker = Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker);
2695
paste: function cc_paste() {
2696
let data = ContextHelper.popupState.data;
2697
let target = ContextHelper.popupState.target;
2698
target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
2702
selectAll: function cc_selectAll() {
2703
let target = ContextHelper.popupState.target;
2704
target.editor.selectAll();
2708
openInNewTab: function cc_openInNewTab() {
2709
Browser.addTab(ContextHelper.popupState.linkURL, false, Browser.selectedTab);
2712
saveLink: function cc_saveLink() {
2713
let browser = ContextHelper.popupState.target;
2714
saveURL(ContextHelper.popupState.linkURL, null, "SaveLinkTitle", false, true, browser.documentURI);
2717
saveImage: function cc_saveImage() {
2718
let browser = ContextHelper.popupState.target;
2719
saveImageURL(ContextHelper.popupState.mediaURL, null, "SaveImageTitle", false, true, browser.documentURI);
2722
shareLink: function cc_shareLink() {
2723
let state = ContextHelper.popupState;
2724
SharingUI.show(state.linkURL, state.linkTitle);
2727
shareMedia: function cc_shareMedia() {
2728
SharingUI.show(ContextHelper.popupState.mediaURL, null);
2731
sendCommand: function cc_playVideo(aCommand) {
2732
let browser = ContextHelper.popupState.target;
2733
browser.messageManager.sendAsyncMessage("Browser:ContextCommand", { command: aCommand });
2736
editBookmark: function cc_editBookmark() {
2737
let target = ContextHelper.popupState.target;
2738
target.startEditing();
2741
removeBookmark: function cc_removeBookmark() {
2742
let target = ContextHelper.popupState.target;
2750
show: function show(aURL, aTitle) {
2752
this.showSharingUI(aURL, aTitle);
2754
this.showFallback(aURL, aTitle);
2758
showSharingUI: function showSharingUI(aURL, aTitle) {
2759
let sharingSvc = Cc["@mozilla.org/uriloader/external-sharing-app-service;1"].getService(Ci.nsIExternalSharingAppService);
2760
sharingSvc.shareWithDefault(aURL, "text/plain", aTitle);
2763
showFallback: function showFallback(aURL, aTitle) {
2764
this._dialog = importDialog(window, "chrome://browser/content/share.xul", null);
2765
document.getElementById("share-title").value = aTitle || aURL;
2767
BrowserUI.pushPopup(this, this._dialog);
2769
let bbox = document.getElementById("share-buttons-box");
2770
this._handlers.forEach(function(handler) {
2771
let button = document.createElement("button");
2772
button.className = "prompt-button";
2773
button.setAttribute("label", handler.name);
2774
button.addEventListener("command", function() {
2776
handler.callback(aURL || "", aTitle || "");
2778
bbox.appendChild(button);
2780
this._dialog.waitForClose();
2781
BrowserUI.popPopup(this);
2784
hide: function hide() {
2785
this._dialog.close();
2786
this._dialog = null;
2792
callback: function callback(aURL, aTitle) {
2793
let url = "mailto:?subject=" + encodeURIComponent(aTitle) +
2794
"&body=" + encodeURIComponent(aURL);
2795
let uri = Services.io.newURI(url, null, null);
2796
let extProtocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
2797
.getService(Ci.nsIExternalProtocolService);
2798
extProtocolSvc.loadUrl(uri);
2803
callback: function callback(aURL, aTitle) {
2804
let url = "http://twitter.com/home?status=" + encodeURIComponent((aTitle ? aTitle+": " : "")+aURL);
2805
BrowserUI.newTab(url, Browser.selectedTab);
2809
name: "Google Reader",
2810
callback: function callback(aURL, aTitle) {
2811
let url = "http://www.google.com/reader/link?url=" + encodeURIComponent(aURL) +
2812
"&title=" + encodeURIComponent(aTitle);
2813
BrowserUI.addTab(url, Browser.selectedTab);
2818
callback: function callback(aURL, aTitle) {
2819
let url = "http://www.facebook.com/share.php?u=" + encodeURIComponent(aURL);
2820
BrowserUI.newTab(url, Browser.selectedTab);
2826
var BadgeHandlers = {
2831
url: "https://mail.google.com/mail",
2832
updateBadge: function(aBadge) {
2833
// Use the cache if possible
2834
let now = Date.now();
2835
if (this._lastCount && this._lastUpdate > now - 1000) {
2836
aBadge.set(this._lastCount);
2840
this._lastUpdate = now;
2842
// Use any saved username and password. If we don't have any login and we are not
2843
// currently logged into Gmail, we won't get any count.
2844
let login = BadgeHandlers.getLogin("https://www.google.com");
2846
// Get the feed and read the count, passing any saved username and password
2847
// but do not show any security dialogs if we fail
2848
let req = new XMLHttpRequest();
2849
req.mozBackgroundRequest = true;
2850
req.open("GET", "https://mail.google.com/mail/feed/atom", true, login.username, login.password);
2851
req.onreadystatechange = function(aEvent) {
2852
if (req.readyState == 4) {
2853
if (req.status == 200 && req.responseXML) {
2854
let count = req.responseXML.getElementsByTagName("fullcount");
2855
this._lastCount = count ? count[0].childNodes[0].nodeValue : 0;
2857
this._lastCount = 0;
2859
this._lastCount = BadgeHandlers.setNumberBadge(aBadge, this._lastCount);
2867
register: function(aPopup) {
2868
let handlers = this._handlers;
2869
for (let i = 0; i < handlers.length; i++)
2870
aPopup.registerBadgeHandler(handlers[i].url, handlers[i]);
2873
getLogin: function(aURL) {
2874
let lm = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
2875
let logins = lm.findLogins({}, aURL, aURL, null);
2876
let username = logins.length > 0 ? logins[0].username : "";
2877
let password = logins.length > 0 ? logins[0].password : "";
2878
return { username: username, password: password };
2881
clampBadge: function(aValue) {
2887
setNumberBadge: function(aBadge, aValue) {
2888
if (parseInt(aValue) != 0) {
2889
aValue = this.clampBadge(aValue);
2898
var FullScreenVideo = {
2901
init: function fsv_init() {
2902
messageManager.addMessageListener("Browser:FullScreenVideo:Start", this.show.bind(this));
2903
messageManager.addMessageListener("Browser:FullScreenVideo:Close", this.hide.bind(this));
2906
show: function fsv_show() {
2907
this.createBrowser();
2908
window.fullScreen = true;
2909
BrowserUI.pushPopup(this, this.browser);
2912
hide: function fsv_hide() {
2913
this.destroyBrowser();
2914
window.fullScreen = false;
2915
BrowserUI.popPopup(this);
2918
createBrowser: function fsv_createBrowser() {
2919
let browser = this.browser = document.createElement("browser");
2920
browser.className = "window-width window-height full-screen";
2921
browser.setAttribute("type", "content");
2922
browser.setAttribute("remote", "true");
2923
browser.setAttribute("src", "chrome://browser/content/fullscreen-video.xhtml");
2924
document.getElementById("main-window").appendChild(browser);
2926
let mm = browser.messageManager;
2927
mm.loadFrameScript("chrome://browser/content/fullscreen-video.js", true);
2929
browser.addEventListener("TapDown", this, true);
2930
browser.addEventListener("TapSingle", this, false);
2935
destroyBrowser: function fsv_destroyBrowser() {
2936
let browser = this.browser;
2937
browser.removeEventListener("TapDown", this, false);
2938
browser.removeEventListener("TapSingle", this, false);
2939
browser.parentNode.removeChild(browser);
2940
this.browser = null;
2943
handleEvent: function fsv_handleEvent(aEvent) {
2944
switch (aEvent.type) {
2946
this._dispatchMouseEvent("Browser:MouseDown", aEvent.clientX, aEvent.clientY);
2949
this._dispatchMouseEvent("Browser:MouseUp", aEvent.clientX, aEvent.clientY);
2954
_dispatchMouseEvent: function fsv_dispatchMouseEvent(aName, aX, aY) {
2955
let pos = this.browser.transformClientToBrowser(aX, aY);
2956
this.browser.messageManager.sendAsyncMessage(aName, {
2967
return this.panel = document.getElementById("appmenu");
2970
show: function show() {
2971
if (BrowserUI.activePanel || BrowserUI.isPanelVisible())
2973
this.panel.setAttribute("count", this.panel.childNodes.length);
2974
this.panel.collapsed = false;
2976
addEventListener("keypress", this, true);
2978
BrowserUI.lockToolbar();
2979
BrowserUI.pushPopup(this, [this.panel, Elements.toolbarContainer]);
2982
hide: function hide() {
2983
this.panel.collapsed = true;
2985
removeEventListener("keypress", this, true);
2987
BrowserUI.unlockToolbar();
2988
BrowserUI.popPopup(this);
2991
toggle: function toggle() {
2992
this.panel.collapsed ? this.show() : this.hide();
2995
handleEvent: function handleEvent(aEvent) {