forked from mirrors/gecko-dev
--HG-- rename : browser/components/uitour/ContentUITour.jsm => browser/components/uitour/content-UITour.js
435 lines
13 KiB
JavaScript
435 lines
13 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* 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/. */
|
|
|
|
/* eslint-env mozilla/frame-script */
|
|
/* eslint no-unused-vars: ["error", {args: "none"}] */
|
|
/* global sendAsyncMessage */
|
|
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
ChromeUtils.defineModuleGetter(this, "AutoCompletePopup",
|
|
"resource://gre/modules/AutoCompletePopupContent.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "AutoScrollController",
|
|
"resource://gre/modules/AutoScrollController.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
|
"resource://gre/modules/BrowserUtils.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "SelectContentHelper",
|
|
"resource://gre/modules/SelectContentHelper.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "FindContent",
|
|
"resource://gre/modules/FindContent.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "PrintingContent",
|
|
"resource://gre/modules/PrintingContent.jsm");
|
|
ChromeUtils.defineModuleGetter(this, "RemoteFinder",
|
|
"resource://gre/modules/RemoteFinder.jsm");
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "formFill",
|
|
"@mozilla.org/satchel/form-fill-controller;1",
|
|
"nsIFormFillController");
|
|
|
|
var global = this;
|
|
|
|
XPCOMUtils.defineLazyProxy(this, "PopupBlocking", () => {
|
|
let tmp = {};
|
|
ChromeUtils.import("resource://gre/modules/PopupBlocking.jsm", tmp);
|
|
return new tmp.PopupBlocking(global);
|
|
});
|
|
|
|
XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent",
|
|
"resource://gre/modules/SelectionSourceContent.jsm");
|
|
|
|
XPCOMUtils.defineLazyProxy(this, "WebChannelContent",
|
|
"resource://gre/modules/WebChannelContent.jsm");
|
|
|
|
XPCOMUtils.defineLazyProxy(this, "DateTimePickerContent", () => {
|
|
let tmp = {};
|
|
ChromeUtils.import("resource://gre/modules/DateTimePickerContent.jsm", tmp);
|
|
return new tmp.DateTimePickerContent(this);
|
|
});
|
|
|
|
XPCOMUtils.defineLazyProxy(this, "FindBarChild", () => {
|
|
let tmp = {};
|
|
ChromeUtils.import("resource://gre/modules/FindBarChild.jsm", tmp);
|
|
return new tmp.FindBarChild(this);
|
|
}, {inQuickFind: false, inPassThrough: false});
|
|
|
|
|
|
// Lazily load the finder code
|
|
addMessageListener("Finder:Initialize", function() {
|
|
let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {});
|
|
new RemoteFinderListener(global);
|
|
});
|
|
|
|
var AutoScrollListener = {
|
|
handleEvent(event) {
|
|
if (event.isTrusted &
|
|
!event.defaultPrevented &&
|
|
event.button == 1) {
|
|
if (!this._controller) {
|
|
this._controller = new AutoScrollController(global);
|
|
}
|
|
this._controller.handleEvent(event);
|
|
}
|
|
}
|
|
};
|
|
Services.els.addSystemEventListener(global, "mousedown", AutoScrollListener, true);
|
|
|
|
addEventListener("MozOpenDateTimePicker", DateTimePickerContent);
|
|
|
|
addEventListener("DOMPopupBlocked", PopupBlocking, true);
|
|
|
|
var Printing = {
|
|
MESSAGES: [
|
|
"Printing:Preview:Enter",
|
|
"Printing:Preview:Exit",
|
|
"Printing:Preview:Navigate",
|
|
"Printing:Preview:ParseDocument",
|
|
"Printing:Print",
|
|
],
|
|
|
|
init() {
|
|
this.MESSAGES.forEach(msgName => addMessageListener(msgName, this));
|
|
addEventListener("PrintingError", this, true);
|
|
addEventListener("printPreviewUpdate", this, true);
|
|
this.init = null;
|
|
},
|
|
|
|
handleEvent(event) {
|
|
return PrintingContent.handleEvent(global, event);
|
|
},
|
|
|
|
receiveMessage(message) {
|
|
return PrintingContent.receiveMessage(global, message);
|
|
},
|
|
};
|
|
Printing.init();
|
|
|
|
function SwitchDocumentDirection(aWindow) {
|
|
// document.dir can also be "auto", in which case it won't change
|
|
if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") {
|
|
aWindow.document.dir = "rtl";
|
|
} else if (aWindow.document.dir == "rtl") {
|
|
aWindow.document.dir = "ltr";
|
|
}
|
|
for (let run = 0; run < aWindow.frames.length; run++) {
|
|
SwitchDocumentDirection(aWindow.frames[run]);
|
|
}
|
|
}
|
|
|
|
addMessageListener("SwitchDocumentDirection", () => {
|
|
SwitchDocumentDirection(content.window);
|
|
});
|
|
|
|
var FindBar = {
|
|
/**
|
|
* _findKey and _findModifiers are used to determine whether a keypress
|
|
* is a user attempting to use the find shortcut, after which we'll
|
|
* route keypresses to the parent until we know the findbar has focus
|
|
* there. To do this, we need shortcut data from the parent.
|
|
*/
|
|
_findKey: null,
|
|
|
|
init() {
|
|
Services.els.addSystemEventListener(global, "keypress",
|
|
this.onKeypress.bind(this), false);
|
|
this.init = null;
|
|
},
|
|
|
|
/**
|
|
* Check whether this key event will start the findbar in the parent,
|
|
* in which case we should pass any further key events to the parent to avoid
|
|
* them being lost.
|
|
* @param aEvent the key event to check.
|
|
*/
|
|
eventMatchesFindShortcut(aEvent) {
|
|
if (!this._findKey) {
|
|
this._findKey = Services.cpmm.sharedData.get("Findbar:Shortcut");
|
|
if (!this._findKey) {
|
|
return false;
|
|
}
|
|
}
|
|
for (let k in this._findKey) {
|
|
if (this._findKey[k] != aEvent[k]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
onKeypress(event) {
|
|
if (!FindBarChild.inPassThrough &&
|
|
this.eventMatchesFindShortcut(event)) {
|
|
return FindBarChild.start(event);
|
|
}
|
|
|
|
if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented ||
|
|
!BrowserUtils.canFastFind(content)) {
|
|
return null;
|
|
}
|
|
|
|
if (FindBarChild.inPassThrough || FindBarChild.inQuickFind) {
|
|
return FindBarChild.onKeypress(event);
|
|
}
|
|
|
|
if (event.charCode && BrowserUtils.shouldFastFind(event.target)) {
|
|
let key = String.fromCharCode(event.charCode);
|
|
if ((key == "/" || key == "'") && RemoteFinder._manualFAYT) {
|
|
return FindBarChild.startQuickFind(event);
|
|
}
|
|
if (key != " " && RemoteFinder._findAsYouType) {
|
|
return FindBarChild.startQuickFind(event, true);
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
};
|
|
FindBar.init();
|
|
|
|
addEventListener("WebChannelMessageToChrome", WebChannelContent,
|
|
true, true);
|
|
addMessageListener("WebChannelMessageToContent", WebChannelContent);
|
|
|
|
var AudioPlaybackListener = {
|
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
|
|
|
|
init() {
|
|
Services.obs.addObserver(this, "audio-playback");
|
|
|
|
addMessageListener("AudioPlayback", this);
|
|
addEventListener("unload", () => {
|
|
AudioPlaybackListener.uninit();
|
|
});
|
|
this.init = null;
|
|
},
|
|
|
|
uninit() {
|
|
Services.obs.removeObserver(this, "audio-playback");
|
|
|
|
removeMessageListener("AudioPlayback", this);
|
|
},
|
|
|
|
handleMediaControlMessage(msg) {
|
|
let utils = global.content.windowUtils;
|
|
let suspendTypes = Ci.nsISuspendedTypes;
|
|
switch (msg) {
|
|
case "mute":
|
|
utils.audioMuted = true;
|
|
break;
|
|
case "unmute":
|
|
utils.audioMuted = false;
|
|
break;
|
|
case "lostAudioFocus":
|
|
utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
|
|
break;
|
|
case "lostAudioFocusTransiently":
|
|
utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE;
|
|
break;
|
|
case "gainAudioFocus":
|
|
utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
|
|
break;
|
|
case "mediaControlPaused":
|
|
utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
|
|
break;
|
|
case "mediaControlStopped":
|
|
utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE;
|
|
break;
|
|
case "resumeMedia":
|
|
// User has clicked the tab audio indicator to play a delayed
|
|
// media. That's clear user intent to play, so gesture activate
|
|
// the content document tree so that the block-autoplay logic
|
|
// allows the media to autoplay.
|
|
content.document.notifyUserGestureActivation();
|
|
utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
|
|
break;
|
|
default:
|
|
dump("Error : wrong media control msg!\n");
|
|
break;
|
|
}
|
|
},
|
|
|
|
observe(subject, topic, data) {
|
|
if (topic === "audio-playback") {
|
|
if (subject && subject.top == global.content) {
|
|
let name = "AudioPlayback:";
|
|
if (data === "activeMediaBlockStart") {
|
|
name += "ActiveMediaBlockStart";
|
|
} else if (data === "activeMediaBlockStop") {
|
|
name += "ActiveMediaBlockStop";
|
|
} else {
|
|
name += (data === "active") ? "Start" : "Stop";
|
|
}
|
|
sendAsyncMessage(name);
|
|
}
|
|
}
|
|
},
|
|
|
|
receiveMessage(msg) {
|
|
if (msg.name == "AudioPlayback") {
|
|
this.handleMediaControlMessage(msg.data.type);
|
|
}
|
|
},
|
|
};
|
|
AudioPlaybackListener.init();
|
|
|
|
var UnselectedTabHoverObserver = {
|
|
init() {
|
|
addMessageListener("Browser:UnselectedTabHover", this);
|
|
addEventListener("UnselectedTabHover:Enable", this);
|
|
addEventListener("UnselectedTabHover:Disable", this);
|
|
this.init = null;
|
|
},
|
|
receiveMessage(message) {
|
|
Services.obs.notifyObservers(content.window, "unselected-tab-hover",
|
|
message.data.hovered);
|
|
},
|
|
handleEvent(event) {
|
|
sendAsyncMessage("UnselectedTabHover:Toggle",
|
|
{ enable: event.type == "UnselectedTabHover:Enable" });
|
|
}
|
|
};
|
|
UnselectedTabHoverObserver.init();
|
|
|
|
|
|
var AudibleAutoplayObserver = {
|
|
init() {
|
|
addEventListener("AudibleAutoplayMediaOccurred", this);
|
|
},
|
|
handleEvent(event) {
|
|
sendAsyncMessage("AudibleAutoplayMediaOccurred");
|
|
}
|
|
};
|
|
AudibleAutoplayObserver.init();
|
|
|
|
addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() {
|
|
let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
|
|
if (!sessionHistory) {
|
|
return;
|
|
}
|
|
|
|
// place the entry at current index at the end of the history list, so it won't get removed
|
|
if (sessionHistory.index < sessionHistory.count - 1) {
|
|
let legacy = sessionHistory.legacySHistory;
|
|
legacy.QueryInterface(Ci.nsISHistoryInternal);
|
|
let indexEntry = legacy.getEntryAtIndex(sessionHistory.index, false);
|
|
indexEntry.QueryInterface(Ci.nsISHEntry);
|
|
legacy.addEntry(indexEntry, true);
|
|
}
|
|
|
|
let purge = sessionHistory.count;
|
|
if (global.content.location.href != "about:blank") {
|
|
--purge; // Don't remove the page the user's staring at from shistory
|
|
}
|
|
|
|
if (purge > 0) {
|
|
sessionHistory.legacySHistory.PurgeHistory(purge);
|
|
}
|
|
});
|
|
|
|
addMessageListener("ViewSource:GetSelection", SelectionSourceContent);
|
|
|
|
addEventListener("MozApplicationManifest", function(e) {
|
|
let doc = e.target;
|
|
let info = {
|
|
uri: doc.documentURI,
|
|
characterSet: doc.characterSet,
|
|
manifest: doc.documentElement.getAttribute("manifest"),
|
|
principal: doc.nodePrincipal,
|
|
};
|
|
sendAsyncMessage("MozApplicationManifest", info);
|
|
}, false);
|
|
|
|
let AutoComplete = {
|
|
_connected: false,
|
|
|
|
init() {
|
|
addEventListener("unload", this, {once: true});
|
|
addEventListener("DOMContentLoaded", this, {once: true});
|
|
// WebExtension browserAction is preloaded and does not receive DCL, wait
|
|
// on pageshow so we can hookup the formfill controller.
|
|
addEventListener("pageshow", this, {capture: true, once: true});
|
|
|
|
XPCOMUtils.defineLazyProxy(this, "popup", () => new AutoCompletePopup(global),
|
|
{QueryInterface: null});
|
|
this.init = null;
|
|
},
|
|
|
|
handleEvent(event) {
|
|
switch (event.type) {
|
|
case "DOMContentLoaded":
|
|
case "pageshow":
|
|
// We need to wait for a content viewer to be available
|
|
// before we can attach our AutoCompletePopup handler,
|
|
// since nsFormFillController assumes one will exist
|
|
// when we call attachToBrowser.
|
|
if (!this._connected) {
|
|
formFill.attachToBrowser(docShell, this.popup);
|
|
this._connected = true;
|
|
}
|
|
break;
|
|
|
|
case "unload":
|
|
if (this._connected) {
|
|
formFill.detachFromBrowser(docShell);
|
|
this._connected = false;
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
};
|
|
|
|
AutoComplete.init();
|
|
|
|
addEventListener("mozshowdropdown", event => {
|
|
if (!event.isTrusted)
|
|
return;
|
|
|
|
if (!SelectContentHelper.open) {
|
|
new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this);
|
|
}
|
|
});
|
|
|
|
addEventListener("mozshowdropdown-sourcetouch", event => {
|
|
if (!event.isTrusted)
|
|
return;
|
|
|
|
if (!SelectContentHelper.open) {
|
|
new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this);
|
|
}
|
|
});
|
|
|
|
let ExtFind = {
|
|
init() {
|
|
addMessageListener("ext-Finder:CollectResults", this);
|
|
addMessageListener("ext-Finder:HighlightResults", this);
|
|
addMessageListener("ext-Finder:clearHighlighting", this);
|
|
this.init = null;
|
|
},
|
|
|
|
_findContent: null,
|
|
|
|
async receiveMessage(message) {
|
|
if (!this._findContent) {
|
|
this._findContent = new FindContent(docShell);
|
|
}
|
|
|
|
let data;
|
|
switch (message.name) {
|
|
case "ext-Finder:CollectResults":
|
|
this.finderInited = true;
|
|
data = await this._findContent.findRanges(message.data);
|
|
sendAsyncMessage("ext-Finder:CollectResultsFinished", data);
|
|
break;
|
|
case "ext-Finder:HighlightResults":
|
|
data = this._findContent.highlightResults(message.data);
|
|
sendAsyncMessage("ext-Finder:HighlightResultsFinished", data);
|
|
break;
|
|
case "ext-Finder:clearHighlighting":
|
|
this._findContent.highlighter.highlight(false);
|
|
break;
|
|
}
|
|
},
|
|
};
|
|
|
|
ExtFind.init();
|