зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
45f2e51f0a
|
@ -12,12 +12,9 @@
|
|||
# Generated from ./tools/rewriting/ThirdPartyPaths.txt
|
||||
# awk '{print "^"$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
|
||||
^browser/components/translation/cld2/.*
|
||||
^build/stlport/.*
|
||||
^db/sqlite3/src/.*
|
||||
^dom/media/platforms/ffmpeg/libav.*
|
||||
^extensions/spellcheck/hunspell/src/.*
|
||||
^gfx/2d/convolver.*
|
||||
^gfx/2d/image_operations.*
|
||||
^gfx/angle/.*
|
||||
^gfx/cairo/.*
|
||||
^gfx/graphite2/.*
|
||||
|
|
|
@ -33,8 +33,7 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
|
|||
MOZ_ASSERT(CheckDocTree());
|
||||
|
||||
if (aData.NewTree().IsEmpty()) {
|
||||
NS_ERROR("no children being added");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
return IPC_FAIL(this, "No children being added");
|
||||
}
|
||||
|
||||
ProxyAccessible* parent = GetAccessible(aData.ID());
|
||||
|
@ -158,8 +157,7 @@ DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID,
|
|||
// We shouldn't actually need this because mAccessibles shouldn't have an
|
||||
// entry for the document itself, but it doesn't hurt to be explicit.
|
||||
if (!aRootID) {
|
||||
NS_ERROR("trying to hide entire document?");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
return IPC_FAIL(this, "Trying to hide entire document?");
|
||||
}
|
||||
|
||||
ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
|
||||
|
@ -398,8 +396,7 @@ DocAccessibleParent::RecvRoleChangedEvent(const uint32_t& aRole)
|
|||
}
|
||||
|
||||
if (aRole > roles::LAST_ROLE) {
|
||||
NS_ERROR("child sent bad role in RoleChangedEvent");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
return IPC_FAIL(this, "Child sent bad role in RoleChangedEvent");
|
||||
}
|
||||
|
||||
mRole = static_cast<a11y::role>(aRole);
|
||||
|
|
|
@ -114,6 +114,9 @@ static const uintptr_t IS_PROXY = 1;
|
|||
// the accessible description (help text) of this particular instance.
|
||||
- (NSString*)help;
|
||||
|
||||
// returns the orientation (vertical, horizontal, or undefined)
|
||||
- (NSString*)orientation;
|
||||
|
||||
- (BOOL)isEnabled;
|
||||
|
||||
// information about focus.
|
||||
|
|
|
@ -323,6 +323,9 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
|||
}
|
||||
if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
|
||||
return [self help];
|
||||
if ([attribute isEqualToString:NSAccessibilityOrientationAttribute])
|
||||
return [self orientation];
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityDOMIdentifierAttribute]) {
|
||||
nsAutoString id;
|
||||
if (accWrap)
|
||||
|
@ -1067,6 +1070,28 @@ struct RoleDescrComparator
|
|||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (NSString*)orientation
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
uint64_t state;
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
||||
state = accWrap->InteractiveState();
|
||||
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
||||
state = proxy->State();
|
||||
else
|
||||
state = 0;
|
||||
|
||||
if (state & states::HORIZONTAL)
|
||||
return NSAccessibilityHorizontalOrientationValue;
|
||||
if (state & states::VERTICAL)
|
||||
return NSAccessibilityVerticalOrientationValue;
|
||||
|
||||
return NSAccessibilityUnknownOrientationValue;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
// objc-style description (from NSObject); not to be confused with the accessible description above.
|
||||
- (NSString*)description
|
||||
{
|
||||
|
|
|
@ -43,6 +43,9 @@ with Files("test/newtab/**"):
|
|||
with Files("test/pageinfo/**"):
|
||||
BUG_COMPONENT = ("Firefox", "Page Info Window")
|
||||
|
||||
with Files("test/performance/browser_appmenu_reflows.js"):
|
||||
BUG_COMPONENT = ("Firefox", "Menus")
|
||||
|
||||
with Files("test/permissions/**"):
|
||||
BUG_COMPONENT = ("Firefox", "Preferences")
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ nsContextMenu.prototype = {
|
|||
srcUrl: this.mediaURL,
|
||||
frameUrl: gContextMenuContentData ? gContextMenuContentData.docLocation : undefined,
|
||||
pageUrl: this.browser ? this.browser.currentURI.spec : undefined,
|
||||
linkText: this.linkTextStr,
|
||||
linkUrl: this.linkURL,
|
||||
selectionText: this.isTextSelected ? this.selectionInfo.text : undefined,
|
||||
frameId: this.frameOuterWindowID,
|
||||
|
|
|
@ -102,6 +102,7 @@ support-files =
|
|||
subsuite = clipboard
|
||||
[browser_urlbarUpdateForDomainCompletion.js]
|
||||
[browser_urlbar_autoFill_backspaced.js]
|
||||
[browser_urlbar_canonize_on_autofill.js]
|
||||
[browser_urlbar_blanking.js]
|
||||
support-files =
|
||||
file_blank_but_not_blank.html
|
||||
|
|
|
@ -40,6 +40,41 @@ add_task(async function clickSuggestion() {
|
|||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
async function testPressEnterOnSuggestion(expectedUrl = null, keyModifiers = {}) {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
gURLBar.focus();
|
||||
await promiseAutocompleteResultPopup("foo");
|
||||
let [idx, suggestion, engineName] = await promiseFirstSuggestion();
|
||||
Assert.equal(engineName,
|
||||
"browser_searchSuggestionEngine%20searchSuggestionEngine.xml",
|
||||
"Expected suggestion engine");
|
||||
|
||||
if (!expectedUrl) {
|
||||
expectedUrl = Services.search.currentEngine.getSubmission(suggestion).uri.spec;
|
||||
}
|
||||
|
||||
let promiseLoad = waitForDocLoadAndStopIt(expectedUrl);
|
||||
|
||||
for (let i = 0; i < idx; ++i) {
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
}
|
||||
EventUtils.synthesizeKey("VK_RETURN", keyModifiers);
|
||||
|
||||
await promiseLoad;
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function plainEnterOnSuggestion() {
|
||||
await testPressEnterOnSuggestion();
|
||||
});
|
||||
|
||||
add_task(async function ctrlEnterOnSuggestion() {
|
||||
await testPressEnterOnSuggestion("http://www.foofoo.com/",
|
||||
AppConstants.platform === "macosx" ?
|
||||
{ metaKey: true } :
|
||||
{ ctrlKey: true });
|
||||
});
|
||||
|
||||
function getFirstSuggestion() {
|
||||
let controller = gURLBar.popup.input.controller;
|
||||
let matchCount = controller.matchCount;
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* This test ensures that pressing ctrl/shift+enter bypasses the autoFilled
|
||||
* value, and only considers what the user typed (but not just enter).
|
||||
*/
|
||||
|
||||
async function test_autocomplete(data) {
|
||||
let {desc, typed, autofilled, modified, waitForUrl, keys} = data;
|
||||
info(desc);
|
||||
|
||||
await promiseAutocompleteResultPopup(typed);
|
||||
is(gURLBar.textValue, autofilled, "autofilled value is as expected");
|
||||
|
||||
let promiseLoad = waitForDocLoadAndStopIt(waitForUrl);
|
||||
|
||||
keys.forEach(([key, mods]) => EventUtils.synthesizeKey(key, mods || {}));
|
||||
|
||||
is(gURLBar.textValue, modified, "value is as expected");
|
||||
|
||||
await promiseLoad;
|
||||
gURLBar.blur();
|
||||
}
|
||||
|
||||
add_task(async function() {
|
||||
registerCleanupFunction(async function() {
|
||||
Services.prefs.clearUserPref("browser.urlbar.autoFill");
|
||||
gURLBar.handleRevert();
|
||||
await PlacesTestUtils.clearHistory();
|
||||
});
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill", true);
|
||||
|
||||
// Add a typed visit, so it will be autofilled.
|
||||
await PlacesTestUtils.addVisits({
|
||||
uri: "http://example.com/",
|
||||
transition: Ci.nsINavHistoryService.TRANSITION_TYPED
|
||||
});
|
||||
|
||||
await test_autocomplete({ desc: "CTRL+ENTER on the autofilled part should use autofill",
|
||||
typed: "exam",
|
||||
autofilled: "example.com/",
|
||||
modified: "example.com/",
|
||||
waitForUrl: "http://example.com/",
|
||||
keys: [["VK_RETURN", {}]]
|
||||
});
|
||||
|
||||
await test_autocomplete({ desc: "CTRL+ENTER on the autofilled part should bypass autofill",
|
||||
typed: "exam",
|
||||
autofilled: "example.com/",
|
||||
modified: "www.exam.com",
|
||||
waitForUrl: "http://www.exam.com/",
|
||||
keys: [["VK_RETURN", AppConstants.platform === "macosx" ?
|
||||
{ metaKey: true } :
|
||||
{ ctrlKey: true }]]
|
||||
});
|
||||
|
||||
await test_autocomplete({ desc: "SHIFT+ENTER on the autofilled part should bypass autofill",
|
||||
typed: "exam",
|
||||
autofilled: "example.com/",
|
||||
modified: "www.exam.net",
|
||||
waitForUrl: "http://www.exam.net/",
|
||||
keys: [["VK_RETURN", { shiftKey: true }]]
|
||||
});
|
||||
});
|
|
@ -1317,7 +1317,19 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
|
||||
if (!this._deferredKeyEventQueue.length &&
|
||||
(this.popup.selectedIndex != 0 || this.gotResultForCurrentQuery)) {
|
||||
this.maybeCanonizeURL(event, this.value);
|
||||
let canonizeValue = this.value;
|
||||
if (event.shiftKey || (AppConstants.platform === "macosx" ?
|
||||
event.metaKey :
|
||||
event.ctrlKey)) {
|
||||
let action = this._parseActionUrl(canonizeValue);
|
||||
if (action && "searchSuggestion" in action.params) {
|
||||
canonizeValue = action.params.searchSuggestion;
|
||||
} else if (this.popup.selectedIndex === 0 &&
|
||||
this.mController.getStyleAt(0).includes("autofill")) {
|
||||
canonizeValue = this.handleEnterSearchString;
|
||||
}
|
||||
}
|
||||
this.maybeCanonizeURL(event, canonizeValue);
|
||||
let handled = this.mController.handleEnter(false, event);
|
||||
this.handleEnterSearchString = null;
|
||||
this.popup.overrideValue = null;
|
||||
|
|
|
@ -96,11 +96,11 @@ function getObserver() {
|
|||
onDeleteURI: function(uri, guid, reason) {
|
||||
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
|
||||
},
|
||||
onVisit: function(uri, visitId, time, sessionId, referringId, transitionType, guid, hidden, visitCount, typed) {
|
||||
onVisit: function(uri, visitId, time, sessionId, referringId, transitionType, guid, hidden, visitCount, typed, lastKnownTitle) {
|
||||
let data = {
|
||||
id: guid,
|
||||
url: uri.spec,
|
||||
title: "",
|
||||
title: lastKnownTitle || "",
|
||||
lastVisitTime: time / 1000, // time from Places is microseconds,
|
||||
visitCount,
|
||||
typedCount: typed,
|
||||
|
|
|
@ -529,6 +529,7 @@ MenuItem.prototype = {
|
|||
|
||||
setIfDefined("parentMenuItemId", this.parentId);
|
||||
setIfDefined("mediaType", mediaType);
|
||||
setIfDefined("linkText", contextData.linkText);
|
||||
setIfDefined("linkUrl", contextData.linkUrl);
|
||||
setIfDefined("srcUrl", contextData.srcUrl);
|
||||
setIfDefined("pageUrl", contextData.pageUrl);
|
||||
|
|
|
@ -72,6 +72,11 @@
|
|||
"optional": true,
|
||||
"description": "One of 'image', 'video', or 'audio' if the context menu was activated on one of these types of elements."
|
||||
},
|
||||
"linkText": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "If the element is a link, the text of that link."
|
||||
},
|
||||
"linkUrl": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
|
|
|
@ -73,7 +73,7 @@ add_task(async function() {
|
|||
type: "separator",
|
||||
});
|
||||
|
||||
let contexts = ["page", "selection", "image", "editable", "password"];
|
||||
let contexts = ["page", "link", "selection", "image", "editable", "password"];
|
||||
for (let i = 0; i < contexts.length; i++) {
|
||||
let context = contexts[i];
|
||||
let title = context;
|
||||
|
@ -183,6 +183,30 @@ add_task(async function() {
|
|||
checkClickInfo(result);
|
||||
|
||||
|
||||
// Test "link" context and OnClick data property.
|
||||
extensionMenuRoot = await openExtensionContextMenu("[href=some-link]");
|
||||
|
||||
// Click on ext-link and check the click results
|
||||
items = extensionMenuRoot.getElementsByAttribute("label", "link");
|
||||
is(items.length, 1, "contextMenu item for parent was found (context=link)");
|
||||
let link = items[0];
|
||||
|
||||
expectedClickInfo = {
|
||||
menuItemId: "ext-link",
|
||||
linkUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/some-link",
|
||||
linkText: "Some link",
|
||||
pageUrl: PAGE,
|
||||
editable: false,
|
||||
};
|
||||
|
||||
await closeExtensionContextMenu(link);
|
||||
|
||||
result = await extension.awaitMessage("onclick");
|
||||
checkClickInfo(result);
|
||||
result = await extension.awaitMessage("browser.contextMenus.onClicked");
|
||||
checkClickInfo(result);
|
||||
|
||||
|
||||
// Test "editable" context and OnClick data property.
|
||||
extensionMenuRoot = await openExtensionContextMenu("#edit-me");
|
||||
|
||||
|
|
|
@ -473,7 +473,7 @@ add_task(async function test_on_visited() {
|
|||
equal(onVisited.url, expected.url, "onVisited received the expected url");
|
||||
// Title will be blank until bug 1287928 lands
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1287928
|
||||
equal(onVisited.title, "", "onVisited received a blank title");
|
||||
equal(onVisited.title, expected.title, "onVisited received the expected title");
|
||||
equal(onVisited.lastVisitTime, expected.time, "onVisited received the expected time");
|
||||
equal(onVisited.visitCount, expected.visitCount, "onVisited received the expected visitCount");
|
||||
}
|
||||
|
|
|
@ -43,13 +43,13 @@
|
|||
<div class="radioRestoreContainer">
|
||||
<input class="radioRestoreButton" id="radioRestoreAll" type="radio"
|
||||
name="restore" checked="checked"/>
|
||||
<label class="radioRestoreLabel" for="radioRestoreAll">&welcomeback2.label.restoreAll;</label>
|
||||
<label class="radioRestoreLabel" for="radioRestoreAll">&welcomeback2.restoreAll.label;</label>
|
||||
</div>
|
||||
|
||||
<div class="radioRestoreContainer">
|
||||
<input class="radioRestoreButton" id="radioRestoreChoose" type="radio"
|
||||
name="restore"/>
|
||||
<label class="radioRestoreLabel" for="radioRestoreChoose">&welcomeback2.label.restoreSome;</label>
|
||||
<label class="radioRestoreLabel" for="radioRestoreChoose">&welcomeback2.restoreSome.label;</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.8.480
|
||||
Current extension version is: 1.8.497
|
||||
|
||||
Taken from upstream commit: 2f2e539b
|
||||
Taken from upstream commit: f2fcf2a5
|
||||
|
|
|
@ -1128,7 +1128,8 @@ MessageHandler.prototype = {
|
|||
let startCapability = createPromiseCapability();
|
||||
this.streamControllers[streamId] = {
|
||||
controller,
|
||||
startCall: startCapability
|
||||
startCall: startCapability,
|
||||
isClosed: false
|
||||
};
|
||||
this.postMessage({
|
||||
sourceName,
|
||||
|
@ -1155,6 +1156,7 @@ MessageHandler.prototype = {
|
|||
cancel: reason => {
|
||||
let cancelCapability = createPromiseCapability();
|
||||
this.streamControllers[streamId].cancelCall = cancelCapability;
|
||||
this.streamControllers[streamId].isClosed = true;
|
||||
this.postMessage({
|
||||
sourceName,
|
||||
targetName,
|
||||
|
@ -1284,9 +1286,15 @@ MessageHandler.prototype = {
|
|||
});
|
||||
break;
|
||||
case 'enqueue':
|
||||
if (!this.streamControllers[data.streamId].isClosed) {
|
||||
this.streamControllers[data.streamId].controller.enqueue(data.chunk);
|
||||
}
|
||||
break;
|
||||
case 'close':
|
||||
if (this.streamControllers[data.streamId].isClosed) {
|
||||
break;
|
||||
}
|
||||
this.streamControllers[data.streamId].isClosed = true;
|
||||
this.streamControllers[data.streamId].controller.close();
|
||||
deleteStreamController();
|
||||
break;
|
||||
|
@ -1299,6 +1307,9 @@ MessageHandler.prototype = {
|
|||
deleteStreamController();
|
||||
break;
|
||||
case 'cancel':
|
||||
if (!this.streamSinks[data.streamId]) {
|
||||
break;
|
||||
}
|
||||
resolveCall(this.streamSinks[data.streamId].onCancel, [data.reason]).then(() => {
|
||||
sendStreamResponse({
|
||||
stream: 'cancel_complete',
|
||||
|
@ -2371,6 +2382,7 @@ function getDocument(src, pdfDataRangeTransport, passwordCallback, progressCallb
|
|||
var params = {};
|
||||
var rangeTransport = null;
|
||||
var worker = null;
|
||||
var CMapReaderFactory = _dom_utils.DOMCMapReaderFactory;
|
||||
for (var key in source) {
|
||||
if (key === 'url' && typeof window !== 'undefined') {
|
||||
params[key] = new URL(source[key], window.location).href;
|
||||
|
@ -2393,12 +2405,14 @@ function getDocument(src, pdfDataRangeTransport, passwordCallback, progressCallb
|
|||
(0, _util.error)('Invalid PDF binary data: either typed array, string or ' + 'array-like object is expected in the data property.');
|
||||
}
|
||||
continue;
|
||||
} else if (key === 'CMapReaderFactory') {
|
||||
CMapReaderFactory = source[key];
|
||||
continue;
|
||||
}
|
||||
params[key] = source[key];
|
||||
}
|
||||
params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
|
||||
params.ignoreErrors = params.stopAtErrors !== true;
|
||||
var CMapReaderFactory = params.CMapReaderFactory || _dom_utils.DOMCMapReaderFactory;
|
||||
if (params.disableNativeImageDecoder !== undefined) {
|
||||
(0, _util.deprecated)('parameter disableNativeImageDecoder, ' + 'use nativeImageDecoderSupport instead');
|
||||
}
|
||||
|
@ -2743,12 +2757,40 @@ var PDFPageProxy = function PDFPageProxyClosure() {
|
|||
}
|
||||
return intentState.opListReadCapability.promise;
|
||||
},
|
||||
getTextContent: function PDFPageProxy_getTextContent(params) {
|
||||
params = params || {};
|
||||
return this.transport.messageHandler.sendWithPromise('GetTextContent', {
|
||||
streamTextContent(params = {}) {
|
||||
const TEXT_CONTENT_CHUNK_SIZE = 100;
|
||||
return this.transport.messageHandler.sendWithStream('GetTextContent', {
|
||||
pageIndex: this.pageNumber - 1,
|
||||
normalizeWhitespace: params.normalizeWhitespace === true,
|
||||
combineTextItems: params.disableCombineTextItems !== true
|
||||
}, {
|
||||
highWaterMark: TEXT_CONTENT_CHUNK_SIZE,
|
||||
size(textContent) {
|
||||
return textContent.items.length;
|
||||
}
|
||||
});
|
||||
},
|
||||
getTextContent: function PDFPageProxy_getTextContent(params) {
|
||||
params = params || {};
|
||||
let readableStream = this.streamTextContent(params);
|
||||
return new Promise(function (resolve, reject) {
|
||||
function pump() {
|
||||
reader.read().then(function ({ value, done }) {
|
||||
if (done) {
|
||||
resolve(textContent);
|
||||
return;
|
||||
}
|
||||
_util.Util.extendObj(textContent.styles, value.styles);
|
||||
_util.Util.appendToArray(textContent.items, value.items);
|
||||
pump();
|
||||
}, reject);
|
||||
}
|
||||
let reader = readableStream.getReader();
|
||||
let textContent = {
|
||||
items: [],
|
||||
styles: Object.create(null)
|
||||
};
|
||||
pump();
|
||||
});
|
||||
},
|
||||
_destroy: function PDFPageProxy_destroy() {
|
||||
|
@ -3656,8 +3698,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() {
|
|||
}();
|
||||
var version, build;
|
||||
{
|
||||
exports.version = version = '1.8.480';
|
||||
exports.build = build = '2f2e539b';
|
||||
exports.version = version = '1.8.497';
|
||||
exports.build = build = 'f2fcf2a5';
|
||||
}
|
||||
exports.getDocument = getDocument;
|
||||
exports.LoopbackPort = LoopbackPort;
|
||||
|
@ -3775,6 +3817,9 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
}
|
||||
}
|
||||
task._textDivProperties.set(textDiv, textDivProperties);
|
||||
if (task._textContentStream) {
|
||||
task._layoutText(textDiv);
|
||||
}
|
||||
if (task._enhanceTextSelection) {
|
||||
var angleCos = 1,
|
||||
angleSin = 0;
|
||||
|
@ -3806,7 +3851,6 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
if (task._canceled) {
|
||||
return;
|
||||
}
|
||||
var textLayerFrag = task._container;
|
||||
var textDivs = task._textDivs;
|
||||
var capability = task._capability;
|
||||
var textDivsLength = textDivs.length;
|
||||
|
@ -3815,39 +3859,10 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
capability.resolve();
|
||||
return;
|
||||
}
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.mozOpaque = true;
|
||||
var ctx = canvas.getContext('2d', { alpha: false });
|
||||
var lastFontSize;
|
||||
var lastFontFamily;
|
||||
if (!task._textContentStream) {
|
||||
for (var i = 0; i < textDivsLength; i++) {
|
||||
var textDiv = textDivs[i];
|
||||
var textDivProperties = task._textDivProperties.get(textDiv);
|
||||
if (textDivProperties.isWhitespace) {
|
||||
continue;
|
||||
task._layoutText(textDivs[i]);
|
||||
}
|
||||
var fontSize = textDiv.style.fontSize;
|
||||
var fontFamily = textDiv.style.fontFamily;
|
||||
if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) {
|
||||
ctx.font = fontSize + ' ' + fontFamily;
|
||||
lastFontSize = fontSize;
|
||||
lastFontFamily = fontFamily;
|
||||
}
|
||||
var width = ctx.measureText(textDiv.textContent).width;
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
var transform = '';
|
||||
if (textDivProperties.canvasWidth !== 0 && width > 0) {
|
||||
textDivProperties.scale = textDivProperties.canvasWidth / width;
|
||||
transform = 'scaleX(' + textDivProperties.scale + ')';
|
||||
}
|
||||
if (textDivProperties.angle !== 0) {
|
||||
transform = 'rotate(' + textDivProperties.angle + 'deg) ' + transform;
|
||||
}
|
||||
if (transform !== '') {
|
||||
textDivProperties.originalTransform = transform;
|
||||
_dom_utils.CustomStyle.setProp('transform', textDiv, transform);
|
||||
}
|
||||
task._textDivProperties.set(textDiv, textDivProperties);
|
||||
}
|
||||
task._renderingDone = true;
|
||||
capability.resolve();
|
||||
|
@ -4077,24 +4092,34 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
}
|
||||
});
|
||||
}
|
||||
function TextLayerRenderTask(textContent, container, viewport, textDivs, enhanceTextSelection) {
|
||||
function TextLayerRenderTask({ textContent, textContentStream, container, viewport, textDivs, textContentItemsStr, enhanceTextSelection }) {
|
||||
this._textContent = textContent;
|
||||
this._textContentStream = textContentStream;
|
||||
this._container = container;
|
||||
this._viewport = viewport;
|
||||
this._textDivs = textDivs || [];
|
||||
this._textContentItemsStr = textContentItemsStr || [];
|
||||
this._enhanceTextSelection = !!enhanceTextSelection;
|
||||
this._reader = null;
|
||||
this._layoutTextLastFontSize = null;
|
||||
this._layoutTextLastFontFamily = null;
|
||||
this._layoutTextCtx = null;
|
||||
this._textDivProperties = new WeakMap();
|
||||
this._renderingDone = false;
|
||||
this._canceled = false;
|
||||
this._capability = (0, _util.createPromiseCapability)();
|
||||
this._renderTimer = null;
|
||||
this._bounds = [];
|
||||
this._enhanceTextSelection = !!enhanceTextSelection;
|
||||
}
|
||||
TextLayerRenderTask.prototype = {
|
||||
get promise() {
|
||||
return this._capability.promise;
|
||||
},
|
||||
cancel: function TextLayer_cancel() {
|
||||
if (this._reader) {
|
||||
this._reader.cancel();
|
||||
this._reader = null;
|
||||
}
|
||||
this._canceled = true;
|
||||
if (this._renderTimer !== null) {
|
||||
clearTimeout(this._renderTimer);
|
||||
|
@ -4102,12 +4127,71 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
}
|
||||
this._capability.reject('canceled');
|
||||
},
|
||||
_render: function TextLayer_render(timeout) {
|
||||
var textItems = this._textContent.items;
|
||||
var textStyles = this._textContent.styles;
|
||||
for (var i = 0, len = textItems.length; i < len; i++) {
|
||||
appendText(this, textItems[i], textStyles);
|
||||
_processItems(items, styleCache) {
|
||||
for (let i = 0, len = items.length; i < len; i++) {
|
||||
this._textContentItemsStr.push(items[i].str);
|
||||
appendText(this, items[i], styleCache);
|
||||
}
|
||||
},
|
||||
_layoutText(textDiv) {
|
||||
let textLayerFrag = this._container;
|
||||
let textDivProperties = this._textDivProperties.get(textDiv);
|
||||
if (textDivProperties.isWhitespace) {
|
||||
return;
|
||||
}
|
||||
let fontSize = textDiv.style.fontSize;
|
||||
let fontFamily = textDiv.style.fontFamily;
|
||||
if (fontSize !== this._layoutTextLastFontSize || fontFamily !== this._layoutTextLastFontFamily) {
|
||||
this._layoutTextCtx.font = fontSize + ' ' + fontFamily;
|
||||
this._lastFontSize = fontSize;
|
||||
this._lastFontFamily = fontFamily;
|
||||
}
|
||||
let width = this._layoutTextCtx.measureText(textDiv.textContent).width;
|
||||
let transform = '';
|
||||
if (textDivProperties.canvasWidth !== 0 && width > 0) {
|
||||
textDivProperties.scale = textDivProperties.canvasWidth / width;
|
||||
transform = 'scaleX(' + textDivProperties.scale + ')';
|
||||
}
|
||||
if (textDivProperties.angle !== 0) {
|
||||
transform = 'rotate(' + textDivProperties.angle + 'deg) ' + transform;
|
||||
}
|
||||
if (transform !== '') {
|
||||
textDivProperties.originalTransform = transform;
|
||||
_dom_utils.CustomStyle.setProp('transform', textDiv, transform);
|
||||
}
|
||||
this._textDivProperties.set(textDiv, textDivProperties);
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
},
|
||||
_render: function TextLayer_render(timeout) {
|
||||
let capability = (0, _util.createPromiseCapability)();
|
||||
let styleCache = Object.create(null);
|
||||
let canvas = document.createElement('canvas');
|
||||
canvas.mozOpaque = true;
|
||||
this._layoutTextCtx = canvas.getContext('2d', { alpha: false });
|
||||
if (this._textContent) {
|
||||
let textItems = this._textContent.items;
|
||||
let textStyles = this._textContent.styles;
|
||||
this._processItems(textItems, textStyles);
|
||||
capability.resolve();
|
||||
} else if (this._textContentStream) {
|
||||
let pump = () => {
|
||||
this._reader.read().then(({ value, done }) => {
|
||||
if (done) {
|
||||
capability.resolve();
|
||||
return;
|
||||
}
|
||||
_util.Util.extendObj(styleCache, value.styles);
|
||||
this._processItems(value.items, styleCache);
|
||||
pump();
|
||||
}, capability.reject);
|
||||
};
|
||||
this._reader = this._textContentStream.getReader();
|
||||
pump();
|
||||
} else {
|
||||
throw new Error('Neither "textContent" nor "textContentStream"' + ' parameters specified.');
|
||||
}
|
||||
capability.promise.then(() => {
|
||||
styleCache = null;
|
||||
if (!timeout) {
|
||||
render(this);
|
||||
} else {
|
||||
|
@ -4116,6 +4200,7 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
this._renderTimer = null;
|
||||
}, timeout);
|
||||
}
|
||||
}, this._capability.reject);
|
||||
},
|
||||
expandTextDivs: function TextLayer_expandTextDivs(expandDivs) {
|
||||
if (!this._enhanceTextSelection || !this._renderingDone) {
|
||||
|
@ -4168,7 +4253,15 @@ var renderTextLayer = function renderTextLayerClosure() {
|
|||
}
|
||||
};
|
||||
function renderTextLayer(renderParameters) {
|
||||
var task = new TextLayerRenderTask(renderParameters.textContent, renderParameters.container, renderParameters.viewport, renderParameters.textDivs, renderParameters.enhanceTextSelection);
|
||||
var task = new TextLayerRenderTask({
|
||||
textContent: renderParameters.textContent,
|
||||
textContentStream: renderParameters.textContentStream,
|
||||
container: renderParameters.container,
|
||||
viewport: renderParameters.viewport,
|
||||
textDivs: renderParameters.textDivs,
|
||||
textContentItemsStr: renderParameters.textContentItemsStr,
|
||||
enhanceTextSelection: renderParameters.enhanceTextSelection
|
||||
});
|
||||
task._render(renderParameters.timeout);
|
||||
return task;
|
||||
}
|
||||
|
@ -4659,8 +4752,8 @@ if (!_util.globalScope.PDFJS) {
|
|||
}
|
||||
var PDFJS = _util.globalScope.PDFJS;
|
||||
{
|
||||
PDFJS.version = '1.8.480';
|
||||
PDFJS.build = '2f2e539b';
|
||||
PDFJS.version = '1.8.497';
|
||||
PDFJS.build = 'f2fcf2a5';
|
||||
}
|
||||
PDFJS.pdfBug = false;
|
||||
if (PDFJS.verbosity !== undefined) {
|
||||
|
@ -10007,8 +10100,8 @@ exports.TilingPattern = TilingPattern;
|
|||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '1.8.480';
|
||||
var pdfjsBuild = '2f2e539b';
|
||||
var pdfjsVersion = '1.8.497';
|
||||
var pdfjsBuild = 'f2fcf2a5';
|
||||
var pdfjsSharedUtil = __w_pdfjs_require__(0);
|
||||
var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
|
||||
var pdfjsDisplayAPI = __w_pdfjs_require__(3);
|
||||
|
|
|
@ -1128,7 +1128,8 @@ MessageHandler.prototype = {
|
|||
let startCapability = createPromiseCapability();
|
||||
this.streamControllers[streamId] = {
|
||||
controller,
|
||||
startCall: startCapability
|
||||
startCall: startCapability,
|
||||
isClosed: false
|
||||
};
|
||||
this.postMessage({
|
||||
sourceName,
|
||||
|
@ -1155,6 +1156,7 @@ MessageHandler.prototype = {
|
|||
cancel: reason => {
|
||||
let cancelCapability = createPromiseCapability();
|
||||
this.streamControllers[streamId].cancelCall = cancelCapability;
|
||||
this.streamControllers[streamId].isClosed = true;
|
||||
this.postMessage({
|
||||
sourceName,
|
||||
targetName,
|
||||
|
@ -1284,9 +1286,15 @@ MessageHandler.prototype = {
|
|||
});
|
||||
break;
|
||||
case 'enqueue':
|
||||
if (!this.streamControllers[data.streamId].isClosed) {
|
||||
this.streamControllers[data.streamId].controller.enqueue(data.chunk);
|
||||
}
|
||||
break;
|
||||
case 'close':
|
||||
if (this.streamControllers[data.streamId].isClosed) {
|
||||
break;
|
||||
}
|
||||
this.streamControllers[data.streamId].isClosed = true;
|
||||
this.streamControllers[data.streamId].controller.close();
|
||||
deleteStreamController();
|
||||
break;
|
||||
|
@ -1299,6 +1307,9 @@ MessageHandler.prototype = {
|
|||
deleteStreamController();
|
||||
break;
|
||||
case 'cancel':
|
||||
if (!this.streamSinks[data.streamId]) {
|
||||
break;
|
||||
}
|
||||
resolveCall(this.streamSinks[data.streamId].onCancel, [data.reason]).then(() => {
|
||||
sendStreamResponse({
|
||||
stream: 'cancel_complete',
|
||||
|
@ -17476,7 +17487,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
throw reason;
|
||||
});
|
||||
},
|
||||
getTextContent({ stream, task, resources, stateManager = null, normalizeWhitespace = false, combineTextItems = false }) {
|
||||
getTextContent({ stream, task, resources, stateManager = null, normalizeWhitespace = false, combineTextItems = false, sink, seenStyles = Object.create(null) }) {
|
||||
resources = resources || _primitives.Dict.empty;
|
||||
stateManager = stateManager || new StateManager(new TextState());
|
||||
var WhitespaceRegexp = /\s/g;
|
||||
|
@ -17507,7 +17518,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
var self = this;
|
||||
var xref = this.xref;
|
||||
var xobjs = null;
|
||||
var xobjsCache = Object.create(null);
|
||||
var skipEmptyXObjs = Object.create(null);
|
||||
var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
|
||||
var textState;
|
||||
function ensureTextContentItem() {
|
||||
|
@ -17515,7 +17526,8 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
return textContentItem;
|
||||
}
|
||||
var font = textState.font;
|
||||
if (!(font.loadedName in textContent.styles)) {
|
||||
if (!(font.loadedName in seenStyles)) {
|
||||
seenStyles[font.loadedName] = true;
|
||||
textContent.styles[font.loadedName] = {
|
||||
fontFamily: font.fallbackName,
|
||||
ascent: font.ascent,
|
||||
|
@ -17670,10 +17682,19 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
textContentItem.initialized = false;
|
||||
textContentItem.str.length = 0;
|
||||
}
|
||||
function enqueueChunk() {
|
||||
let length = textContent.items.length;
|
||||
if (length > 0) {
|
||||
sink.enqueue(textContent, length);
|
||||
textContent.items = [];
|
||||
textContent.styles = Object.create(null);
|
||||
}
|
||||
}
|
||||
var timeSlotManager = new TimeSlotManager();
|
||||
return new Promise(function promiseBody(resolve, reject) {
|
||||
var next = function (promise) {
|
||||
promise.then(function () {
|
||||
let next = function (promise) {
|
||||
enqueueChunk();
|
||||
Promise.all([promise, sink.ready]).then(function () {
|
||||
try {
|
||||
promiseBody(resolve, reject);
|
||||
} catch (ex) {
|
||||
|
@ -17828,11 +17849,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
xobjs = resources.get('XObject') || _primitives.Dict.empty;
|
||||
}
|
||||
var name = args[0].name;
|
||||
if (xobjsCache.key === name) {
|
||||
if (xobjsCache.texts) {
|
||||
_util.Util.appendToArray(textContent.items, xobjsCache.texts.items);
|
||||
_util.Util.extendObj(textContent.styles, xobjsCache.texts.styles);
|
||||
}
|
||||
if (name in skipEmptyXObjs) {
|
||||
break;
|
||||
}
|
||||
var xobj = xobjs.get(name);
|
||||
|
@ -17843,8 +17860,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
var type = xobj.dict.get('Subtype');
|
||||
(0, _util.assert)((0, _primitives.isName)(type), 'XObject should have a Name subtype');
|
||||
if (type.name !== 'Form') {
|
||||
xobjsCache.key = name;
|
||||
xobjsCache.texts = null;
|
||||
skipEmptyXObjs[name] = true;
|
||||
break;
|
||||
}
|
||||
var currentState = stateManager.state.clone();
|
||||
|
@ -17853,18 +17869,33 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
if ((0, _util.isArray)(matrix) && matrix.length === 6) {
|
||||
xObjStateManager.transform(matrix);
|
||||
}
|
||||
enqueueChunk();
|
||||
let sinkWrapper = {
|
||||
enqueueInvoked: false,
|
||||
enqueue(chunk, size) {
|
||||
this.enqueueInvoked = true;
|
||||
sink.enqueue(chunk, size);
|
||||
},
|
||||
get desiredSize() {
|
||||
return sink.desiredSize;
|
||||
},
|
||||
get ready() {
|
||||
return sink.ready;
|
||||
}
|
||||
};
|
||||
next(self.getTextContent({
|
||||
stream: xobj,
|
||||
task,
|
||||
resources: xobj.dict.get('Resources') || resources,
|
||||
stateManager: xObjStateManager,
|
||||
normalizeWhitespace,
|
||||
combineTextItems
|
||||
}).then(function (formTextContent) {
|
||||
_util.Util.appendToArray(textContent.items, formTextContent.items);
|
||||
_util.Util.extendObj(textContent.styles, formTextContent.styles);
|
||||
xobjsCache.key = name;
|
||||
xobjsCache.texts = formTextContent;
|
||||
combineTextItems,
|
||||
sink: sinkWrapper,
|
||||
seenStyles
|
||||
}).then(function () {
|
||||
if (!sinkWrapper.enqueueInvoked) {
|
||||
skipEmptyXObjs[name] = true;
|
||||
}
|
||||
}));
|
||||
return;
|
||||
case _util.OPS.setGState:
|
||||
|
@ -17887,18 +17918,24 @@ var PartialEvaluator = function PartialEvaluatorClosure() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (textContent.items.length >= sink.desiredSize) {
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stop) {
|
||||
next(deferred);
|
||||
return;
|
||||
}
|
||||
flushTextContentItem();
|
||||
resolve(textContent);
|
||||
enqueueChunk();
|
||||
resolve();
|
||||
}).catch(reason => {
|
||||
if (this.options.ignoreErrors) {
|
||||
(0, _util.warn)('getTextContent - ignoring errors during task: ' + task.name);
|
||||
flushTextContentItem();
|
||||
return textContent;
|
||||
enqueueChunk();
|
||||
return;
|
||||
}
|
||||
throw reason;
|
||||
});
|
||||
|
@ -24126,27 +24163,31 @@ var WorkerMessageHandler = {
|
|||
});
|
||||
});
|
||||
}, this);
|
||||
handler.on('GetTextContent', function wphExtractText(data) {
|
||||
handler.on('GetTextContent', function wphExtractText(data, sink) {
|
||||
var pageIndex = data.pageIndex;
|
||||
return pdfManager.getPage(pageIndex).then(function (page) {
|
||||
sink.onPull = function (desiredSize) {};
|
||||
sink.onCancel = function (reason) {};
|
||||
pdfManager.getPage(pageIndex).then(function (page) {
|
||||
var task = new WorkerTask('GetTextContent: page ' + pageIndex);
|
||||
startWorkerTask(task);
|
||||
var pageNum = pageIndex + 1;
|
||||
var start = Date.now();
|
||||
return page.extractTextContent({
|
||||
page.extractTextContent({
|
||||
handler,
|
||||
task,
|
||||
sink,
|
||||
normalizeWhitespace: data.normalizeWhitespace,
|
||||
combineTextItems: data.combineTextItems
|
||||
}).then(function (textContent) {
|
||||
}).then(function () {
|
||||
finishWorkerTask(task);
|
||||
(0, _util.info)('text indexing: page=' + pageNum + ' - time=' + (Date.now() - start) + 'ms');
|
||||
return textContent;
|
||||
sink.close();
|
||||
}, function (reason) {
|
||||
finishWorkerTask(task);
|
||||
if (task.terminated) {
|
||||
return;
|
||||
}
|
||||
sink.error(reason);
|
||||
throw reason;
|
||||
});
|
||||
});
|
||||
|
@ -29061,7 +29102,7 @@ var Page = function PageClosure() {
|
|||
});
|
||||
});
|
||||
},
|
||||
extractTextContent({ handler, task, normalizeWhitespace, combineTextItems }) {
|
||||
extractTextContent({ handler, task, normalizeWhitespace, sink, combineTextItems }) {
|
||||
var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream');
|
||||
var resourcesPromise = this.loadResources(['ExtGState', 'XObject', 'Font']);
|
||||
var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
|
||||
|
@ -29081,7 +29122,8 @@ var Page = function PageClosure() {
|
|||
task,
|
||||
resources: this.resources,
|
||||
normalizeWhitespace,
|
||||
combineTextItems
|
||||
combineTextItems,
|
||||
sink
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -30512,24 +30554,24 @@ var Font = function FontClosure() {
|
|||
type = 'Type1';
|
||||
}
|
||||
} else if (isOpenTypeFile(file)) {
|
||||
type = subtype = 'OpenType';
|
||||
subtype = 'OpenType';
|
||||
}
|
||||
}
|
||||
if (subtype === 'CIDFontType0C' && type !== 'CIDFontType0') {
|
||||
type = 'CIDFontType0';
|
||||
}
|
||||
if (subtype === 'OpenType') {
|
||||
type = 'OpenType';
|
||||
}
|
||||
if (type === 'CIDFontType0') {
|
||||
if (isType1File(file)) {
|
||||
subtype = 'CIDFontType0';
|
||||
} else if (isOpenTypeFile(file)) {
|
||||
type = subtype = 'OpenType';
|
||||
subtype = 'OpenType';
|
||||
} else {
|
||||
subtype = 'CIDFontType0C';
|
||||
}
|
||||
}
|
||||
if (subtype === 'OpenType' && type !== 'OpenType') {
|
||||
type = 'OpenType';
|
||||
}
|
||||
var data;
|
||||
switch (type) {
|
||||
case 'MMType1':
|
||||
|
@ -31672,7 +31714,7 @@ var Font = function FontClosure() {
|
|||
}
|
||||
var isTrueType = !tables['CFF '];
|
||||
if (!isTrueType) {
|
||||
if (header.version === 'OTTO' && !properties.composite || !tables['head'] || !tables['hhea'] || !tables['maxp'] || !tables['post']) {
|
||||
if (header.version === 'OTTO' && !(properties.composite && properties.cidToGidMap) || !tables['head'] || !tables['hhea'] || !tables['maxp'] || !tables['post']) {
|
||||
cffFile = new _stream.Stream(tables['CFF '].data);
|
||||
cff = new CFFFont(cffFile, properties);
|
||||
adjustWidths(properties);
|
||||
|
@ -39776,8 +39818,8 @@ exports.Type1Parser = Type1Parser;
|
|||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '1.8.480';
|
||||
var pdfjsBuild = '2f2e539b';
|
||||
var pdfjsVersion = '1.8.497';
|
||||
var pdfjsBuild = 'f2fcf2a5';
|
||||
var pdfjsCoreWorker = __w_pdfjs_require__(17);
|
||||
;
|
||||
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
|
||||
|
|
|
@ -4886,7 +4886,6 @@ var _dom_events = __webpack_require__(2);
|
|||
|
||||
var _pdf_rendering_queue = __webpack_require__(3);
|
||||
|
||||
const TEXT_LAYER_RENDER_DELAY = 200;
|
||||
class PDFPageView {
|
||||
constructor(options) {
|
||||
let container = options.container;
|
||||
|
@ -5190,10 +5189,9 @@ class PDFPageView {
|
|||
let resultPromise = paintTask.promise.then(function () {
|
||||
return finishPaintTask(null).then(function () {
|
||||
if (textLayer) {
|
||||
pdfPage.getTextContent({ normalizeWhitespace: true }).then(function textContentResolved(textContent) {
|
||||
textLayer.setTextContent(textContent);
|
||||
textLayer.render(TEXT_LAYER_RENDER_DELAY);
|
||||
});
|
||||
let readableStream = pdfPage.streamTextContent({ normalizeWhitespace: true });
|
||||
textLayer.setTextContentStream(readableStream);
|
||||
textLayer.render();
|
||||
}
|
||||
});
|
||||
}, function (reason) {
|
||||
|
@ -7392,6 +7390,8 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
this.textLayerDiv = options.textLayerDiv;
|
||||
this.eventBus = options.eventBus || (0, _dom_events.getGlobalEventBus)();
|
||||
this.textContent = null;
|
||||
this.textContentItemsStr = [];
|
||||
this.textContentStream = null;
|
||||
this.renderingDone = false;
|
||||
this.pageIdx = options.pageIndex;
|
||||
this.pageNumber = this.pageIdx + 1;
|
||||
|
@ -7418,7 +7418,7 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
});
|
||||
},
|
||||
render: function TextLayerBuilder_render(timeout) {
|
||||
if (!this.textContent || this.renderingDone) {
|
||||
if (!(this.textContent || this.textContentStream) || this.renderingDone) {
|
||||
return;
|
||||
}
|
||||
this.cancel();
|
||||
|
@ -7426,9 +7426,11 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
var textLayerFrag = document.createDocumentFragment();
|
||||
this.textLayerRenderTask = (0, _pdfjsLib.renderTextLayer)({
|
||||
textContent: this.textContent,
|
||||
textContentStream: this.textContentStream,
|
||||
container: textLayerFrag,
|
||||
viewport: this.viewport,
|
||||
textDivs: this.textDivs,
|
||||
textContentItemsStr: this.textContentItemsStr,
|
||||
timeout,
|
||||
enhanceTextSelection: this.enhanceTextSelection
|
||||
});
|
||||
|
@ -7444,6 +7446,10 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
this.textLayerRenderTask = null;
|
||||
}
|
||||
},
|
||||
setTextContentStream(readableStream) {
|
||||
this.cancel();
|
||||
this.textContentStream = readableStream;
|
||||
},
|
||||
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
|
||||
this.cancel();
|
||||
this.textContent = textContent;
|
||||
|
@ -7451,8 +7457,8 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
convertMatches: function TextLayerBuilder_convertMatches(matches, matchesLength) {
|
||||
var i = 0;
|
||||
var iIndex = 0;
|
||||
var bidiTexts = this.textContent.items;
|
||||
var end = bidiTexts.length - 1;
|
||||
let textContentItemsStr = this.textContentItemsStr;
|
||||
var end = textContentItemsStr.length - 1;
|
||||
var queryLen = this.findController === null ? 0 : this.findController.state.query.length;
|
||||
var ret = [];
|
||||
if (!matches) {
|
||||
|
@ -7460,11 +7466,11 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
}
|
||||
for (var m = 0, len = matches.length; m < len; m++) {
|
||||
var matchIdx = matches[m];
|
||||
while (i !== end && matchIdx >= iIndex + bidiTexts[i].str.length) {
|
||||
iIndex += bidiTexts[i].str.length;
|
||||
while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
|
||||
iIndex += textContentItemsStr[i].length;
|
||||
i++;
|
||||
}
|
||||
if (i === bidiTexts.length) {
|
||||
if (i === textContentItemsStr.length) {
|
||||
console.error('Could not find a matching mapping');
|
||||
}
|
||||
var match = {
|
||||
|
@ -7478,8 +7484,8 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
} else {
|
||||
matchIdx += queryLen;
|
||||
}
|
||||
while (i !== end && matchIdx > iIndex + bidiTexts[i].str.length) {
|
||||
iIndex += bidiTexts[i].str.length;
|
||||
while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
|
||||
iIndex += textContentItemsStr[i].length;
|
||||
i++;
|
||||
}
|
||||
match.end = {
|
||||
|
@ -7494,7 +7500,7 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
if (matches.length === 0) {
|
||||
return;
|
||||
}
|
||||
var bidiTexts = this.textContent.items;
|
||||
let textContentItemsStr = this.textContentItemsStr;
|
||||
var textDivs = this.textDivs;
|
||||
var prevEnd = null;
|
||||
var pageIdx = this.pageIdx;
|
||||
|
@ -7512,7 +7518,7 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
}
|
||||
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
|
||||
var div = textDivs[divIdx];
|
||||
var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
|
||||
var content = textContentItemsStr[divIdx].substring(fromOffset, toOffset);
|
||||
var node = document.createTextNode(content);
|
||||
if (className) {
|
||||
var span = document.createElement('span');
|
||||
|
@ -7569,14 +7575,14 @@ var TextLayerBuilder = function TextLayerBuilderClosure() {
|
|||
}
|
||||
var matches = this.matches;
|
||||
var textDivs = this.textDivs;
|
||||
var bidiTexts = this.textContent.items;
|
||||
let textContentItemsStr = this.textContentItemsStr;
|
||||
var clearedUntilDivIdx = -1;
|
||||
for (var i = 0, len = matches.length; i < len; i++) {
|
||||
var match = matches[i];
|
||||
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
|
||||
for (var n = begin, end = match.end.divIdx; n <= end; n++) {
|
||||
var div = textDivs[n];
|
||||
div.textContent = bidiTexts[n].str;
|
||||
div.textContent = textContentItemsStr[n];
|
||||
div.className = '';
|
||||
}
|
||||
clearedUntilDivIdx = match.end.divIdx + 1;
|
||||
|
@ -7689,9 +7695,8 @@ class Toolbar {
|
|||
this._updateUIState(true);
|
||||
}
|
||||
_bindListeners() {
|
||||
let eventBus = this.eventBus;
|
||||
let { eventBus, items } = this;
|
||||
let self = this;
|
||||
let items = this.items;
|
||||
items.previous.addEventListener('click', function () {
|
||||
eventBus.dispatch('previouspage');
|
||||
});
|
||||
|
@ -7748,31 +7753,9 @@ class Toolbar {
|
|||
if (!this._wasLocalized) {
|
||||
return;
|
||||
}
|
||||
let selectScaleOption = (value, scale) => {
|
||||
let customScale = Math.round(scale * 10000) / 100;
|
||||
this.l10n.get('page_scale_percent', { scale: customScale }, '{{scale}}%').then(msg => {
|
||||
let options = items.scaleSelect.options;
|
||||
let predefinedValueFound = false;
|
||||
for (let i = 0, ii = options.length; i < ii; i++) {
|
||||
let option = options[i];
|
||||
if (option.value !== value) {
|
||||
option.selected = false;
|
||||
continue;
|
||||
}
|
||||
option.selected = true;
|
||||
predefinedValueFound = true;
|
||||
}
|
||||
if (!predefinedValueFound) {
|
||||
items.customScaleOption.textContent = msg;
|
||||
items.customScaleOption.selected = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
let pageNumber = this.pageNumber;
|
||||
let { pageNumber, pagesCount, items } = this;
|
||||
let scaleValue = (this.pageScaleValue || this.pageScale).toString();
|
||||
let scale = this.pageScale;
|
||||
let items = this.items;
|
||||
let pagesCount = this.pagesCount;
|
||||
if (resetNumPages) {
|
||||
if (this.hasPageLabels) {
|
||||
items.pageNumber.type = 'text';
|
||||
|
@ -7799,7 +7782,24 @@ class Toolbar {
|
|||
items.next.disabled = pageNumber >= pagesCount;
|
||||
items.zoomOut.disabled = scale <= _ui_utils.MIN_SCALE;
|
||||
items.zoomIn.disabled = scale >= _ui_utils.MAX_SCALE;
|
||||
selectScaleOption(scaleValue, scale);
|
||||
let customScale = Math.round(scale * 10000) / 100;
|
||||
this.l10n.get('page_scale_percent', { scale: customScale }, '{{scale}}%').then(msg => {
|
||||
let options = items.scaleSelect.options;
|
||||
let predefinedValueFound = false;
|
||||
for (let i = 0, ii = options.length; i < ii; i++) {
|
||||
let option = options[i];
|
||||
if (option.value !== scaleValue) {
|
||||
option.selected = false;
|
||||
continue;
|
||||
}
|
||||
option.selected = true;
|
||||
predefinedValueFound = true;
|
||||
}
|
||||
if (!predefinedValueFound) {
|
||||
items.customScaleOption.textContent = msg;
|
||||
items.customScaleOption.selected = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
updateLoadingIndicatorState(loading = false) {
|
||||
let pageNumberInput = this.items.pageNumber;
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
<!ENTITY welcomeback2.pageTitle "Success!">
|
||||
<!ENTITY welcomeback2.pageInfo1 "&brandShortName; is ready to go.">
|
||||
|
||||
<!ENTITY welcomeback2.label.restoreAll "Restore all Windows and Tabs">
|
||||
<!ENTITY welcomeback2.label.restoreSome "Restore only the ones you want">
|
||||
<!ENTITY welcomeback2.restoreAll.label "Restore all windows & tabs">
|
||||
<!ENTITY welcomeback2.restoreSome.label "Restore only the ones you want">
|
||||
|
||||
|
||||
<!-- LOCALIZATION NOTE (welcomeback2.beforelink.pageInfo2,
|
||||
|
|
|
@ -19,12 +19,6 @@
|
|||
# define MOZ_INCLUDE_MOZALLOC_H_FROM_${HEADER}
|
||||
#endif
|
||||
|
||||
// Code built with !_HAS_EXCEPTIONS calls std::_Throw(), but the win2k
|
||||
// CRT doesn't export std::_Throw(). So we define it.
|
||||
#ifndef mozilla_Throw_h
|
||||
# include "mozilla/throw_msvc.h"
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
// From
|
||||
// http://msdn.microsoft.com/en-us/library/aa985982%28VS.80%29.aspx
|
||||
|
|
|
@ -895,19 +895,14 @@ StyleEditorUI.prototype = {
|
|||
this._jumpToLocation.bind(this, location));
|
||||
|
||||
let cond = this._panelDoc.createElement("div");
|
||||
cond.textContent = rule.conditionText;
|
||||
cond.className = "media-rule-condition";
|
||||
if (!rule.matches) {
|
||||
cond.classList.add("media-condition-unmatched");
|
||||
}
|
||||
if (this._target.tab.tagName == "tab") {
|
||||
const minMaxPattern = /(min\-|max\-)(width|height):\s\d+(px)/ig;
|
||||
const replacement =
|
||||
"<a href='#' class='media-responsive-mode-toggle'>$&</a>";
|
||||
|
||||
cond.innerHTML = cond.textContent.replace(minMaxPattern, replacement);
|
||||
cond.addEventListener("click",
|
||||
this._onMediaConditionClick.bind(this));
|
||||
this._setConditionContents(cond, rule.conditionText);
|
||||
} else {
|
||||
cond.textContent = rule.conditionText;
|
||||
}
|
||||
div.appendChild(cond);
|
||||
|
||||
|
@ -927,6 +922,43 @@ StyleEditorUI.prototype = {
|
|||
}.bind(this)).catch(e => console.error(e));
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to safely inject media query links
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* The element corresponding to the media sidebar condition
|
||||
* @param {String} rawText
|
||||
* The raw condition text to parse
|
||||
*/
|
||||
_setConditionContents(element, rawText) {
|
||||
const minMaxPattern = /(min\-|max\-)(width|height):\s\d+(px)/ig;
|
||||
|
||||
let match = minMaxPattern.exec(rawText);
|
||||
let lastParsed = 0;
|
||||
while (match && match.index != minMaxPattern.lastIndex) {
|
||||
let matchEnd = match.index + match[0].length;
|
||||
let node = this._panelDoc.createTextNode(
|
||||
rawText.substring(lastParsed, match.index)
|
||||
);
|
||||
element.appendChild(node);
|
||||
|
||||
let link = this._panelDoc.createElement("a");
|
||||
link.href = "#";
|
||||
link.className = "media-responsive-mode-toggle";
|
||||
link.textContent = rawText.substring(match.index, matchEnd);
|
||||
link.addEventListener("click", this._onMediaConditionClick.bind(this));
|
||||
element.appendChild(link);
|
||||
|
||||
match = minMaxPattern.exec(rawText);
|
||||
lastParsed = matchEnd;
|
||||
}
|
||||
|
||||
let node = this._panelDoc.createTextNode(
|
||||
rawText.substring(lastParsed, rawText.length)
|
||||
);
|
||||
element.appendChild(node);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a media condition is clicked
|
||||
* If a responsive mode link is clicked, it will launch it.
|
||||
|
@ -935,9 +967,6 @@ StyleEditorUI.prototype = {
|
|||
* Event object
|
||||
*/
|
||||
_onMediaConditionClick: function (e) {
|
||||
if (!e.target.matches(".media-responsive-mode-toggle")) {
|
||||
return;
|
||||
}
|
||||
let conditionText = e.target.textContent;
|
||||
let isWidthCond = conditionText.toLowerCase().indexOf("width") > -1;
|
||||
let mediaVal = parseInt(/\d+/.exec(conditionText), 10);
|
||||
|
|
|
@ -209,6 +209,7 @@
|
|||
#include "nsINode.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsIDOMHTMLDocument.h"
|
||||
|
@ -432,6 +433,7 @@ class nsTextNodeDirectionalityMap
|
|||
|
||||
public:
|
||||
explicit nsTextNodeDirectionalityMap(nsINode* aTextNode)
|
||||
: mElementToBeRemoved(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aTextNode, "Null text node");
|
||||
MOZ_COUNT_CTOR(nsTextNodeDirectionalityMap);
|
||||
|
@ -445,11 +447,28 @@ public:
|
|||
MOZ_COUNT_DTOR(nsTextNodeDirectionalityMap);
|
||||
}
|
||||
|
||||
static void
|
||||
nsTextNodeDirectionalityMapPropertyDestructor(void* aObject,
|
||||
nsIAtom* aProperty,
|
||||
void* aPropertyValue,
|
||||
void* aData)
|
||||
{
|
||||
nsTextNode* textNode =
|
||||
static_cast<nsTextNode*>(aPropertyValue);
|
||||
nsTextNodeDirectionalityMap* map = GetDirectionalityMap(textNode);
|
||||
if (map) {
|
||||
map->RemoveEntryForProperty(static_cast<Element*>(aObject));
|
||||
}
|
||||
NS_RELEASE(textNode);
|
||||
}
|
||||
|
||||
void AddEntry(nsTextNode* aTextNode, Element* aElement)
|
||||
{
|
||||
if (!mElements.Contains(aElement)) {
|
||||
mElements.Put(aElement);
|
||||
aElement->SetProperty(nsGkAtoms::dirAutoSetBy, aTextNode);
|
||||
NS_ADDREF(aTextNode);
|
||||
aElement->SetProperty(nsGkAtoms::dirAutoSetBy, aTextNode,
|
||||
nsTextNodeDirectionalityMapPropertyDestructor);
|
||||
aElement->SetHasDirAutoSet();
|
||||
}
|
||||
}
|
||||
|
@ -461,11 +480,21 @@ public:
|
|||
|
||||
mElements.Remove(aElement);
|
||||
aElement->ClearHasDirAutoSet();
|
||||
aElement->UnsetProperty(nsGkAtoms::dirAutoSetBy);
|
||||
aElement->DeleteProperty(nsGkAtoms::dirAutoSetBy);
|
||||
}
|
||||
|
||||
void RemoveEntryForProperty(Element* aElement)
|
||||
{
|
||||
if (mElementToBeRemoved != aElement) {
|
||||
mElements.Remove(aElement);
|
||||
}
|
||||
aElement->ClearHasDirAutoSet();
|
||||
}
|
||||
|
||||
private:
|
||||
nsCheapSet<nsPtrHashKey<Element>> mElements;
|
||||
// Only used for comparison.
|
||||
Element* mElementToBeRemoved;
|
||||
|
||||
static nsTextNodeDirectionalityMap* GetDirectionalityMap(nsINode* aTextNode)
|
||||
{
|
||||
|
@ -489,18 +518,29 @@ private:
|
|||
return OpNext;
|
||||
}
|
||||
|
||||
struct nsTextNodeDirectionalityMapAndElement
|
||||
{
|
||||
nsTextNodeDirectionalityMap* mMap;
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
};
|
||||
|
||||
static nsCheapSetOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aData)
|
||||
{
|
||||
MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
|
||||
// run the downward propagation algorithm
|
||||
// and remove the text node from the map
|
||||
nsINode* oldTextNode = static_cast<Element*>(aData);
|
||||
nsTextNodeDirectionalityMapAndElement* data =
|
||||
static_cast<nsTextNodeDirectionalityMapAndElement*>(aData);
|
||||
nsINode* oldTextNode = data->mNode;
|
||||
Element* rootNode = aEntry->GetKey();
|
||||
nsTextNode* newTextNode = nullptr;
|
||||
if (rootNode->GetParentNode() && rootNode->HasDirAuto()) {
|
||||
newTextNode = WalkDescendantsSetDirectionFromText(rootNode, true,
|
||||
oldTextNode);
|
||||
}
|
||||
|
||||
AutoRestore<Element*> restore(data->mMap->mElementToBeRemoved);
|
||||
data->mMap->mElementToBeRemoved = rootNode;
|
||||
if (newTextNode) {
|
||||
nsINode* oldDirAutoSetBy =
|
||||
static_cast<nsTextNode*>(rootNode->GetProperty(nsGkAtoms::dirAutoSetBy));
|
||||
|
@ -511,7 +551,7 @@ private:
|
|||
nsTextNodeDirectionalityMap::AddEntryToMap(newTextNode, rootNode);
|
||||
} else {
|
||||
rootNode->ClearHasDirAutoSet();
|
||||
rootNode->UnsetProperty(nsGkAtoms::dirAutoSetBy);
|
||||
rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
|
||||
}
|
||||
return OpRemove;
|
||||
}
|
||||
|
@ -520,7 +560,7 @@ private:
|
|||
{
|
||||
Element* rootNode = aEntry->GetKey();
|
||||
rootNode->ClearHasDirAutoSet();
|
||||
rootNode->UnsetProperty(nsGkAtoms::dirAutoSetBy);
|
||||
rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
|
||||
return OpRemove;
|
||||
}
|
||||
|
||||
|
@ -532,11 +572,13 @@ public:
|
|||
|
||||
void ResetAutoDirection(nsINode* aTextNode)
|
||||
{
|
||||
mElements.EnumerateEntries(ResetNodeDirection, aTextNode);
|
||||
nsTextNodeDirectionalityMapAndElement data = { this, aTextNode };
|
||||
mElements.EnumerateEntries(ResetNodeDirection, &data);
|
||||
}
|
||||
|
||||
void EnsureMapIsClear(nsINode* aTextNode)
|
||||
{
|
||||
AutoRestore<Element*> restore(mElementToBeRemoved);
|
||||
DebugOnly<uint32_t> clearedEntries =
|
||||
mElements.EnumerateEntries(ClearEntry, aTextNode);
|
||||
MOZ_ASSERT(clearedEntries == 0, "Map should be empty already");
|
||||
|
@ -572,7 +614,8 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(aTextNode->HasTextNodeDirectionalityMap(),
|
||||
"Map missing in ResetTextNodeDirection");
|
||||
GetDirectionalityMap(aTextNode)->ResetAutoDirection(aChangedTextNode);
|
||||
RefPtr<nsTextNode> textNode = aTextNode;
|
||||
GetDirectionalityMap(textNode)->ResetAutoDirection(aChangedTextNode);
|
||||
}
|
||||
|
||||
static void EnsureMapIsClearFor(nsINode* aTextNode)
|
||||
|
|
|
@ -3324,6 +3324,7 @@ static nsIAtom** sPropertiesToTraverseAndUnlink[] =
|
|||
{
|
||||
&nsGkAtoms::sandbox,
|
||||
&nsGkAtoms::sizes,
|
||||
&nsGkAtoms::dirAutoSetBy,
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -326,6 +326,7 @@ nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
|
|||
nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
|
||||
nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
|
||||
bool nsContentUtils::sFragmentParsingActive = false;
|
||||
nsISerialEventTarget* nsContentUtils::sStableStateEventTarget = nullptr;
|
||||
|
||||
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
bool nsContentUtils::sDOMWindowDumpEnabled;
|
||||
|
@ -519,6 +520,52 @@ class SameOriginCheckerImpl final : public nsIChannelEventSink,
|
|||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
};
|
||||
|
||||
class StableStateEventTarget final : public nsISerialEventTarget
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
private:
|
||||
~StableStateEventTarget() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(StableStateEventTarget, nsISerialEventTarget);
|
||||
|
||||
bool
|
||||
StableStateEventTarget::IsOnCurrentThreadInfallible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StableStateEventTarget::IsOnCurrentThread(bool* aResult)
|
||||
{
|
||||
*aResult = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StableStateEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
|
||||
{
|
||||
if (NS_WARN_IF(!CycleCollectedJSContext::Get())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsContentUtils::RunInStableState(Move(aEvent));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StableStateEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
|
||||
{
|
||||
return Dispatch(nsCOMPtr<nsIRunnable>(aEvent).forget(), aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StableStateEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelay)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
|
@ -730,6 +777,9 @@ nsContentUtils::Init()
|
|||
|
||||
Unused << nsRFPService::GetOrCreate();
|
||||
|
||||
RefPtr<StableStateEventTarget> stableStateEventTarget = new StableStateEventTarget();
|
||||
stableStateEventTarget.forget(&sStableStateEventTarget);
|
||||
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
|
||||
do_GetService("@mozilla.org/uuid-generator;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -2156,6 +2206,8 @@ nsContentUtils::Shutdown()
|
|||
|
||||
NS_IF_RELEASE(sSameOriginChecker);
|
||||
|
||||
NS_IF_RELEASE(sStableStateEventTarget);
|
||||
|
||||
if (sUserInteractionObserver) {
|
||||
sUserInteractionObserver->Shutdown();
|
||||
NS_RELEASE(sUserInteractionObserver);
|
||||
|
@ -5661,6 +5713,13 @@ nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
|
|||
CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable));
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsISerialEventTarget*
|
||||
nsContentUtils::GetStableStateEventTarget()
|
||||
{
|
||||
return sStableStateEventTarget;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::EnterMicroTask()
|
||||
{
|
||||
|
|
|
@ -1879,6 +1879,16 @@ public:
|
|||
*/
|
||||
static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable);
|
||||
|
||||
/**
|
||||
* Returns a nsISerialEventTarget which will run any event dispatched to it
|
||||
* once the event loop has reached a "stable state". Runnables dispatched to
|
||||
* this event target must not cause any queued events to be processed (i.e.
|
||||
* must not spin the event loop).
|
||||
*
|
||||
* See RunInStableState for more information about stable states
|
||||
*/
|
||||
static nsISerialEventTarget* GetStableStateEventTarget();
|
||||
|
||||
// Call EnterMicroTask when you're entering JS execution.
|
||||
// Usually the best way to do this is to use nsAutoMicroTask.
|
||||
static void EnterMicroTask();
|
||||
|
@ -3227,6 +3237,8 @@ private:
|
|||
*/
|
||||
static bool sFragmentParsingActive;
|
||||
|
||||
static nsISerialEventTarget* sStableStateEventTarget;
|
||||
|
||||
static nsString* sShiftText;
|
||||
static nsString* sControlText;
|
||||
static nsString* sMetaText;
|
||||
|
|
|
@ -36,11 +36,13 @@
|
|||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/layers/KeyboardMap.h"
|
||||
|
||||
#include "nsIClipboardDragDropHooks.h"
|
||||
#include "nsIClipboardDragDropHookList.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
constexpr const char * sSelectAllString = "cmd_selectAll";
|
||||
constexpr const char * sSelectNoneString = "cmd_selectNone";
|
||||
|
@ -271,33 +273,44 @@ IsCaretOnInWindow(nsPIDOMWindowOuter* aWindow, nsISelectionController* aSelCont)
|
|||
|
||||
static constexpr struct BrowseCommand {
|
||||
const char *reverse, *forward;
|
||||
KeyboardScrollAction::KeyboardScrollActionType scrollAction;
|
||||
nsresult (NS_STDCALL nsISelectionController::*scroll)(bool);
|
||||
nsresult (NS_STDCALL nsISelectionController::*move)(bool, bool);
|
||||
} browseCommands[] = {
|
||||
{ sScrollTopString, sScrollBottomString,
|
||||
KeyboardScrollAction::eScrollComplete,
|
||||
&nsISelectionController::CompleteScroll },
|
||||
{ sScrollPageUpString, sScrollPageDownString,
|
||||
KeyboardScrollAction::eScrollPage,
|
||||
&nsISelectionController::ScrollPage },
|
||||
{ sScrollLineUpString, sScrollLineDownString,
|
||||
KeyboardScrollAction::eScrollLine,
|
||||
&nsISelectionController::ScrollLine },
|
||||
{ sScrollLeftString, sScrollRightString,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter },
|
||||
{ sMoveTopString, sMoveBottomString,
|
||||
KeyboardScrollAction::eScrollComplete,
|
||||
&nsISelectionController::CompleteScroll,
|
||||
&nsISelectionController::CompleteMove },
|
||||
{ sMovePageUpString, sMovePageDownString,
|
||||
KeyboardScrollAction::eScrollPage,
|
||||
&nsISelectionController::ScrollPage,
|
||||
&nsISelectionController::PageMove },
|
||||
{ sLinePreviousString, sLineNextString,
|
||||
KeyboardScrollAction::eScrollLine,
|
||||
&nsISelectionController::ScrollLine,
|
||||
&nsISelectionController::LineMove },
|
||||
{ sWordPreviousString, sWordNextString,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter,
|
||||
&nsISelectionController::WordMove },
|
||||
{ sCharPreviousString, sCharNextString,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter,
|
||||
&nsISelectionController::CharacterMove },
|
||||
{ sBeginLineString, sEndLineString,
|
||||
KeyboardScrollAction::eScrollComplete,
|
||||
&nsISelectionController::CompleteScroll,
|
||||
&nsISelectionController::IntraLineMove }
|
||||
};
|
||||
|
@ -334,23 +347,32 @@ nsSelectMoveScrollCommand::DoCommand(const char *aCommandName, nsISupports *aCom
|
|||
static const struct PhysicalBrowseCommand {
|
||||
const char *command;
|
||||
int16_t direction, amount;
|
||||
KeyboardScrollAction::KeyboardScrollActionType scrollAction;
|
||||
nsresult (NS_STDCALL nsISelectionController::*scroll)(bool);
|
||||
} physicalBrowseCommands[] = {
|
||||
{ sMoveLeftString, nsISelectionController::MOVE_LEFT, 0,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter },
|
||||
{ sMoveRightString, nsISelectionController::MOVE_RIGHT, 0,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter },
|
||||
{ sMoveUpString, nsISelectionController::MOVE_UP, 0,
|
||||
KeyboardScrollAction::eScrollLine,
|
||||
&nsISelectionController::ScrollLine },
|
||||
{ sMoveDownString, nsISelectionController::MOVE_DOWN, 0,
|
||||
KeyboardScrollAction::eScrollLine,
|
||||
&nsISelectionController::ScrollLine },
|
||||
{ sMoveLeft2String, nsISelectionController::MOVE_LEFT, 1,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter },
|
||||
{ sMoveRight2String, nsISelectionController::MOVE_RIGHT, 1,
|
||||
KeyboardScrollAction::eScrollCharacter,
|
||||
&nsISelectionController::ScrollCharacter },
|
||||
{ sMoveUp2String, nsISelectionController::MOVE_UP, 1,
|
||||
KeyboardScrollAction::eScrollComplete,
|
||||
&nsISelectionController::CompleteScroll },
|
||||
{ sMoveDown2String, nsISelectionController::MOVE_DOWN, 1,
|
||||
KeyboardScrollAction::eScrollComplete,
|
||||
&nsISelectionController::CompleteScroll },
|
||||
};
|
||||
|
||||
|
@ -1262,3 +1284,36 @@ nsWindowCommandRegistration::RegisterWindowCommands(
|
|||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsGlobalWindowCommands::FindScrollCommand(const char* aCommandName,
|
||||
KeyboardScrollAction* aOutAction)
|
||||
{
|
||||
// Search for a keyboard scroll action to do for this command in browseCommands
|
||||
// and physicalBrowseCommands. Each command exists in only one of them, so the
|
||||
// order we examine browseCommands and physicalBrowseCommands doesn't matter.
|
||||
|
||||
for (size_t i = 0; i < ArrayLength(browseCommands); i++) {
|
||||
const BrowseCommand& cmd = browseCommands[i];
|
||||
bool forward = !strcmp(aCommandName, cmd.forward);
|
||||
bool reverse = !strcmp(aCommandName, cmd.reverse);
|
||||
if (forward || reverse) {
|
||||
*aOutAction = KeyboardScrollAction(cmd.scrollAction, forward);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ArrayLength(physicalBrowseCommands); i++) {
|
||||
const PhysicalBrowseCommand& cmd = physicalBrowseCommands[i];
|
||||
if (!strcmp(aCommandName, cmd.command)) {
|
||||
int16_t dir = cmd.direction;
|
||||
bool forward = (dir == nsISelectionController::MOVE_RIGHT ||
|
||||
dir == nsISelectionController::MOVE_DOWN);
|
||||
|
||||
*aOutAction = KeyboardScrollAction(cmd.scrollAction, forward);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
|
||||
#include "nscore.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
struct KeyboardScrollAction;
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
class nsIControllerCommandTable;
|
||||
|
||||
class nsWindowCommandRegistration
|
||||
|
@ -17,6 +23,22 @@ public:
|
|||
static nsresult RegisterWindowCommands(nsIControllerCommandTable *ccm);
|
||||
};
|
||||
|
||||
class nsGlobalWindowCommands
|
||||
{
|
||||
public:
|
||||
typedef mozilla::layers::KeyboardScrollAction KeyboardScrollAction;
|
||||
|
||||
/**
|
||||
* Search through nsGlobalWindowCommands to find the keyboard scrolling action
|
||||
* that would be done in response to a command.
|
||||
*
|
||||
* @param aCommandName the name of the command
|
||||
* @param aOutAction the result of searching for this command, must not be null
|
||||
* @returns whether a keyboard action was found or not
|
||||
*/
|
||||
static bool FindScrollCommand(const char* aCommandName,
|
||||
KeyboardScrollAction* aOutAction);
|
||||
};
|
||||
|
||||
// XXX find a better home for these
|
||||
#define NS_WINDOWCONTROLLER_CID \
|
||||
|
|
|
@ -41,7 +41,7 @@ class nsWindowRoot;
|
|||
// This may waste space for some other nsWrapperCache-derived objects that have
|
||||
// a 32-bit field as their first member, but those objects are unlikely to be as
|
||||
// numerous or performance-critical as DOM nodes.
|
||||
#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
|
||||
#if defined(_M_X64) || defined(__LP64__)
|
||||
static_assert(sizeof(void*) == 8, "These architectures should be 64-bit");
|
||||
#define BOOL_FLAGS_ON_WRAPPER_CACHE
|
||||
#else
|
||||
|
|
|
@ -97,10 +97,12 @@ FormatForPackingInfo(const PackingInfo& pi)
|
|||
|
||||
case LOCAL_GL_RGB:
|
||||
case LOCAL_GL_RGB_INTEGER:
|
||||
case LOCAL_GL_SRGB:
|
||||
return WebGLTexelFormat::RGB8;
|
||||
|
||||
case LOCAL_GL_RGBA:
|
||||
case LOCAL_GL_RGBA_INTEGER:
|
||||
case LOCAL_GL_SRGB_ALPHA:
|
||||
return WebGLTexelFormat::RGBA8;
|
||||
|
||||
case LOCAL_GL_RG:
|
||||
|
@ -434,11 +436,11 @@ bool
|
|||
TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
|
||||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset, GLenum* const out_error) const
|
||||
GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
|
||||
GLenum* const out_error) const
|
||||
{
|
||||
WebGLContext* webgl = tex->mContext;
|
||||
|
||||
const auto pi = dui->ToPacking();
|
||||
const auto format = FormatForPackingInfo(pi);
|
||||
const auto bytesPerPixel = webgl::BytesPerPixel(pi);
|
||||
|
||||
|
@ -613,7 +615,8 @@ bool
|
|||
TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
|
||||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset, GLenum* const out_error) const
|
||||
GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
|
||||
GLenum* const out_error) const
|
||||
{
|
||||
MOZ_ASSERT_IF(needsRespec, !isSubImage);
|
||||
|
||||
|
@ -739,7 +742,7 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
|||
mSrcAlphaType);
|
||||
|
||||
return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
|
||||
dui, xOffset, yOffset, zOffset, out_error);
|
||||
dui, xOffset, yOffset, zOffset, pi, out_error);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -821,8 +824,8 @@ TexUnpackSurface::Validate(WebGLContext* webgl, const char* funcName,
|
|||
bool
|
||||
TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
|
||||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dstDUI, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset, const webgl::PackingInfo& dstPI,
|
||||
GLenum* const out_error) const
|
||||
{
|
||||
const auto& webgl = tex->mContext;
|
||||
|
@ -832,7 +835,6 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f
|
|||
const auto rowLength = mSurf->GetSize().width;
|
||||
const auto rowCount = mSurf->GetSize().height;
|
||||
|
||||
const auto& dstPI = dstDUI->ToPacking();
|
||||
const auto& dstBPP = webgl::BytesPerPixel(dstPI);
|
||||
const auto dstFormat = FormatForPackingInfo(dstPI);
|
||||
|
||||
|
@ -892,7 +894,7 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f
|
|||
gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
|
||||
}
|
||||
|
||||
*out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dstDUI, xOffset,
|
||||
*out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
|
||||
yOffset, zOffset, mWidth, mHeight, mDepth, dstBegin);
|
||||
|
||||
gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
|
||||
|
|
|
@ -86,7 +86,7 @@ public:
|
|||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset,
|
||||
GLenum* const out_error) const = 0;
|
||||
const webgl::PackingInfo& pi, GLenum* const out_error) const = 0;
|
||||
};
|
||||
|
||||
class TexUnpackBytes final : public TexUnpackBlob
|
||||
|
@ -108,7 +108,7 @@ public:
|
|||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset,
|
||||
GLenum* const out_error) const override;
|
||||
const webgl::PackingInfo& pi, GLenum* const out_error) const override;
|
||||
};
|
||||
|
||||
class TexUnpackImage final : public TexUnpackBlob
|
||||
|
@ -128,7 +128,7 @@ public:
|
|||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset,
|
||||
GLenum* const out_error) const override;
|
||||
const webgl::PackingInfo& dstPI, GLenum* const out_error) const override;
|
||||
};
|
||||
|
||||
class TexUnpackSurface final : public TexUnpackBlob
|
||||
|
@ -146,7 +146,7 @@ public:
|
|||
WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
const webgl::DriverUnpackInfo* dui, GLint xOffset,
|
||||
GLint yOffset, GLint zOffset,
|
||||
GLenum* const out_error) const override;
|
||||
const webgl::PackingInfo& dstPI, GLenum* const out_error) const override;
|
||||
};
|
||||
|
||||
} // namespace webgl
|
||||
|
|
|
@ -1292,7 +1292,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
|
|||
|
||||
GLenum glError;
|
||||
if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, &glError))
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, pi, &glError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1380,7 +1380,7 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
|
|||
|
||||
GLenum glError;
|
||||
if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, &glError))
|
||||
driverUnpackInfo, xOffset, yOffset, zOffset, pi, &glError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -7378,7 +7378,6 @@ skip-if = (os == 'android' || os == 'linux')
|
|||
[generated/test_2_conformance__textures__misc__tex-image-webgl.html]
|
||||
skip-if = (os == 'android' || os == 'linux')
|
||||
[generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
|
||||
fail-if = (os == 'mac')
|
||||
skip-if = (os == 'android' || os == 'linux')
|
||||
[generated/test_2_conformance__textures__misc__tex-image-with-invalid-data.html]
|
||||
skip-if = (os == 'android' || os == 'linux')
|
||||
|
|
|
@ -130,8 +130,6 @@ skip-if = (os == 'mac' && os_version == '10.6')
|
|||
# application crashed [@ mozilla::gl::GLContext::AfterGLCall]
|
||||
skip-if = (os == 'android') || (os == 'win')
|
||||
|
||||
[generated/test_2_conformance__textures__misc__tex-image-with-format-and-type.html]
|
||||
fail-if = (os == 'mac')
|
||||
[generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_conformance__extensions__oes-texture-half-float.html]
|
||||
|
|
|
@ -1767,6 +1767,23 @@ EventListenerManager::TraceListeners(JSTracer* aTrc)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
EventListenerManager::HasUntrustedOrNonSystemGroupKeyEventListeners()
|
||||
{
|
||||
uint32_t count = mListeners.Length();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
Listener* listener = &mListeners.ElementAt(i);
|
||||
if (!listener->mFlags.mInSystemGroup &&
|
||||
listener->mFlags.mAllowUntrustedEvents &&
|
||||
(listener->mTypeAtom == nsGkAtoms::onkeydown ||
|
||||
listener->mTypeAtom == nsGkAtoms::onkeypress ||
|
||||
listener->mTypeAtom == nsGkAtoms::onkeyup)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
EventListenerManager::HasApzAwareListeners()
|
||||
{
|
||||
|
|
|
@ -470,6 +470,8 @@ public:
|
|||
|
||||
dom::EventTarget* GetTarget() { return mTarget; }
|
||||
|
||||
bool HasUntrustedOrNonSystemGroupKeyEventListeners();
|
||||
|
||||
bool HasApzAwareListeners();
|
||||
bool IsApzAwareListener(Listener* aListener);
|
||||
bool IsApzAwareEvent(nsIAtom* aEvent);
|
||||
|
|
|
@ -5287,7 +5287,7 @@ EventStateManager::DoContentCommandScrollEvent(
|
|||
aEvent->mSucceeded = true;
|
||||
|
||||
nsIScrollableFrame* sf =
|
||||
ps->GetFrameToScrollAsScrollable(nsIPresShell::eEither);
|
||||
ps->GetScrollableFrameToScroll(nsIPresShell::eEither);
|
||||
aEvent->mIsEnabled = sf ?
|
||||
(aEvent->mScroll.mIsHorizontal ?
|
||||
WheelHandlingUtils::CanScrollOn(sf, aEvent->mScroll.mAmount, 0) :
|
||||
|
|
|
@ -57,6 +57,13 @@ EventTarget::SetEventHandler(nsIAtom* aType, const nsAString& aTypeString,
|
|||
GetOrCreateListenerManager()->SetEventHandler(aType, aTypeString, aHandler);
|
||||
}
|
||||
|
||||
bool
|
||||
EventTarget::HasUntrustedOrNonSystemGroupKeyEventListeners() const
|
||||
{
|
||||
EventListenerManager* elm = GetExistingListenerManager();
|
||||
return elm && elm->HasUntrustedOrNonSystemGroupKeyEventListeners();
|
||||
}
|
||||
|
||||
bool
|
||||
EventTarget::IsApzAware() const
|
||||
{
|
||||
|
|
|
@ -99,6 +99,10 @@ public:
|
|||
// Called from AsyncEventDispatcher to notify it is running.
|
||||
virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) {}
|
||||
|
||||
// Used by FocusTarget to determine whether this event target has listeners
|
||||
// for untrusted or non system group key events.
|
||||
bool HasUntrustedOrNonSystemGroupKeyEventListeners() const;
|
||||
|
||||
virtual bool IsApzAware() const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -859,6 +859,17 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
|||
uint32_t maxTouchPoints = 0;
|
||||
DimensionInfo dimensionInfo;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
|
||||
if (aParent) {
|
||||
nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow =
|
||||
nsPIDOMWindowOuter::From(aParent)->GetTop();
|
||||
if (parentTopWindow) {
|
||||
parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
|
||||
}
|
||||
}
|
||||
|
||||
// Send down the request to open the window.
|
||||
RefPtr<CreateWindowPromise> windowCreated;
|
||||
if (aIframeMoz) {
|
||||
MOZ_ASSERT(aTabOpener);
|
||||
nsAutoCString url;
|
||||
|
@ -871,10 +882,11 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
|||
url.SetIsVoid(true);
|
||||
}
|
||||
|
||||
// NOTE: BrowserFrameOpenWindowPromise is the same type as
|
||||
// CreateWindowPromise, and this code depends on that fact.
|
||||
windowCreated =
|
||||
newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
|
||||
name, NS_ConvertUTF8toUTF16(features),
|
||||
aWindowIsNew, &textureFactoryIdentifier,
|
||||
&layersId, &compositorOptions, &maxTouchPoints);
|
||||
name, NS_ConvertUTF8toUTF16(features));
|
||||
} else {
|
||||
nsAutoCString baseURIString;
|
||||
float fullZoom;
|
||||
|
@ -883,30 +895,98 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (!SendCreateWindow(aTabOpener, newChild, renderFrame,
|
||||
windowCreated =
|
||||
SendCreateWindow(aTabOpener, newChild, renderFrame,
|
||||
aChromeFlags, aCalledFromJS, aPositionSpecified,
|
||||
aSizeSpecified,
|
||||
features,
|
||||
baseURIString,
|
||||
fullZoom,
|
||||
&rv,
|
||||
aWindowIsNew,
|
||||
&frameScripts,
|
||||
&urlToLoad,
|
||||
&textureFactoryIdentifier,
|
||||
&layersId,
|
||||
&compositorOptions,
|
||||
&maxTouchPoints,
|
||||
&dimensionInfo)) {
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
fullZoom);
|
||||
}
|
||||
|
||||
// Await the promise being resolved. When the promise is resolved, we'll set
|
||||
// the `ready` local variable, which will cause us to exit our nested event
|
||||
// loop.
|
||||
//
|
||||
// NOTE: We need to run this callback on the StableStateEventTarget because we
|
||||
// need to resolve our runnable and exit from the nested event loop before
|
||||
// processing any events which were sent after the reply to CreateWindow was
|
||||
// sent.
|
||||
bool ready = false;
|
||||
windowCreated->Then(nsContentUtils::GetStableStateEventTarget(), __func__,
|
||||
[&] (const CreatedWindowInfo& info) {
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
|
||||
"windowCreated->Then must run on the main thread");
|
||||
rv = info.rv();
|
||||
*aWindowIsNew = info.windowOpened();
|
||||
frameScripts = info.frameScripts();
|
||||
urlToLoad = info.urlToLoad();
|
||||
textureFactoryIdentifier = info.textureFactoryIdentifier();
|
||||
layersId = info.layersId();
|
||||
compositorOptions = info.compositorOptions();
|
||||
maxTouchPoints = info.maxTouchPoints();
|
||||
dimensionInfo = info.dimensions();
|
||||
ready = true;
|
||||
},
|
||||
[&] (const CreateWindowPromise::RejectValueType aReason) {
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
|
||||
"windowCreated->Then must run on the main thread");
|
||||
NS_WARNING("windowCreated promise rejected");
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
ready = true;
|
||||
});
|
||||
|
||||
// =======================
|
||||
// Begin Nested Event Loop
|
||||
// =======================
|
||||
|
||||
// We have to wait for a response from either SendCreateWindow or
|
||||
// SendBrowserFrameOpenWindow with information we're going to need to return
|
||||
// from this function, So we spin a nested event loop until they get back to
|
||||
// us.
|
||||
|
||||
// Prevent the docshell from becoming active while the nested event loop is
|
||||
// spinning.
|
||||
newChild->AddPendingDocShellBlocker();
|
||||
auto removePendingDocShellBlocker = MakeScopeExit([&] {
|
||||
if (newChild) {
|
||||
newChild->RemovePendingDocShellBlocker();
|
||||
}
|
||||
});
|
||||
|
||||
// Suspend our window if we have one to make sure we don't re-enter it.
|
||||
if (parentTopInnerWindow) {
|
||||
parentTopInnerWindow->Suspend();
|
||||
}
|
||||
|
||||
{
|
||||
AutoNoJSAPI nojsapi;
|
||||
|
||||
// Spin the event loop until we get a response. Callers of this function
|
||||
// already have to guard against an inner event loop spinning in the
|
||||
// non-e10s case because of the need to spin one to create a new chrome
|
||||
// window.
|
||||
SpinEventLoopUntil([&] () { return ready; });
|
||||
MOZ_RELEASE_ASSERT(ready,
|
||||
"We are on the main thread, so we should not exit this "
|
||||
"loop without ready being true.");
|
||||
}
|
||||
|
||||
if (parentTopInnerWindow) {
|
||||
parentTopInnerWindow->Resume();
|
||||
}
|
||||
|
||||
// =====================
|
||||
// End Nested Event Loop
|
||||
// =====================
|
||||
|
||||
// Handle the error which we got back from the parent process, if we got
|
||||
// one.
|
||||
if (NS_FAILED(rv)) {
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*aWindowIsNew) {
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
return NS_ERROR_ABORT;
|
||||
|
|
|
@ -1539,19 +1539,18 @@ ContentParent::RemoveFromList()
|
|||
}
|
||||
}
|
||||
} else if (sBrowserContentParents) {
|
||||
nsTArray<ContentParent*>* contentParents =
|
||||
sBrowserContentParents->Get(mRemoteType);
|
||||
if (contentParents) {
|
||||
if (auto entry = sBrowserContentParents->Lookup(mRemoteType)) {
|
||||
nsTArray<ContentParent*>* contentParents = entry.Data();
|
||||
contentParents->RemoveElement(this);
|
||||
if (contentParents->IsEmpty()) {
|
||||
sBrowserContentParents->Remove(mRemoteType);
|
||||
entry.Remove();
|
||||
}
|
||||
}
|
||||
if (sBrowserContentParents->IsEmpty()) {
|
||||
delete sBrowserContentParents;
|
||||
sBrowserContentParents = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sPrivateContent) {
|
||||
sPrivateContent->RemoveElement(this);
|
||||
|
@ -4665,25 +4664,30 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
|||
const nsCString& aFeatures,
|
||||
const nsCString& aBaseURI,
|
||||
const float& aFullZoom,
|
||||
nsresult* aResult,
|
||||
bool* aWindowIsNew,
|
||||
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
|
||||
nsCString* aURLToLoad,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId,
|
||||
CompositorOptions* aCompositorOptions,
|
||||
uint32_t* aMaxTouchPoints,
|
||||
DimensionInfo* aDimensions)
|
||||
CreateWindowResolver&& aResolve)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
CreatedWindowInfo cwi;
|
||||
|
||||
// We always expect to open a new window here. If we don't, it's an error.
|
||||
*aWindowIsNew = true;
|
||||
*aResult = NS_OK;
|
||||
cwi.windowOpened() = true;
|
||||
cwi.layersId() = 0;
|
||||
cwi.maxTouchPoints() = 0;
|
||||
|
||||
// Make sure to resolve the resolver when this function exits, even if we
|
||||
// failed to generate a valid response.
|
||||
auto resolveOnExit = MakeScopeExit([&] {
|
||||
// Copy over the nsresult, and then resolve.
|
||||
cwi.rv() = rv;
|
||||
aResolve(cwi);
|
||||
});
|
||||
|
||||
TabParent* newTab = TabParent::GetFrom(aNewTab);
|
||||
MOZ_ASSERT(newTab);
|
||||
|
||||
auto destroyNewTabOnError = MakeScopeExit([&] {
|
||||
if (!*aWindowIsNew || NS_FAILED(*aResult)) {
|
||||
// We always expect to open a new window here. If we don't, it's an error.
|
||||
if (!cwi.windowOpened() || NS_FAILED(rv)) {
|
||||
if (newTab) {
|
||||
newTab->Destroy();
|
||||
}
|
||||
|
@ -4694,7 +4698,7 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
|||
// we must have an opener.
|
||||
newTab->SetHasContentOpener(true);
|
||||
|
||||
TabParent::AutoUseNewTab aunt(newTab, aURLToLoad);
|
||||
TabParent::AutoUseNewTab aunt(newTab, &cwi.urlToLoad());
|
||||
const uint64_t nextTabParentId = ++sNextTabParentId;
|
||||
sNextTabParents.Put(nextTabParentId, newTab);
|
||||
|
||||
|
@ -4703,35 +4707,35 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
|||
CommonCreateWindow(aThisTab, /* aSetOpener = */ true, aChromeFlags,
|
||||
aCalledFromJS, aPositionSpecified, aSizeSpecified,
|
||||
nullptr, aFeatures, aBaseURI, aFullZoom,
|
||||
nextTabParentId, NullString(), *aResult,
|
||||
newRemoteTab, aWindowIsNew);
|
||||
nextTabParentId, NullString(), rv,
|
||||
newRemoteTab, &cwi.windowOpened());
|
||||
if (!ipcResult) {
|
||||
return ipcResult;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(*aResult))) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
if (sNextTabParents.GetAndRemove(nextTabParentId).valueOr(nullptr)) {
|
||||
*aWindowIsNew = false;
|
||||
cwi.windowOpened() = false;
|
||||
}
|
||||
MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
|
||||
|
||||
newTab->SwapFrameScriptsFrom(*aFrameScripts);
|
||||
newTab->SwapFrameScriptsFrom(cwi.frameScripts());
|
||||
|
||||
RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
|
||||
if (!newTab->SetRenderFrame(rfp) ||
|
||||
!newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
|
||||
*aResult = NS_ERROR_FAILURE;
|
||||
!newTab->GetRenderFrameInfo(&cwi.textureFactoryIdentifier(), &cwi.layersId())) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
*aCompositorOptions = rfp->GetCompositorOptions();
|
||||
cwi.compositorOptions() = rfp->GetCompositorOptions();
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
|
||||
*aMaxTouchPoints = widget ? widget->GetMaxTouchPoints() : 0;
|
||||
|
||||
// NOTE: widget must be set for this to return a meaningful value.
|
||||
*aDimensions = widget ? newTab->GetDimensionInfo() : DimensionInfo();
|
||||
if (widget) {
|
||||
cwi.maxTouchPoints() = widget->GetMaxTouchPoints();
|
||||
cwi.dimensions() = newTab->GetDimensionInfo();
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -5090,11 +5094,7 @@ ContentParent::RecvGetFilesRequest(const nsID& aUUID,
|
|||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvDeleteGetFilesRequest(const nsID& aUUID)
|
||||
{
|
||||
GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
|
||||
if (helper) {
|
||||
mGetFilesPendingRequests.Remove(aUUID);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -5102,9 +5102,8 @@ void
|
|||
ContentParent::SendGetFilesResponseAndForget(const nsID& aUUID,
|
||||
const GetFilesResponseResult& aResult)
|
||||
{
|
||||
GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
|
||||
if (helper) {
|
||||
mGetFilesPendingRequests.Remove(aUUID);
|
||||
if (auto entry = mGetFilesPendingRequests.Lookup(aUUID)) {
|
||||
entry.Remove();
|
||||
Unused << SendGetFilesResponse(aUUID, aResult);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -536,15 +536,7 @@ public:
|
|||
const nsCString& aFeatures,
|
||||
const nsCString& aBaseURI,
|
||||
const float& aFullZoom,
|
||||
nsresult* aResult,
|
||||
bool* aWindowIsNew,
|
||||
InfallibleTArray<FrameScriptInfo>* aFrameScripts,
|
||||
nsCString* aURLToLoad,
|
||||
layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId,
|
||||
mozilla::layers::CompositorOptions* aCompositorOptions,
|
||||
uint32_t* aMaxTouchPoints,
|
||||
DimensionInfo* aDimensions) override;
|
||||
CreateWindowResolver&& aResolve) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvCreateWindowInDifferentProcess(
|
||||
PBrowserParent* aThisTab,
|
||||
|
|
|
@ -22,6 +22,8 @@ using CSSRect from "Units.h";
|
|||
using CSSSize from "Units.h";
|
||||
using mozilla::LayoutDeviceIntPoint from "Units.h";
|
||||
using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
|
||||
using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
|
||||
using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -96,5 +98,27 @@ struct DimensionInfo
|
|||
LayoutDeviceIntPoint chromeDisp;
|
||||
};
|
||||
|
||||
struct FrameScriptInfo
|
||||
{
|
||||
nsString url;
|
||||
bool runInGlobalScope;
|
||||
};
|
||||
|
||||
/**
|
||||
* The information required to complete a window creation request.
|
||||
*/
|
||||
struct CreatedWindowInfo
|
||||
{
|
||||
nsresult rv;
|
||||
bool windowOpened;
|
||||
FrameScriptInfo[] frameScripts;
|
||||
nsCString urlToLoad;
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint64_t layersId;
|
||||
CompositorOptions compositorOptions;
|
||||
uint32_t maxTouchPoints;
|
||||
DimensionInfo dimensions;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -149,11 +149,6 @@ parent:
|
|||
|
||||
async PPaymentRequest();
|
||||
|
||||
/**
|
||||
* Return native data of root widget
|
||||
*/
|
||||
nested(inside_cpow) sync GetWidgetNativeData() returns (WindowsHandle value);
|
||||
|
||||
/**
|
||||
* Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the
|
||||
* widget's shareable window on the chrome side. Only used on Windows.
|
||||
|
@ -459,13 +454,9 @@ parent:
|
|||
*
|
||||
* @param opener the PBrowser whose content called window.open.
|
||||
*/
|
||||
sync BrowserFrameOpenWindow(PBrowser opener, PRenderFrame renderFrame,
|
||||
async BrowserFrameOpenWindow(PBrowser opener, PRenderFrame renderFrame,
|
||||
nsString aURL, nsString aName, nsString aFeatures)
|
||||
returns (bool windowOpened,
|
||||
TextureFactoryIdentifier textureFactoryIdentifier,
|
||||
uint64_t layersId,
|
||||
CompositorOptions compositorOptions,
|
||||
uint32_t maxTouchPoints);
|
||||
returns (CreatedWindowInfo window);
|
||||
|
||||
/**
|
||||
* Tells the containing widget whether the given input block results in a
|
||||
|
@ -898,6 +889,12 @@ child:
|
|||
*/
|
||||
async SetOriginAttributes(OriginAttributes aOriginAttributes);
|
||||
|
||||
/**
|
||||
* Pass the current handle for the current native widget to the content
|
||||
* process, so it can be used by PuppetWidget.
|
||||
*/
|
||||
async SetWidgetNativeData(WindowsHandle aHandle);
|
||||
|
||||
/*
|
||||
* FIXME: write protocol!
|
||||
|
||||
|
|
|
@ -177,14 +177,6 @@ struct DomainPolicyClone
|
|||
URIParams[] superWhitelist;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct FrameScriptInfo
|
||||
{
|
||||
nsString url;
|
||||
bool runInGlobalScope;
|
||||
};
|
||||
|
||||
struct AndroidSystemInfo
|
||||
{
|
||||
nsString device;
|
||||
|
@ -990,7 +982,7 @@ parent:
|
|||
sync GetGraphicsDeviceInitData()
|
||||
returns (ContentDeviceData aData);
|
||||
|
||||
sync CreateWindow(nullable PBrowser aThisTab,
|
||||
async CreateWindow(nullable PBrowser aThisTab,
|
||||
PBrowser aNewTab,
|
||||
PRenderFrame aRenderFrame,
|
||||
uint32_t aChromeFlags,
|
||||
|
@ -1000,15 +992,7 @@ parent:
|
|||
nsCString aFeatures,
|
||||
nsCString aBaseURI,
|
||||
float aFullZoom)
|
||||
returns (nsresult rv,
|
||||
bool windowOpened,
|
||||
FrameScriptInfo[] frameScripts,
|
||||
nsCString urlToLoad,
|
||||
TextureFactoryIdentifier textureFactoryIdentifier,
|
||||
uint64_t layersId,
|
||||
CompositorOptions compositorOptions,
|
||||
uint32_t maxTouchPoints,
|
||||
DimensionInfo dimensions);
|
||||
returns (CreatedWindowInfo window);
|
||||
|
||||
async CreateWindowInDifferentProcess(
|
||||
PBrowser aThisTab,
|
||||
|
|
|
@ -407,6 +407,11 @@ TabChild::TabChild(nsIContentChild* aManager,
|
|||
#if defined(ACCESSIBILITY)
|
||||
, mTopLevelDocAccessibleChild(nullptr)
|
||||
#endif
|
||||
, mPendingDocShellIsActive(false)
|
||||
, mPendingDocShellPreserveLayers(false)
|
||||
, mPendingDocShellReceivedMessage(false)
|
||||
, mPendingDocShellBlockers(0)
|
||||
, mWidgetNativeData(0)
|
||||
{
|
||||
nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this))); // for capture by the lambda
|
||||
mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId,
|
||||
|
@ -2362,20 +2367,26 @@ TabChild::RecvDestroy()
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
|
||||
const bool& aPreserveLayers,
|
||||
const uint64_t& aLayerObserverEpoch)
|
||||
void
|
||||
TabChild::AddPendingDocShellBlocker()
|
||||
{
|
||||
// Since SetDocShellIsActive requests come in from both the hang monitor
|
||||
// channel and the PContent channel, we have an ordering problem. This code
|
||||
// ensures that we respect the order in which the requests were made and
|
||||
// ignore stale requests.
|
||||
if (mLayerObserverEpoch >= aLayerObserverEpoch) {
|
||||
return IPC_OK();
|
||||
mPendingDocShellBlockers++;
|
||||
}
|
||||
mLayerObserverEpoch = aLayerObserverEpoch;
|
||||
|
||||
void
|
||||
TabChild::RemovePendingDocShellBlocker()
|
||||
{
|
||||
mPendingDocShellBlockers--;
|
||||
if (!mPendingDocShellBlockers && mPendingDocShellReceivedMessage) {
|
||||
mPendingDocShellReceivedMessage = false;
|
||||
InternalSetDocShellIsActive(mPendingDocShellIsActive,
|
||||
mPendingDocShellPreserveLayers);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::InternalSetDocShellIsActive(bool aIsActive, bool aPreserveLayers)
|
||||
{
|
||||
auto clearForcePaint = MakeScopeExit([&] {
|
||||
// We might force a paint, or we might already have painted and this is a
|
||||
// no-op. In either case, once we exit this scope, we need to alert the
|
||||
|
@ -2401,7 +2412,7 @@ TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
|
|||
// We send the current layer observer epoch to the compositor so that
|
||||
// TabParent knows whether a layer update notification corresponds to the
|
||||
// latest SetDocShellIsActive request that was made.
|
||||
mPuppetWidget->GetLayerManager()->SetLayerObserverEpoch(aLayerObserverEpoch);
|
||||
mPuppetWidget->GetLayerManager()->SetLayerObserverEpoch(mLayerObserverEpoch);
|
||||
}
|
||||
|
||||
// docshell is consider prerendered only if not active yet
|
||||
|
@ -2415,8 +2426,8 @@ TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
|
|||
// notification to fire in the parent (so that it knows that the child has
|
||||
// updated its epoch). ForcePaintNoOp does that.
|
||||
if (IPCOpen()) {
|
||||
Unused << SendForcePaintNoOp(aLayerObserverEpoch);
|
||||
return IPC_OK();
|
||||
Unused << SendForcePaintNoOp(mLayerObserverEpoch);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2457,7 +2468,33 @@ TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
|
|||
} else if (!aPreserveLayers) {
|
||||
MakeHidden();
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
|
||||
const bool& aPreserveLayers,
|
||||
const uint64_t& aLayerObserverEpoch)
|
||||
{
|
||||
// Since requests to change the active docshell come in from both the hang
|
||||
// monitor channel and the PContent channel, we have an ordering problem. This
|
||||
// code ensures that we respect the order in which the requests were made and
|
||||
// ignore stale requests.
|
||||
if (mLayerObserverEpoch >= aLayerObserverEpoch) {
|
||||
return IPC_OK();
|
||||
}
|
||||
mLayerObserverEpoch = aLayerObserverEpoch;
|
||||
|
||||
// If we're currently waiting for window opening to complete, we need to hold
|
||||
// off on setting the docshell active. We queue up the values we're receiving
|
||||
// in the mWindowOpenDocShellActiveStatus.
|
||||
if (mPendingDocShellBlockers > 0) {
|
||||
mPendingDocShellReceivedMessage = true;
|
||||
mPendingDocShellIsActive = aIsActive;
|
||||
mPendingDocShellPreserveLayers = aPreserveLayers;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
InternalSetDocShellIsActive(aIsActive, aPreserveLayers);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -3175,6 +3212,13 @@ TabChild::RecvSetOriginAttributes(const OriginAttributes& aOriginAttributes)
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabChild::RecvSetWidgetNativeData(const WindowsHandle& aWidgetNativeData)
|
||||
{
|
||||
mWidgetNativeData = aWidgetNativeData;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::plugins::PPluginWidgetChild*
|
||||
TabChild::AllocPPluginWidgetChild()
|
||||
{
|
||||
|
|
|
@ -694,6 +694,15 @@ public:
|
|||
}
|
||||
#endif
|
||||
|
||||
void AddPendingDocShellBlocker();
|
||||
void RemovePendingDocShellBlocker();
|
||||
|
||||
// The HANDLE object for the widget this TabChild in.
|
||||
WindowsHandle WidgetNativeData()
|
||||
{
|
||||
return mWidgetNativeData;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~TabChild();
|
||||
|
||||
|
@ -735,6 +744,8 @@ protected:
|
|||
|
||||
virtual mozilla::ipc::IPCResult RecvSetOriginAttributes(const OriginAttributes& aOriginAttributes) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetWidgetNativeData(const WindowsHandle& aWidgetNativeData) override;
|
||||
|
||||
private:
|
||||
void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
|
||||
const ScrollableLayerGuid& aGuid);
|
||||
|
@ -790,6 +801,9 @@ private:
|
|||
const ScrollableLayerGuid& aGuid,
|
||||
const uint64_t& aInputBlockId);
|
||||
|
||||
void InternalSetDocShellIsActive(bool aIsActive,
|
||||
bool aPreserveLayers);
|
||||
|
||||
class DelayedDeleteRunnable;
|
||||
|
||||
TextureFactoryIdentifier mTextureFactoryIdentifier;
|
||||
|
@ -870,6 +884,13 @@ private:
|
|||
PDocAccessibleChild* mTopLevelDocAccessibleChild;
|
||||
#endif
|
||||
|
||||
bool mPendingDocShellIsActive;
|
||||
bool mPendingDocShellPreserveLayers;
|
||||
bool mPendingDocShellReceivedMessage;
|
||||
uint32_t mPendingDocShellBlockers;
|
||||
|
||||
WindowsHandle mWidgetNativeData;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
|
||||
};
|
||||
|
||||
|
|
|
@ -292,6 +292,17 @@ TabParent::SetOwnerElement(Element* aElement)
|
|||
|
||||
AddWindowListeners();
|
||||
TryCacheDPIAndScale();
|
||||
|
||||
// Try to send down WidgetNativeData, now that this TabParent is associated
|
||||
// with a widget.
|
||||
nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
|
||||
if (widget) {
|
||||
WindowsHandle widgetNativeData = reinterpret_cast<WindowsHandle>(
|
||||
widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW));
|
||||
if (widgetNativeData) {
|
||||
Unused << SendSetWidgetNativeData(widgetNativeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2315,18 +2326,6 @@ TabParent::GetTopLevelWidget()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvGetWidgetNativeData(WindowsHandle* aValue)
|
||||
{
|
||||
*aValue = 0;
|
||||
nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
|
||||
if (widget) {
|
||||
*aValue = reinterpret_cast<WindowsHandle>(
|
||||
widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW));
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvSetNativeChildOfShareableWindow(const uintptr_t& aChildWindow)
|
||||
{
|
||||
|
@ -2580,21 +2579,31 @@ TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
|
|||
const nsString& aURL,
|
||||
const nsString& aName,
|
||||
const nsString& aFeatures,
|
||||
bool* aOutWindowOpened,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId,
|
||||
CompositorOptions* aCompositorOptions,
|
||||
uint32_t* aMaxTouchPoints)
|
||||
BrowserFrameOpenWindowResolver&& aResolve)
|
||||
{
|
||||
CreatedWindowInfo cwi;
|
||||
cwi.rv() = NS_OK;
|
||||
cwi.layersId() = 0;
|
||||
cwi.maxTouchPoints() = 0;
|
||||
|
||||
BrowserElementParent::OpenWindowResult opened =
|
||||
BrowserElementParent::OpenWindowOOP(TabParent::GetFrom(aOpener),
|
||||
this, aRenderFrame, aURL, aName, aFeatures,
|
||||
aTextureFactoryIdentifier, aLayersId);
|
||||
*aCompositorOptions = static_cast<RenderFrameParent*>(aRenderFrame)->GetCompositorOptions();
|
||||
*aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
|
||||
&cwi.textureFactoryIdentifier(),
|
||||
&cwi.layersId());
|
||||
cwi.compositorOptions() =
|
||||
static_cast<RenderFrameParent*>(aRenderFrame)->GetCompositorOptions();
|
||||
cwi.windowOpened() = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
*aMaxTouchPoints = widget ? widget->GetMaxTouchPoints() : 0;
|
||||
if (!*aOutWindowOpened) {
|
||||
if (widget) {
|
||||
cwi.maxTouchPoints() = widget->GetMaxTouchPoints();
|
||||
cwi.dimensions() = GetDimensionInfo();
|
||||
}
|
||||
|
||||
// Resolve the request with the information we collected.
|
||||
aResolve(cwi);
|
||||
|
||||
if (!cwi.windowOpened()) {
|
||||
Destroy();
|
||||
}
|
||||
return IPC_OK();
|
||||
|
|
|
@ -165,16 +165,13 @@ public:
|
|||
virtual mozilla::ipc::IPCResult
|
||||
RecvSetHasBeforeUnload(const bool& aHasBeforeUnload) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
|
||||
PRenderFrameParent* aRenderFrame,
|
||||
const nsString& aURL,
|
||||
const nsString& aName,
|
||||
const nsString& aFeatures,
|
||||
bool* aOutWindowOpened,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId,
|
||||
CompositorOptions* aCompositorOptions,
|
||||
uint32_t* aMaxTouchPoints) override;
|
||||
BrowserFrameOpenWindowResolver&& aResolve) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvSyncMessage(const nsString& aMessage,
|
||||
|
@ -308,8 +305,6 @@ public:
|
|||
|
||||
virtual mozilla::ipc::IPCResult RecvGetWidgetRounding(int32_t* aValue) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetWidgetNativeData(WindowsHandle* aValue) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvDispatchFocusToTopLevelWindow() override;
|
||||
|
|
|
@ -12,18 +12,18 @@ support-files =
|
|||
[test_has_permissions.html]
|
||||
[test_permissions.html]
|
||||
[test_register.html]
|
||||
skip-if = os == "win" && !debug # Bug 1373346
|
||||
skip-if = os == "win" # Bug 1373346
|
||||
[test_register_key.html]
|
||||
[test_multiple_register.html]
|
||||
[test_multiple_register_during_service_activation.html]
|
||||
[test_unregister.html]
|
||||
[test_multiple_register_different_scope.html]
|
||||
[test_subscription_change.html]
|
||||
skip-if = os == "win" && !debug # Bug 1373346
|
||||
skip-if = os == "win" # Bug 1373346
|
||||
[test_data.html]
|
||||
skip-if = os == "win" && !debug # Bug 1373346
|
||||
skip-if = os == "win" # Bug 1373346
|
||||
[test_try_registering_offline_disabled.html]
|
||||
skip-if = os == "win" && !debug # Bug 1373346
|
||||
skip-if = os == "win" # Bug 1373346
|
||||
[test_serviceworker_lifetime.html]
|
||||
skip-if = os == "win" && !debug # Bug 1373346
|
||||
skip-if = os == "win" # Bug 1373346
|
||||
[test_error_reporting.html]
|
||||
|
|
|
@ -13,6 +13,7 @@ EXPORTS += [
|
|||
'nsBindingManager.h',
|
||||
'nsXBLBinding.h',
|
||||
'nsXBLService.h',
|
||||
'nsXBLWindowKeyHandler.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
|
|
|
@ -17,9 +17,7 @@ class nsIDOMKeyEvent;
|
|||
class nsXBLPrototypeHandler;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct IgnoreModifierState;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsXBLEventHandler : public nsIDOMEventListener
|
||||
|
@ -55,7 +53,7 @@ private:
|
|||
|
||||
class nsXBLKeyEventHandler : public nsIDOMEventListener
|
||||
{
|
||||
typedef mozilla::dom::IgnoreModifierState IgnoreModifierState;
|
||||
typedef mozilla::IgnoreModifierState IgnoreModifierState;
|
||||
|
||||
public:
|
||||
nsXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase, uint8_t aType);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsGlobalWindowCommands.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIDOMKeyEvent.h"
|
||||
|
@ -46,13 +47,16 @@
|
|||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/JSEventHandler.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/EventHandlerBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/layers/KeyboardMap.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
uint32_t nsXBLPrototypeHandler::gRefCnt = 0;
|
||||
|
||||
|
@ -136,6 +140,66 @@ nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
|
|||
NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler);
|
||||
}
|
||||
|
||||
bool
|
||||
nsXBLPrototypeHandler::TryConvertToKeyboardShortcut(
|
||||
KeyboardShortcut* aOut) const
|
||||
{
|
||||
// Convert the event type
|
||||
KeyboardInput::KeyboardEventType eventType;
|
||||
|
||||
if (mEventName == nsGkAtoms::keydown) {
|
||||
eventType = KeyboardInput::KEY_DOWN;
|
||||
} else if (mEventName == nsGkAtoms::keypress) {
|
||||
eventType = KeyboardInput::KEY_PRESS;
|
||||
} else if (mEventName == nsGkAtoms::keyup) {
|
||||
eventType = KeyboardInput::KEY_UP;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the modifiers
|
||||
Modifiers modifiersMask = GetModifiers();
|
||||
Modifiers modifiers = GetModifiersMask();
|
||||
|
||||
// Mask away any bits that won't be compared
|
||||
modifiers &= modifiersMask;
|
||||
|
||||
// Convert the keyCode or charCode
|
||||
uint32_t keyCode;
|
||||
uint32_t charCode;
|
||||
|
||||
if (mMisc) {
|
||||
keyCode = 0;
|
||||
charCode = static_cast<uint32_t>(mDetail);
|
||||
} else {
|
||||
keyCode = static_cast<uint32_t>(mDetail);
|
||||
charCode = 0;
|
||||
}
|
||||
|
||||
NS_LossyConvertUTF16toASCII commandText(mHandlerText);
|
||||
KeyboardScrollAction action;
|
||||
if (!nsGlobalWindowCommands::FindScrollCommand(commandText.get(), &action)) {
|
||||
// This action doesn't represent a scroll so we need to create a dispatch
|
||||
// to content keyboard shortcut so APZ handles this command correctly
|
||||
*aOut = KeyboardShortcut(eventType,
|
||||
keyCode,
|
||||
charCode,
|
||||
modifiers,
|
||||
modifiersMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This prototype is a command which represents a scroll action, so create
|
||||
// a keyboard shortcut to handle it
|
||||
*aOut = KeyboardShortcut(eventType,
|
||||
keyCode,
|
||||
charCode,
|
||||
modifiers,
|
||||
modifiersMask,
|
||||
action);
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIContent>
|
||||
nsXBLPrototypeHandler::GetHandlerElement()
|
||||
{
|
||||
|
@ -539,6 +603,54 @@ nsXBLPrototypeHandler::DispatchXULKeyCommand(nsIDOMEvent* aEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Modifiers
|
||||
nsXBLPrototypeHandler::GetModifiers() const
|
||||
{
|
||||
Modifiers modifiers = 0;
|
||||
|
||||
if (mKeyMask & cMeta) {
|
||||
modifiers |= MODIFIER_META;
|
||||
}
|
||||
if (mKeyMask & cOS) {
|
||||
modifiers |= MODIFIER_OS;
|
||||
}
|
||||
if (mKeyMask & cShift) {
|
||||
modifiers |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (mKeyMask & cAlt) {
|
||||
modifiers |= MODIFIER_ALT;
|
||||
}
|
||||
if (mKeyMask & cControl) {
|
||||
modifiers |= MODIFIER_CONTROL;
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
Modifiers
|
||||
nsXBLPrototypeHandler::GetModifiersMask() const
|
||||
{
|
||||
Modifiers modifiersMask = 0;
|
||||
|
||||
if (mKeyMask & cMetaMask) {
|
||||
modifiersMask |= MODIFIER_META;
|
||||
}
|
||||
if (mKeyMask & cOSMask) {
|
||||
modifiersMask |= MODIFIER_OS;
|
||||
}
|
||||
if (mKeyMask & cShiftMask) {
|
||||
modifiersMask |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (mKeyMask & cAltMask) {
|
||||
modifiersMask |= MODIFIER_ALT;
|
||||
}
|
||||
if (mKeyMask & cControlMask) {
|
||||
modifiersMask |= MODIFIER_CONTROL;
|
||||
}
|
||||
|
||||
return modifiersMask;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIAtom>
|
||||
nsXBLPrototypeHandler::GetEventName()
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef nsXBLPrototypeHandler_h__
|
||||
#define nsXBLPrototypeHandler_h__
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -27,10 +28,18 @@ class nsIObjectOutputStream;
|
|||
class nsXBLPrototypeBinding;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct IgnoreModifierState;
|
||||
|
||||
namespace dom {
|
||||
class AutoJSAPI;
|
||||
class EventTarget;
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
class KeyboardShortcut;
|
||||
} // namespace layers
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#define NS_HANDLER_TYPE_XBL_JS (1 << 0)
|
||||
|
@ -46,29 +55,11 @@ class EventTarget;
|
|||
#define NS_PHASE_TARGET 2
|
||||
#define NS_PHASE_BUBBLING 3
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct IgnoreModifierState
|
||||
{
|
||||
// When mShift is true, Shift key state will be ignored.
|
||||
bool mShift;
|
||||
// When mOS is true, OS key state will be ignored.
|
||||
bool mOS;
|
||||
|
||||
IgnoreModifierState()
|
||||
: mShift(false)
|
||||
, mOS(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsXBLPrototypeHandler
|
||||
{
|
||||
typedef mozilla::dom::IgnoreModifierState IgnoreModifierState;
|
||||
typedef mozilla::IgnoreModifierState IgnoreModifierState;
|
||||
typedef mozilla::layers::KeyboardShortcut KeyboardShortcut;
|
||||
typedef mozilla::Modifiers Modifiers;
|
||||
|
||||
public:
|
||||
// This constructor is used by XBL handlers (both the JS and command shorthand variety)
|
||||
|
@ -90,6 +81,17 @@ public:
|
|||
|
||||
~nsXBLPrototypeHandler();
|
||||
|
||||
/**
|
||||
* Try and convert this XBL handler into an APZ KeyboardShortcut for handling
|
||||
* key events on the compositor thread. This only works for XBL handlers that
|
||||
* represent scroll commands.
|
||||
*
|
||||
* @param aOut the converted KeyboardShortcut, must be non null
|
||||
* @return whether the handler was converted into a KeyboardShortcut
|
||||
*/
|
||||
bool TryConvertToKeyboardShortcut(
|
||||
KeyboardShortcut* aOut) const;
|
||||
|
||||
bool EventTypeEquals(nsIAtom* aEventType) const
|
||||
{
|
||||
return mEventName == aEventType;
|
||||
|
@ -186,6 +188,10 @@ protected:
|
|||
nsresult DispatchXULKeyCommand(nsIDOMEvent* aEvent);
|
||||
nsresult EnsureEventHandler(mozilla::dom::AutoJSAPI& jsapi, nsIAtom* aName,
|
||||
JS::MutableHandle<JSObject*> aHandler);
|
||||
|
||||
Modifiers GetModifiers() const;
|
||||
Modifiers GetModifiersMask() const;
|
||||
|
||||
static int32_t KeyToMask(int32_t key);
|
||||
static int32_t AccelKeyMask();
|
||||
|
||||
|
|
|
@ -25,17 +25,21 @@
|
|||
#include "nsIPresShell.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/layers/KeyboardMap.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIHTMLEditor.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
class nsXBLSpecialDocInfo : public nsIObserver
|
||||
{
|
||||
|
@ -146,9 +150,18 @@ nsXBLSpecialDocInfo::GetAllHandlers(const char* aType,
|
|||
}
|
||||
|
||||
// Init statics
|
||||
nsXBLSpecialDocInfo* nsXBLWindowKeyHandler::sXBLSpecialDocInfo = nullptr;
|
||||
static StaticRefPtr<nsXBLSpecialDocInfo> sXBLSpecialDocInfo;
|
||||
uint32_t nsXBLWindowKeyHandler::sRefCnt = 0;
|
||||
|
||||
/* static */ void
|
||||
nsXBLWindowKeyHandler::EnsureSpecialDocInfo()
|
||||
{
|
||||
if (!sXBLSpecialDocInfo) {
|
||||
sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
|
||||
}
|
||||
sXBLSpecialDocInfo->LoadDocInfo();
|
||||
}
|
||||
|
||||
nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement* aElement,
|
||||
EventTarget* aTarget)
|
||||
: mTarget(aTarget),
|
||||
|
@ -167,7 +180,7 @@ nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
|
|||
|
||||
--sRefCnt;
|
||||
if (!sRefCnt) {
|
||||
NS_IF_RELEASE(sXBLSpecialDocInfo);
|
||||
sXBLSpecialDocInfo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,11 +241,7 @@ nsXBLWindowKeyHandler::EnsureHandlers()
|
|||
nsCOMPtr<nsIContent> content(do_QueryInterface(el));
|
||||
BuildHandlerChain(content, &mHandler);
|
||||
} else { // We are an XBL file of handlers.
|
||||
if (!sXBLSpecialDocInfo) {
|
||||
sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
|
||||
NS_ADDREF(sXBLSpecialDocInfo);
|
||||
}
|
||||
sXBLSpecialDocInfo->LoadDocInfo();
|
||||
EnsureSpecialDocInfo();
|
||||
|
||||
// Now determine which handlers we should be using.
|
||||
if (IsHTMLEditableFieldFocused()) {
|
||||
|
@ -397,6 +406,41 @@ nsXBLWindowKeyHandler::RemoveKeyboardEventListenersFrom(
|
|||
TrustedEventsAtSystemGroupBubble());
|
||||
}
|
||||
|
||||
/* static */ KeyboardMap
|
||||
nsXBLWindowKeyHandler::CollectKeyboardShortcuts()
|
||||
{
|
||||
// Load the XBL handlers
|
||||
EnsureSpecialDocInfo();
|
||||
|
||||
nsXBLPrototypeHandler* handlers = nullptr;
|
||||
nsXBLPrototypeHandler* userHandlers = nullptr;
|
||||
sXBLSpecialDocInfo->GetAllHandlers("browser", &handlers, &userHandlers);
|
||||
|
||||
// Convert the handlers into keyboard shortcuts, using an AutoTArray with
|
||||
// the maximum amount of shortcuts used on any platform to minimize allocations
|
||||
AutoTArray<KeyboardShortcut, 46> shortcuts;
|
||||
|
||||
for (nsXBLPrototypeHandler* handler = handlers;
|
||||
handler;
|
||||
handler = handler->GetNextHandler()) {
|
||||
KeyboardShortcut shortcut;
|
||||
if (handler->TryConvertToKeyboardShortcut(&shortcut)) {
|
||||
shortcuts.AppendElement(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
for (nsXBLPrototypeHandler* handler = userHandlers;
|
||||
handler;
|
||||
handler = handler->GetNextHandler()) {
|
||||
KeyboardShortcut shortcut;
|
||||
if (handler->TryConvertToKeyboardShortcut(&shortcut)) {
|
||||
shortcuts.AppendElement(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
return KeyboardMap(mozilla::Move(shortcuts));
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsXBLWindowKeyHandler::ConvertEventToDOMEventType(
|
||||
const WidgetKeyboardEvent& aWidgetKeyboardEvent) const
|
||||
|
|
|
@ -8,28 +8,29 @@
|
|||
#define nsXBLWindowKeyHandler_h__
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/layers/KeyboardMap.h"
|
||||
#include "nsWeakPtr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
class nsIAtom;
|
||||
class nsIDOMElement;
|
||||
class nsIDOMKeyEvent;
|
||||
class nsXBLSpecialDocInfo;
|
||||
class nsXBLPrototypeHandler;
|
||||
|
||||
namespace mozilla {
|
||||
class EventListenerManager;
|
||||
struct IgnoreModifierState;
|
||||
namespace dom {
|
||||
class Element;
|
||||
class EventTarget;
|
||||
struct IgnoreModifierState;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsXBLWindowKeyHandler : public nsIDOMEventListener
|
||||
{
|
||||
typedef mozilla::dom::IgnoreModifierState IgnoreModifierState;
|
||||
typedef mozilla::EventListenerManager EventListenerManager;
|
||||
typedef mozilla::IgnoreModifierState IgnoreModifierState;
|
||||
typedef mozilla::layers::KeyboardMap KeyboardMap;
|
||||
|
||||
public:
|
||||
nsXBLWindowKeyHandler(nsIDOMElement* aElement, mozilla::dom::EventTarget* aTarget);
|
||||
|
@ -39,6 +40,8 @@ public:
|
|||
void RemoveKeyboardEventListenersFrom(
|
||||
EventListenerManager* aEventListenerManager);
|
||||
|
||||
static KeyboardMap CollectKeyboardShortcuts();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
|
@ -80,6 +83,9 @@ protected:
|
|||
nsIAtom* ConvertEventToDOMEventType(
|
||||
const mozilla::WidgetKeyboardEvent& aWidgetKeyboardEvent) const;
|
||||
|
||||
// lazily load the special doc info for loading handlers
|
||||
static void EnsureSpecialDocInfo();
|
||||
|
||||
// lazily load the handlers. Overridden to handle being attached
|
||||
// to a particular element rather than the document
|
||||
nsresult EnsureHandlers();
|
||||
|
@ -120,8 +126,7 @@ protected:
|
|||
nsXBLPrototypeHandler* mHandler; // platform bindings
|
||||
nsXBLPrototypeHandler* mUserHandler; // user-specific bindings
|
||||
|
||||
// holds document info about bindings
|
||||
static nsXBLSpecialDocInfo* sXBLSpecialDocInfo;
|
||||
// holds reference count to document info about bindings
|
||||
static uint32_t sRefCnt;
|
||||
};
|
||||
|
||||
|
|
|
@ -197,6 +197,18 @@ struct TextureFactoryIdentifier
|
|||
, mUsingAdvancedLayers(false)
|
||||
, mSyncHandle(aSyncHandle)
|
||||
{}
|
||||
|
||||
bool operator==(const TextureFactoryIdentifier& aOther) const {
|
||||
return
|
||||
mParentBackend == aOther.mParentBackend &&
|
||||
mParentProcessType == aOther.mParentProcessType &&
|
||||
mMaxTextureSize == aOther.mMaxTextureSize &&
|
||||
mCompositorUseANGLE == aOther.mCompositorUseANGLE &&
|
||||
mSupportsTextureBlitting == aOther.mSupportsTextureBlitting &&
|
||||
mSupportsPartialUploads == aOther.mSupportsPartialUploads &&
|
||||
mSupportsComponentAlpha == aOther.mSupportsComponentAlpha &&
|
||||
mSyncHandle == aOther.mSyncHandle;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -690,6 +690,12 @@ struct ScrollSnapInfo {
|
|||
mScrollSnapCoordinates == aOther.mScrollSnapCoordinates;
|
||||
}
|
||||
|
||||
bool HasScrollSnapping() const
|
||||
{
|
||||
return mScrollSnapTypeY != NS_STYLE_SCROLL_SNAP_TYPE_NONE ||
|
||||
mScrollSnapTypeX != NS_STYLE_SCROLL_SNAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
// The scroll frame's scroll-snap-type.
|
||||
// One of NS_STYLE_SCROLL_SNAP_{NONE, MANDATORY, PROXIMITY}.
|
||||
uint8_t mScrollSnapTypeX;
|
||||
|
|
|
@ -2102,6 +2102,15 @@ Layer::RemoveUserData(void* aKey)
|
|||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
Layer::SetManager(LayerManager* aManager, HostLayer* aSelf)
|
||||
{
|
||||
// No one should be calling this for weird reasons.
|
||||
MOZ_ASSERT(aSelf);
|
||||
MOZ_ASSERT(aSelf->GetLayer() == this);
|
||||
mManager = aManager;
|
||||
}
|
||||
|
||||
void
|
||||
PaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
{
|
||||
|
|
|
@ -97,6 +97,7 @@ class ReadbackLayer;
|
|||
class ReadbackProcessor;
|
||||
class RefLayer;
|
||||
class HostLayer;
|
||||
class FocusTarget;
|
||||
class KnowsCompositor;
|
||||
class ShadowableLayer;
|
||||
class ShadowLayerForwarder;
|
||||
|
@ -599,6 +600,11 @@ public:
|
|||
*/
|
||||
virtual void SetIsFirstPaint() {}
|
||||
|
||||
/**
|
||||
* Set the current focus target to be sent with the next paint.
|
||||
*/
|
||||
virtual void SetFocusTarget(const FocusTarget& aFocusTarget) {}
|
||||
|
||||
/**
|
||||
* Make sure that the previous transaction has been entirely
|
||||
* completed.
|
||||
|
@ -844,6 +850,11 @@ public:
|
|||
*/
|
||||
LayerManager* Manager() { return mManager; }
|
||||
|
||||
/**
|
||||
* This should only be called when changing layer managers from HostLayers.
|
||||
*/
|
||||
void SetManager(LayerManager* aManager, HostLayer* aSelf);
|
||||
|
||||
enum {
|
||||
/**
|
||||
* If this is set, the caller is promising that by the end of this
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/EventStateManager.h" // for WheelPrefs
|
||||
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
|
||||
#include "mozilla/MouseEvents.h" // for WidgetMouseEvent
|
||||
#include "mozilla/TextEvents.h" // for WidgetKeyboardEvent
|
||||
#include "mozilla/TouchEvents.h" // for WidgetTouchEvent
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -69,11 +70,12 @@ IAPZCTreeManager::ReceiveInputEvent(
|
|||
mouseEvent.mRefPoint.x = input.mOrigin.x;
|
||||
mouseEvent.mRefPoint.y = input.mOrigin.y;
|
||||
mouseEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
|
||||
mouseEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
TransformEventRefPoint(&mouseEvent.mRefPoint, aOutTargetGuid);
|
||||
ProcessUnhandledEvent(&mouseEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
case eTouchEventClass: {
|
||||
|
@ -92,6 +94,7 @@ IAPZCTreeManager::ReceiveInputEvent(
|
|||
touchInput.mTouches[i].ToNewDOMTouch();
|
||||
}
|
||||
touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
|
||||
touchEvent.mFocusSequenceNumber = touchInput.mFocusSequenceNumber;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
@ -133,18 +136,30 @@ IAPZCTreeManager::ReceiveInputEvent(
|
|||
wheelEvent.mRefPoint.x = input.mOrigin.x;
|
||||
wheelEvent.mRefPoint.y = input.mOrigin.y;
|
||||
wheelEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
|
||||
wheelEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
|
||||
return status;
|
||||
}
|
||||
|
||||
UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
|
||||
TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
|
||||
ProcessUnhandledEvent(&aEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
}
|
||||
case eKeyboardEventClass: {
|
||||
WidgetKeyboardEvent& keyboardEvent = *aEvent.AsKeyboardEvent();
|
||||
|
||||
KeyboardInput input(keyboardEvent);
|
||||
|
||||
nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId);
|
||||
|
||||
keyboardEvent.mFlags.mHandledByAPZ = input.mHandledByAPZ;
|
||||
keyboardEvent.mFocusSequenceNumber = input.mFocusSequenceNumber;
|
||||
return status;
|
||||
}
|
||||
default: {
|
||||
|
||||
UpdateWheelTransaction(aEvent.mRefPoint, aEvent.mMessage);
|
||||
TransformEventRefPoint(&aEvent.mRefPoint, aOutTargetGuid);
|
||||
ProcessUnhandledEvent(&aEvent.mRefPoint, aOutTargetGuid, &aEvent.mFocusSequenceNumber);
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ class InputData;
|
|||
|
||||
namespace layers {
|
||||
|
||||
class KeyboardMap;
|
||||
|
||||
enum AllowedTouchBehavior {
|
||||
NONE = 0,
|
||||
VERTICAL_PAN = 1 << 0,
|
||||
|
@ -104,6 +106,11 @@ public:
|
|||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId);
|
||||
|
||||
/**
|
||||
* Set the keyboard shortcuts to use for translating keyboard events.
|
||||
*/
|
||||
virtual void SetKeyboardMap(const KeyboardMap& aKeyboardMap) = 0;
|
||||
|
||||
/**
|
||||
* Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
|
||||
* in. The actual animation is done on the compositor thread after being set
|
||||
|
@ -204,9 +211,10 @@ protected:
|
|||
|
||||
// Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
|
||||
|
||||
virtual void TransformEventRefPoint(
|
||||
virtual void ProcessUnhandledEvent(
|
||||
LayoutDeviceIntPoint* aRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid) = 0;
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber) = 0;
|
||||
|
||||
virtual void UpdateWheelTransaction(
|
||||
LayoutDeviceIntPoint aRefPoint,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <stack>
|
||||
#include <unordered_set>
|
||||
#include "APZCTreeManager.h"
|
||||
#include "AsyncPanZoomController.h"
|
||||
#include "Compositor.h" // for Compositor
|
||||
|
@ -21,6 +22,7 @@
|
|||
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
|
||||
#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
|
||||
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
|
||||
#include "mozilla/layers/FocusState.h" // for FocusState
|
||||
#include "mozilla/layers/LayerMetricsWrapper.h"
|
||||
#include "mozilla/layers/WebRenderScrollDataWrapper.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
|
@ -82,6 +84,11 @@ struct APZCTreeManager::TreeBuildingState {
|
|||
// This is initialized with all nodes in the old tree, and nodes are removed
|
||||
// from it as we reuse them in the new tree.
|
||||
nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
|
||||
// A set of layer trees that are no longer in the hit testing tree. This is
|
||||
// used to destroy unneeded focus targets at the end of tree building. This
|
||||
// is needed in addition to mNodesToDestroy because a hit testing node for a
|
||||
// layer tree can be removed without the whole layer tree being removed.
|
||||
std::unordered_set<uint64_t> mLayersIdsToDestroy;
|
||||
|
||||
// This map is populated as we place APZCs into the new tree. Its purpose is
|
||||
// to facilitate re-using the same APZC for different layers that scroll
|
||||
|
@ -158,6 +165,35 @@ APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* A RAII class used for setting the focus sequence number on input events
|
||||
* as they are being processed. Any input event is assumed to be potentially
|
||||
* focus changing unless explicitly marked otherwise.
|
||||
*/
|
||||
class MOZ_RAII AutoFocusSequenceNumberSetter
|
||||
{
|
||||
public:
|
||||
AutoFocusSequenceNumberSetter(FocusState& aFocusState, InputData& aEvent)
|
||||
: mFocusState(aFocusState)
|
||||
, mEvent(aEvent)
|
||||
, mMayChangeFocus(true)
|
||||
{ }
|
||||
|
||||
void MarkAsNonFocusChanging() { mMayChangeFocus = false; }
|
||||
|
||||
~AutoFocusSequenceNumberSetter()
|
||||
{
|
||||
if (mMayChangeFocus) {
|
||||
mFocusState.ReceiveFocusChangingEvent();
|
||||
}
|
||||
mEvent.mFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
FocusState& mFocusState;
|
||||
InputData& mEvent;
|
||||
bool mMayChangeFocus;
|
||||
};
|
||||
|
||||
/*static*/ const ScreenMargin
|
||||
APZCTreeManager::CalculatePendingDisplayPort(
|
||||
|
@ -263,6 +299,7 @@ APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
|
|||
{
|
||||
state.mNodesToDestroy.AppendElement(aNode);
|
||||
});
|
||||
state.mLayersIdsToDestroy = mFocusState.GetFocusTargetLayerIds();
|
||||
mRootNode = nullptr;
|
||||
|
||||
if (aRoot) {
|
||||
|
@ -273,6 +310,8 @@ APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
|
|||
uint64_t layersId = aRootLayerTreeId;
|
||||
ancestorTransforms.push(Matrix4x4());
|
||||
|
||||
state.mLayersIdsToDestroy.erase(aRootLayerTreeId);
|
||||
|
||||
mApzcTreeLog << "[start]\n";
|
||||
mTreeLock.AssertCurrentThreadOwns();
|
||||
|
||||
|
@ -310,7 +349,15 @@ APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
|
|||
MOZ_ASSERT(!node->GetFirstChild());
|
||||
parent = node;
|
||||
next = nullptr;
|
||||
layersId = aLayerMetrics.GetReferentId().valueOr(layersId);
|
||||
|
||||
// Update the layersId if we have a new one
|
||||
if (Maybe<uint64_t> newLayersId = aLayerMetrics.GetReferentId()) {
|
||||
layersId = *newLayersId;
|
||||
|
||||
// Mark that this layer tree is being used
|
||||
state.mLayersIdsToDestroy.erase(layersId);
|
||||
}
|
||||
|
||||
indents.push(gfx::TreeAutoIndent(mApzcTreeLog));
|
||||
},
|
||||
[&](ScrollNode aLayerMetrics)
|
||||
|
@ -335,6 +382,11 @@ APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
|
|||
state.mNodesToDestroy[i]->Destroy();
|
||||
}
|
||||
|
||||
// Clear out any focus targets that are no longer needed
|
||||
for (auto layersId : state.mLayersIdsToDestroy) {
|
||||
mFocusState.RemoveFocusTarget(layersId);
|
||||
}
|
||||
|
||||
#if ENABLE_APZCTM_LOGGING
|
||||
// Make the hit-test tree line up with the layer dump
|
||||
printf_stderr("APZCTreeManager (%p)\n", this);
|
||||
|
@ -342,6 +394,20 @@ APZCTreeManager::UpdateHitTestingTreeImpl(uint64_t aRootLayerTreeId,
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::UpdateFocusState(uint64_t aRootLayerTreeId,
|
||||
uint64_t aOriginatingLayersId,
|
||||
const FocusTarget& aFocusTarget)
|
||||
{
|
||||
if (!gfxPrefs::APZKeyboardEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mFocusState.Update(aRootLayerTreeId,
|
||||
aOriginatingLayersId,
|
||||
aFocusTarget);
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
|
||||
Layer* aRoot,
|
||||
|
@ -868,6 +934,9 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
|
|||
{
|
||||
APZThreadUtils::AssertOnControllerThread();
|
||||
|
||||
// Use a RAII class for updating the focus sequence number of this event
|
||||
AutoFocusSequenceNumberSetter focusSetter(mFocusState, aEvent);
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
MOZ_ASSERT(mToolbarAnimator);
|
||||
ScreenPoint scrollOffset;
|
||||
|
@ -1035,7 +1104,6 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
|
|||
FlushRepaintsToClearScreenToGeckoTransform();
|
||||
|
||||
ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
|
||||
|
||||
wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
|
||||
if (!wheelInput.mHandledByAPZ) {
|
||||
return result;
|
||||
|
@ -1176,6 +1244,91 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
|
|||
apzc->GetGuid(aOutTargetGuid);
|
||||
tapInput.mPoint = *untransformedPoint;
|
||||
}
|
||||
break;
|
||||
} case KEYBOARD_INPUT: {
|
||||
// Disable async keyboard scrolling when accessibility.browsewithcaret is enabled
|
||||
if (!gfxPrefs::APZKeyboardEnabled() ||
|
||||
gfxPrefs::AccessibilityBrowseWithCaret()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
KeyboardInput& keyInput = aEvent.AsKeyboardInput();
|
||||
|
||||
// Try and find a matching shortcut for this keyboard input
|
||||
Maybe<KeyboardShortcut> shortcut = mKeyboardMap.FindMatch(keyInput);
|
||||
|
||||
if (!shortcut) {
|
||||
// If we don't have a shortcut for this key event, then we can keep our focus
|
||||
// only if we know there are no key event listeners for this target
|
||||
if (mFocusState.CanIgnoreKeyboardShortcutMisses()) {
|
||||
focusSetter.MarkAsNonFocusChanging();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if this shortcut needs to be dispatched to content. Anything matching
|
||||
// this is assumed to be able to change focus.
|
||||
if (shortcut->mDispatchToContent) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// We know we have an action to execute on whatever is the current focus target
|
||||
const KeyboardScrollAction& action = shortcut->mAction;
|
||||
|
||||
// The current focus target depends on which direction the scroll is to happen
|
||||
Maybe<ScrollableLayerGuid> targetGuid;
|
||||
switch (action.mType)
|
||||
{
|
||||
case KeyboardScrollAction::eScrollCharacter: {
|
||||
targetGuid = mFocusState.GetHorizontalTarget();
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollLine:
|
||||
case KeyboardScrollAction::eScrollPage:
|
||||
case KeyboardScrollAction::eScrollComplete: {
|
||||
targetGuid = mFocusState.GetVerticalTarget();
|
||||
break;
|
||||
}
|
||||
|
||||
case KeyboardScrollAction::eSentinel: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid KeyboardScrollActionType");
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a scroll target then either we have a stale focus target,
|
||||
// the focused element has event listeners, or the focused element doesn't have a
|
||||
// layerized scroll frame. In any case we need to dispatch to content.
|
||||
if (!targetGuid) {
|
||||
return result;
|
||||
}
|
||||
|
||||
RefPtr<AsyncPanZoomController> targetApzc = GetTargetAPZC(targetGuid->mLayersId,
|
||||
targetGuid->mScrollId);
|
||||
|
||||
// Scroll snapping behavior with keyboard input is more complicated, so
|
||||
// ignore any input events that are targeted at an Apzc with scroll snap
|
||||
// points.
|
||||
if (!targetApzc || targetApzc->HasScrollSnapping()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Attach the keyboard scroll action to the input event for processing
|
||||
// by the input queue.
|
||||
keyInput.mAction = action;
|
||||
|
||||
// Dispatch the event to the input queue.
|
||||
result = mInputQueue->ReceiveInputEvent(
|
||||
targetApzc,
|
||||
/* aTargetConfirmed = */ true,
|
||||
keyInput, aOutInputBlockId);
|
||||
|
||||
// Any keyboard event that is dispatched to the input queue at this point
|
||||
// should have been consumed
|
||||
MOZ_ASSERT(result == nsEventStatus_eConsumeNoDefault);
|
||||
|
||||
keyInput.mHandledByAPZ = true;
|
||||
focusSetter.MarkAsNonFocusChanging();
|
||||
|
||||
break;
|
||||
} case SENTINEL_INPUT: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid InputType.");
|
||||
|
@ -1404,8 +1557,9 @@ APZCTreeManager::UpdateWheelTransaction(LayoutDeviceIntPoint aRefPoint,
|
|||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::TransformEventRefPoint(LayoutDeviceIntPoint* aRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid)
|
||||
APZCTreeManager::ProcessUnhandledEvent(LayoutDeviceIntPoint* aRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber)
|
||||
{
|
||||
// Transform the aRefPoint.
|
||||
// If the event hits an overscrolled APZC, instruct the caller to ignore it.
|
||||
|
@ -1427,6 +1581,10 @@ APZCTreeManager::TransformEventRefPoint(LayoutDeviceIntPoint* aRefPoint,
|
|||
ViewAs<LayoutDevicePixel>(*untransformedRefPoint, LDIsScreen);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the focus sequence number and attach it to the event
|
||||
mFocusState.ReceiveFocusChangingEvent();
|
||||
*aOutFocusSequenceNumber = mFocusState.LastAPZProcessedEvent();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1437,6 +1595,12 @@ APZCTreeManager::ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap)
|
||||
{
|
||||
mKeyboardMap = aKeyboardMap;
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
|
||||
const CSSRect& aRect,
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
#include "mozilla/layers/TouchCounter.h"// for TouchCounter
|
||||
#include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager
|
||||
#include "mozilla/layers/KeyboardMap.h" // for KeyboardMap
|
||||
#include "mozilla/layers/FocusState.h" // for FocusState
|
||||
#include "mozilla/Mutex.h" // for Mutex
|
||||
#include "mozilla/RefPtr.h" // for RefPtr
|
||||
#include "mozilla/TimeStamp.h" // for mozilla::TimeStamp
|
||||
|
@ -40,6 +42,7 @@ class APZCTreeManagerParent;
|
|||
class CompositorBridgeParent;
|
||||
class OverscrollHandoffChain;
|
||||
struct OverscrollHandoffState;
|
||||
class FocusTarget;
|
||||
struct FlingHandoffState;
|
||||
struct ScrollableLayerGuidHash;
|
||||
class LayerMetricsWrapper;
|
||||
|
@ -111,6 +114,19 @@ public:
|
|||
*/
|
||||
static void InitializeGlobalState();
|
||||
|
||||
/**
|
||||
* Rebuild the focus state based on the focus target from the layer tree update
|
||||
* that just occurred.
|
||||
*
|
||||
* @param aRootLayerTreeId The layer tree ID of the root layer corresponding
|
||||
* to this APZCTreeManager
|
||||
* @param aOriginatingLayersId The layer tree ID of the layer corresponding to
|
||||
* this layer tree update.
|
||||
*/
|
||||
void UpdateFocusState(uint64_t aRootLayerTreeId,
|
||||
uint64_t aOriginatingLayersId,
|
||||
const FocusTarget& aFocusTarget);
|
||||
|
||||
/**
|
||||
* Rebuild the hit-testing tree based on the layer update that just came up.
|
||||
* Preserve nodes and APZC instances where possible, but retire those whose
|
||||
|
@ -211,6 +227,11 @@ public:
|
|||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId) override;
|
||||
|
||||
/**
|
||||
* Set the keyboard shortcuts to use for translating keyboard events.
|
||||
*/
|
||||
void SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
|
||||
|
||||
/**
|
||||
* Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
|
||||
* in. The actual animation is done on the compositor thread after being set
|
||||
|
@ -430,9 +451,10 @@ public:
|
|||
|
||||
// Methods to help process WidgetInputEvents (or manage conversion to/from InputData)
|
||||
|
||||
void TransformEventRefPoint(
|
||||
void ProcessUnhandledEvent(
|
||||
LayoutDeviceIntPoint* aRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid) override;
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber) override;
|
||||
|
||||
void UpdateWheelTransaction(
|
||||
LayoutDeviceIntPoint aRefPoint,
|
||||
|
@ -550,6 +572,14 @@ private:
|
|||
/* Holds the zoom constraints for scrollable layers, as determined by the
|
||||
* the main-thread gecko code. */
|
||||
std::unordered_map<ScrollableLayerGuid, ZoomConstraints, ScrollableLayerGuidHash> mZoomConstraints;
|
||||
/* A list of keyboard shortcuts to use for translating keyboard inputs into
|
||||
* keyboard actions. This is gathered on the main thread from XBL bindings.
|
||||
*/
|
||||
KeyboardMap mKeyboardMap;
|
||||
/* This tracks the focus targets of chrome and content and whether we have
|
||||
* a current focus target or whether we are waiting for a new confirmation.
|
||||
*/
|
||||
FocusState mFocusState;
|
||||
/* This tracks the APZC that should receive all inputs for the current input event block.
|
||||
* This allows touch points to move outside the thing they started on, but still have the
|
||||
* touch events delivered to the same initial APZC. This will only ever be touched on the
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace mozilla {
|
|||
namespace layers {
|
||||
|
||||
class WheelScrollAnimation;
|
||||
class KeyboardScrollAnimation;
|
||||
class SmoothScrollAnimation;
|
||||
|
||||
class AsyncPanZoomAnimation {
|
||||
|
@ -50,6 +51,9 @@ public:
|
|||
return Move(mDeferredTasks);
|
||||
}
|
||||
|
||||
virtual KeyboardScrollAnimation* AsKeyboardScrollAnimation() {
|
||||
return nullptr;
|
||||
}
|
||||
virtual WheelScrollAnimation* AsWheelScrollAnimation() {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
#include "SharedMemoryBasic.h" // for SharedMemoryBasic
|
||||
#include "ScrollSnap.h" // for ScrollSnapUtils
|
||||
#include "WheelScrollAnimation.h"
|
||||
#include "KeyboardScrollAnimation.h"
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
#include "AndroidAPZ.h"
|
||||
#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
|
||||
|
@ -1005,6 +1006,11 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
|
|||
rv = HandleGestureEvent(tapInput);
|
||||
break;
|
||||
}
|
||||
case KEYBOARD_INPUT: {
|
||||
KeyboardInput keyInput = aEvent.AsKeyboardInput();
|
||||
rv = OnKeyboard(keyInput);
|
||||
break;
|
||||
}
|
||||
case SENTINEL_INPUT: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid value");
|
||||
break;
|
||||
|
@ -1067,6 +1073,7 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
|
|||
case SMOOTH_SCROLL:
|
||||
case OVERSCROLL_ANIMATION:
|
||||
case WHEEL_SCROLL:
|
||||
case KEYBOARD_SCROLL:
|
||||
case PAN_MOMENTUM:
|
||||
MOZ_ASSERT(GetCurrentTouchBlock());
|
||||
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
|
||||
|
@ -1142,6 +1149,7 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
|
|||
return nsEventStatus_eIgnore;
|
||||
|
||||
case WHEEL_SCROLL:
|
||||
case KEYBOARD_SCROLL:
|
||||
case OVERSCROLL_ANIMATION:
|
||||
// Should not receive a touch-move in the OVERSCROLL_ANIMATION state
|
||||
// as touch blocks that begin in an overscrolled state cancel the
|
||||
|
@ -1222,6 +1230,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
|||
return nsEventStatus_eIgnore;
|
||||
|
||||
case WHEEL_SCROLL:
|
||||
case KEYBOARD_SCROLL:
|
||||
case OVERSCROLL_ANIMATION:
|
||||
// Should not receive a touch-end in the OVERSCROLL_ANIMATION state
|
||||
// as touch blocks that begin in an overscrolled state cancel the
|
||||
|
@ -1656,6 +1665,152 @@ AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent) cons
|
|||
return delta;
|
||||
}
|
||||
|
||||
static
|
||||
void ReportKeyboardScrollAction(const KeyboardScrollAction& aAction)
|
||||
{
|
||||
ScrollInputMethod scrollMethod;
|
||||
|
||||
switch (aAction.mType) {
|
||||
case KeyboardScrollAction::eScrollLine: {
|
||||
scrollMethod = ScrollInputMethod::ApzScrollLine;
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollCharacter: {
|
||||
scrollMethod = ScrollInputMethod::ApzScrollCharacter;
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollPage: {
|
||||
scrollMethod = ScrollInputMethod::ApzScrollPage;
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollComplete: {
|
||||
scrollMethod = ScrollInputMethod::ApzCompleteScroll;
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eSentinel: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid KeyboardScrollAction.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
|
||||
(uint32_t)scrollMethod);
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent)
|
||||
{
|
||||
// Report the type of scroll action to telemetry
|
||||
ReportKeyboardScrollAction(aEvent.mAction);
|
||||
|
||||
// Calculate the destination for this keyboard scroll action
|
||||
nsPoint destination = CSSPoint::ToAppUnits(GetKeyboardDestination(aEvent.mAction));
|
||||
|
||||
// The lock must be held across the entire update operation, so the
|
||||
// compositor doesn't end the animation before we get a chance to
|
||||
// update it.
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
// Use a keyboard scroll animation to scroll, reusing an existing one if it exists
|
||||
if (mState != KEYBOARD_SCROLL) {
|
||||
CancelAnimation();
|
||||
SetState(KEYBOARD_SCROLL);
|
||||
|
||||
nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
|
||||
StartAnimation(new KeyboardScrollAnimation(*this, initialPosition, aEvent.mAction.mType));
|
||||
}
|
||||
|
||||
// Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
|
||||
// appunits/second. We perform a cast to ParentLayerPoints/ms without a
|
||||
// conversion so that the scroll duration isn't affected by zoom
|
||||
nsPoint velocity =
|
||||
CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
|
||||
|
||||
KeyboardScrollAnimation* animation = mAnimation->AsKeyboardScrollAnimation();
|
||||
MOZ_ASSERT(animation);
|
||||
|
||||
animation->UpdateDestination(aEvent.mTimeStamp, destination, nsSize(velocity.x, velocity.y));
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
CSSPoint
|
||||
AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aAction) const
|
||||
{
|
||||
CSSSize lineScrollSize;
|
||||
CSSSize pageScrollSize;
|
||||
CSSPoint scrollOffset;
|
||||
CSSRect scrollRect;
|
||||
|
||||
{
|
||||
// Grab the lock to access the frame metrics.
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
lineScrollSize = mScrollMetadata.GetLineScrollAmount() /
|
||||
mFrameMetrics.GetDevPixelsPerCSSPixel();
|
||||
pageScrollSize = mScrollMetadata.GetPageScrollAmount() /
|
||||
mFrameMetrics.GetDevPixelsPerCSSPixel();
|
||||
|
||||
if (mState == WHEEL_SCROLL) {
|
||||
scrollOffset = mAnimation->AsWheelScrollAnimation()->GetDestination();
|
||||
} else if (mState == SMOOTH_SCROLL) {
|
||||
scrollOffset = mAnimation->AsSmoothScrollAnimation()->GetDestination();
|
||||
} else if (mState == KEYBOARD_SCROLL) {
|
||||
scrollOffset = mAnimation->AsKeyboardScrollAnimation()->GetDestination();
|
||||
} else {
|
||||
scrollOffset = mFrameMetrics.GetScrollOffset();
|
||||
}
|
||||
|
||||
scrollRect = mFrameMetrics.GetScrollableRect();
|
||||
}
|
||||
|
||||
// Calculate the scroll destination based off of the scroll type and direction
|
||||
CSSPoint scrollDestination = scrollOffset;
|
||||
|
||||
switch (aAction.mType) {
|
||||
case KeyboardScrollAction::eScrollCharacter: {
|
||||
int32_t scrollDistance = gfxPrefs::ToolkitHorizontalScrollDistance();
|
||||
|
||||
if (aAction.mForward) {
|
||||
scrollDestination.x += scrollDistance * lineScrollSize.width;
|
||||
} else {
|
||||
scrollDestination.x -= scrollDistance * lineScrollSize.width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollLine: {
|
||||
int32_t scrollDistance = gfxPrefs::ToolkitVerticalScrollDistance();
|
||||
|
||||
if (aAction.mForward) {
|
||||
scrollDestination.y += scrollDistance * lineScrollSize.height;
|
||||
} else {
|
||||
scrollDestination.y -= scrollDistance * lineScrollSize.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollPage: {
|
||||
if (aAction.mForward) {
|
||||
scrollDestination.y += pageScrollSize.height;
|
||||
} else {
|
||||
scrollDestination.y -= pageScrollSize.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollComplete: {
|
||||
if (aAction.mForward) {
|
||||
scrollDestination.y = scrollRect.YMost();
|
||||
} else {
|
||||
scrollDestination.y = scrollRect.y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eSentinel:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected keyboard delta type");
|
||||
}
|
||||
|
||||
return scrollDestination;
|
||||
}
|
||||
|
||||
// Return whether or not the underlying layer can be scrolled on either axis.
|
||||
bool
|
||||
AsyncPanZoomController::CanScroll(const InputData& aEvent) const
|
||||
|
@ -1822,6 +1977,8 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
|
|||
startPosition = mAnimation->AsWheelScrollAnimation()->GetDestination();
|
||||
} else if (mState == SMOOTH_SCROLL) {
|
||||
startPosition = mAnimation->AsSmoothScrollAnimation()->GetDestination();
|
||||
} else if (mState == KEYBOARD_SCROLL) {
|
||||
startPosition = mAnimation->AsKeyboardScrollAnimation()->GetDestination();
|
||||
}
|
||||
if (MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition)) {
|
||||
// If we're scroll snapping, use a smooth scroll animation to get
|
||||
|
@ -1845,12 +2002,13 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
|
|||
nsPoint deltaInAppUnits =
|
||||
CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom());
|
||||
// Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
|
||||
// appunits/second
|
||||
// appunits/second. We perform a cast to ParentLayerPoints/ms without a
|
||||
// conversion so that the scroll duration isn't affected by zoom
|
||||
nsPoint velocity =
|
||||
CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
|
||||
|
||||
WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation();
|
||||
animation->Update(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
|
||||
animation->UpdateDelta(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2608,7 +2766,8 @@ void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination) {
|
|||
SetState(SMOOTH_SCROLL);
|
||||
nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
|
||||
// Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
|
||||
// appunits/second
|
||||
// appunits/second. We perform a cast to ParentLayerPoints/ms without a
|
||||
// conversion so that the scroll duration isn't affected by zoom
|
||||
nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(),
|
||||
mY.GetVelocity())) * 1000.0f;
|
||||
nsPoint destination = CSSPoint::ToAppUnits(aDestination);
|
||||
|
|
|
@ -61,6 +61,7 @@ class OverscrollEffectBase;
|
|||
class WidgetOverscrollEffect;
|
||||
class GenericOverscrollEffect;
|
||||
class AndroidSpecificState;
|
||||
struct KeyboardScrollAction;
|
||||
|
||||
// Base class for grouping platform-specific APZC state variables.
|
||||
class PlatformSpecificStateBase {
|
||||
|
@ -339,6 +340,13 @@ public:
|
|||
*/
|
||||
bool HasScrollgrab() const { return mScrollMetadata.GetHasScrollgrab(); }
|
||||
|
||||
/**
|
||||
* Returns whether this APZC has scroll snap points.
|
||||
*/
|
||||
bool HasScrollSnapping() const {
|
||||
return mScrollMetadata.GetSnapInfo().HasScrollSnapping();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this APZC has room to be panned (in any direction).
|
||||
*/
|
||||
|
@ -482,6 +490,13 @@ protected:
|
|||
|
||||
ParentLayerPoint GetScrollWheelDelta(const ScrollWheelInput& aEvent) const;
|
||||
|
||||
/**
|
||||
* Helper methods for handling keyboard events.
|
||||
*/
|
||||
nsEventStatus OnKeyboard(const KeyboardInput& aEvent);
|
||||
|
||||
CSSPoint GetKeyboardDestination(const KeyboardScrollAction& aAction) const;
|
||||
|
||||
/**
|
||||
* Helper methods for long press gestures.
|
||||
*/
|
||||
|
@ -839,7 +854,8 @@ protected:
|
|||
the finger is lifted. */
|
||||
SMOOTH_SCROLL, /* Smooth scrolling to destination. Used by
|
||||
CSSOM-View smooth scroll-behavior */
|
||||
WHEEL_SCROLL /* Smooth scrolling to a destination for a wheel event. */
|
||||
WHEEL_SCROLL, /* Smooth scrolling to a destination for a wheel event. */
|
||||
KEYBOARD_SCROLL /* Smooth scrolling to a destination for a keyboard event. */
|
||||
};
|
||||
|
||||
// This is in theory protected by |mMonitor|; that is, it should be held whenever
|
||||
|
@ -936,7 +952,9 @@ private:
|
|||
friend class GenericFlingAnimation;
|
||||
friend class OverscrollAnimation;
|
||||
friend class SmoothScrollAnimation;
|
||||
friend class GenericScrollAnimation;
|
||||
friend class WheelScrollAnimation;
|
||||
friend class KeyboardScrollAnimation;
|
||||
|
||||
friend class GenericOverscrollEffect;
|
||||
friend class WidgetOverscrollEffect;
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "mozilla/layers/FocusState.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
FocusState::FocusState()
|
||||
: mLastAPZProcessedEvent(1)
|
||||
, mLastContentProcessedEvent(0)
|
||||
, mFocusHasKeyEventListeners(false)
|
||||
, mFocusLayersId(0)
|
||||
, mFocusHorizontalTarget(FrameMetrics::NULL_SCROLL_ID)
|
||||
, mFocusVerticalTarget(FrameMetrics::NULL_SCROLL_ID)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
FocusState::IsCurrent() const
|
||||
{
|
||||
MOZ_ASSERT(mLastContentProcessedEvent <= mLastAPZProcessedEvent);
|
||||
return mLastContentProcessedEvent == mLastAPZProcessedEvent;
|
||||
}
|
||||
|
||||
void
|
||||
FocusState::ReceiveFocusChangingEvent()
|
||||
{
|
||||
mLastAPZProcessedEvent += 1;
|
||||
}
|
||||
|
||||
void
|
||||
FocusState::Update(uint64_t aRootLayerTreeId,
|
||||
uint64_t aOriginatingLayersId,
|
||||
const FocusTarget& aState)
|
||||
{
|
||||
// Update the focus tree with the latest target
|
||||
mFocusTree[aOriginatingLayersId] = aState;
|
||||
|
||||
// Reset our internal state so we can recalculate it
|
||||
mFocusHasKeyEventListeners = false;
|
||||
mFocusLayersId = aRootLayerTreeId;
|
||||
mFocusHorizontalTarget = FrameMetrics::NULL_SCROLL_ID;
|
||||
mFocusVerticalTarget = FrameMetrics::NULL_SCROLL_ID;
|
||||
|
||||
// To update the focus state for the entire APZCTreeManager, we need
|
||||
// to traverse the focus tree to find the current leaf which is the global
|
||||
// focus target we can use for async keyboard scrolling
|
||||
while (true) {
|
||||
auto currentNode = mFocusTree.find(mFocusLayersId);
|
||||
if (currentNode == mFocusTree.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const FocusTarget& target = currentNode->second;
|
||||
|
||||
// Accumulate event listener flags on the path to the focus target
|
||||
mFocusHasKeyEventListeners |= target.mFocusHasKeyEventListeners;
|
||||
|
||||
switch (target.mType) {
|
||||
case FocusTarget::eRefLayer: {
|
||||
// Guard against infinite loops
|
||||
MOZ_ASSERT(mFocusLayersId != target.mData.mRefLayerId);
|
||||
if (mFocusLayersId == target.mData.mRefLayerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The focus target is in a child layer tree
|
||||
mFocusLayersId = target.mData.mRefLayerId;
|
||||
break;
|
||||
}
|
||||
case FocusTarget::eScrollLayer: {
|
||||
// This is the global focus target
|
||||
mFocusHorizontalTarget = target.mData.mScrollTargets.mHorizontal;
|
||||
mFocusVerticalTarget = target.mData.mScrollTargets.mVertical;
|
||||
|
||||
// Mark what sequence number this target has so we can determine whether
|
||||
// it is stale or not
|
||||
mLastContentProcessedEvent = target.mSequenceNumber;
|
||||
return;
|
||||
}
|
||||
case FocusTarget::eNone: {
|
||||
// Mark what sequence number this target has for debugging purposes so
|
||||
// we can always accurately report on whether we are stale or not
|
||||
mLastContentProcessedEvent = target.mSequenceNumber;
|
||||
return;
|
||||
}
|
||||
case FocusTarget::eSentinel: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid FocusTargetType");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t>
|
||||
FocusState::GetFocusTargetLayerIds() const
|
||||
{
|
||||
std::unordered_set<uint64_t> layersIds;
|
||||
layersIds.reserve(mFocusTree.size());
|
||||
|
||||
for (const auto& focusNode : mFocusTree) {
|
||||
layersIds.insert(focusNode.first);
|
||||
}
|
||||
|
||||
return layersIds;
|
||||
}
|
||||
|
||||
void
|
||||
FocusState::RemoveFocusTarget(uint64_t aLayersId)
|
||||
{
|
||||
mFocusTree.erase(aLayersId);
|
||||
}
|
||||
|
||||
Maybe<ScrollableLayerGuid>
|
||||
FocusState::GetHorizontalTarget() const
|
||||
{
|
||||
// There is not a scrollable layer to async scroll if
|
||||
// 1. We aren't current
|
||||
// 2. There are event listeners that could change the focus
|
||||
// 3. The target has not been layerized
|
||||
if (!IsCurrent() ||
|
||||
mFocusHasKeyEventListeners ||
|
||||
mFocusHorizontalTarget == FrameMetrics::NULL_SCROLL_ID) {
|
||||
return Nothing();
|
||||
}
|
||||
return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusHorizontalTarget));
|
||||
}
|
||||
|
||||
Maybe<ScrollableLayerGuid>
|
||||
FocusState::GetVerticalTarget() const
|
||||
{
|
||||
// There is not a scrollable layer to async scroll if:
|
||||
// 1. We aren't current
|
||||
// 2. There are event listeners that could change the focus
|
||||
// 3. The target has not been layerized
|
||||
if (!IsCurrent() ||
|
||||
mFocusHasKeyEventListeners ||
|
||||
mFocusVerticalTarget == FrameMetrics::NULL_SCROLL_ID) {
|
||||
return Nothing();
|
||||
}
|
||||
return Some(ScrollableLayerGuid(mFocusLayersId, 0, mFocusVerticalTarget));
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,173 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef mozilla_layers_FocusState_h
|
||||
#define mozilla_layers_FocusState_h
|
||||
|
||||
#include <unordered_map> // for std::unordered_map
|
||||
#include <unordered_set> // for std::unordered_set
|
||||
|
||||
#include "FrameMetrics.h" // for FrameMetrics::ViewID
|
||||
|
||||
#include "mozilla/layers/FocusTarget.h" // for FocusTarget
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* This class is used for tracking chrome and content focus targets and calculating
|
||||
* global focus information from them for use by APZCTreeManager for async keyboard
|
||||
* scrolling.
|
||||
*
|
||||
* # Calculating the element to scroll
|
||||
*
|
||||
* Chrome and content processes have independently focused elements. This makes it
|
||||
* difficult to calculate the global focused element and its scrollable frame from
|
||||
* the chrome or content side. So instead we send the local focus information from
|
||||
* each process to here and then calculate the global focus information. This
|
||||
* local information resides in a `focus target`.
|
||||
*
|
||||
* A focus target indicates that either:
|
||||
* 1. The focused element is a remote browser along with its layer tree ID
|
||||
* 2. The focused element is not scrollable
|
||||
* 3. The focused element is scrollable along with the ViewID's of its
|
||||
scrollable layers
|
||||
*
|
||||
* Using this information we can determine the global focus information by
|
||||
* starting at the focus target of the root layer tree ID and following remote
|
||||
* browsers until we reach a scrollable or non-scrollable focus target.
|
||||
*
|
||||
* # Determinism and sequence numbers
|
||||
*
|
||||
* The focused element in content can be changed within any javascript code. And
|
||||
* javascript can run in response to an event or at any moment from `setTimeout`
|
||||
* and others. This makes it impossible to always have the current focus
|
||||
* information in APZ as it can be changed asynchronously at any moment. If we
|
||||
* don't have the latest focus information, we may incorrectly scroll a target
|
||||
* when we shouldn't.
|
||||
*
|
||||
* A tradeoff is designed here whereby we will maintain deterministic focus
|
||||
* changes for user input, but not for other javascript code. The reasoning
|
||||
* here is that `setTimeout` and others are already non-deterministic and so it
|
||||
* might not be as breaking to web content.
|
||||
*
|
||||
* To maintain deterministic focus changes for a given stream of user inputs, we
|
||||
* invalidate our focus state whenever we receive a user input that may trigger
|
||||
* event listeners. We then attach a new sequence number to these events and
|
||||
* dispatch them to content. Content will then include the latest sequence number
|
||||
* it has processed to every focus update. Using this we can determine whether
|
||||
* any potentially focus changing events have yet to be handled by content.
|
||||
*
|
||||
* Once we have received the latest focus sequence number from content, we know
|
||||
* that all event listeners triggered by user inputs, and their resulting focus
|
||||
* changes, have been processed and so we have a current target that we can use
|
||||
* again.
|
||||
*/
|
||||
class FocusState final
|
||||
{
|
||||
public:
|
||||
FocusState();
|
||||
|
||||
/**
|
||||
* The sequence number of the last potentially focus changing event processed
|
||||
* by APZ. This number starts at one and increases monotonically. This number
|
||||
* will never be zero as that is used to catch uninitialized focus sequence
|
||||
* numbers on input events.
|
||||
*/
|
||||
uint64_t LastAPZProcessedEvent() const { return mLastAPZProcessedEvent; }
|
||||
|
||||
/**
|
||||
* Whether the current focus state is known to be current or else if an event
|
||||
* has been processed that could change the focus but we have not received an
|
||||
* update with a new confirmed target.
|
||||
*/
|
||||
bool IsCurrent() const;
|
||||
|
||||
/**
|
||||
* Notify focus state of a potentially focus changing event. This will
|
||||
* increment the current focus sequence number. The new value can be gotten
|
||||
* from LastAPZProcessedEvent().
|
||||
*/
|
||||
void ReceiveFocusChangingEvent();
|
||||
|
||||
/**
|
||||
* Update the internal focus tree and recalculate the global focus target for
|
||||
* a focus target update received from chrome or content.
|
||||
*
|
||||
* @param aRootLayerTreeId the layer tree ID of the root layer for the
|
||||
parent APZCTreeManager
|
||||
* @param aOriginatingLayersId the layer tree ID that this focus target
|
||||
belongs to
|
||||
*/
|
||||
void Update(uint64_t aRootLayerTreeId,
|
||||
uint64_t aOriginatingLayersId,
|
||||
const FocusTarget& aTarget);
|
||||
|
||||
/**
|
||||
* Collects a set of the layer tree IDs that we have a focus target for.
|
||||
*/
|
||||
std::unordered_set<uint64_t> GetFocusTargetLayerIds() const;
|
||||
|
||||
/**
|
||||
* Removes a focus target by its layer tree ID.
|
||||
*/
|
||||
void RemoveFocusTarget(uint64_t aLayersId);
|
||||
|
||||
/**
|
||||
* Gets the scrollable layer that should be horizontally scrolled for a key
|
||||
* event, if any. The returned ScrollableLayerGuid doesn't contain a presShellId,
|
||||
* and so it should not be used in comparisons.
|
||||
*
|
||||
* No scrollable layer is returned if any of the following are true:
|
||||
* 1. We don't have a current focus target
|
||||
* 2. There are event listeners that could change the focus
|
||||
* 3. The target has not been layerized
|
||||
*/
|
||||
Maybe<ScrollableLayerGuid> GetHorizontalTarget() const;
|
||||
/**
|
||||
* The same as GetHorizontalTarget() but for vertical scrolling.
|
||||
*/
|
||||
Maybe<ScrollableLayerGuid> GetVerticalTarget() const;
|
||||
|
||||
/**
|
||||
* Gets whether it is safe to not increment the focus sequence number for an
|
||||
* unmatched keyboard event.
|
||||
*/
|
||||
bool CanIgnoreKeyboardShortcutMisses() const
|
||||
{
|
||||
return IsCurrent() && !mFocusHasKeyEventListeners;
|
||||
}
|
||||
|
||||
private:
|
||||
// The set of focus targets received indexed by their layer tree ID
|
||||
std::unordered_map<uint64_t, FocusTarget> mFocusTree;
|
||||
|
||||
// The focus sequence number of the last potentially focus changing event
|
||||
// processed by APZ. This number starts at one and increases monotonically.
|
||||
// We don't worry about wrap around here because at a pace of 100 increments/sec,
|
||||
// it would take 5.85*10^9 years before we would wrap around. This number will
|
||||
// never be zero as that is used to catch uninitialized focus sequence numbers
|
||||
// on input events.
|
||||
uint64_t mLastAPZProcessedEvent;
|
||||
// The focus sequence number last received in a focus update.
|
||||
uint64_t mLastContentProcessedEvent;
|
||||
|
||||
// A flag whether there is a key listener on the event target chain for the
|
||||
// focused element
|
||||
bool mFocusHasKeyEventListeners;
|
||||
|
||||
// The layer tree ID which contains the scrollable frame of the focused element
|
||||
uint64_t mFocusLayersId;
|
||||
// The scrollable layer corresponding to the scrollable frame that is used to
|
||||
// scroll the focused element. This depends on the direction the user is
|
||||
// scrolling.
|
||||
FrameMetrics::ViewID mFocusHorizontalTarget;
|
||||
FrameMetrics::ViewID mFocusVerticalTarget;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_FocusState_h
|
|
@ -0,0 +1,154 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "mozilla/layers/FocusTarget.h"
|
||||
|
||||
#include "mozilla/dom/EventTarget.h" // for EventTarget
|
||||
#include "mozilla/dom/TabParent.h" // for TabParent
|
||||
#include "mozilla/EventDispatcher.h" // for EventDispatcher
|
||||
#include "mozilla/layout/RenderFrameParent.h" // For RenderFrameParent
|
||||
#include "nsIPresShell.h" // for nsIPresShell
|
||||
#include "nsLayoutUtils.h" // for nsLayoutUtils
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::layout;
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
static already_AddRefed<nsIPresShell>
|
||||
GetRetargetEventPresShell(nsIPresShell* aRootPresShell)
|
||||
{
|
||||
MOZ_ASSERT(aRootPresShell);
|
||||
|
||||
// Use the last focused window in this PresShell and its
|
||||
// associated PresShell
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window =
|
||||
aRootPresShell->GetFocusedDOMWindowInOurWindow();
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> retargetEventDoc = window->GetExtantDoc();
|
||||
if (!retargetEventDoc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
|
||||
return presShell.forget();
|
||||
}
|
||||
|
||||
static bool
|
||||
HasListenersForKeyEvents(nsIContent* aContent)
|
||||
{
|
||||
if (!aContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WidgetEvent event(true, eVoidEvent);
|
||||
nsTArray<EventTarget*> targets;
|
||||
nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
|
||||
nullptr, nullptr, &targets);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
for (size_t i = 0; i < targets.Length(); i++) {
|
||||
if (targets[i]->HasUntrustedOrNonSystemGroupKeyEventListeners()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsEditableNode(nsINode* aNode)
|
||||
{
|
||||
return aNode && aNode->IsEditable();
|
||||
}
|
||||
|
||||
FocusTarget::FocusTarget()
|
||||
: mSequenceNumber(0)
|
||||
, mFocusHasKeyEventListeners(false)
|
||||
, mType(FocusTarget::eNone)
|
||||
{
|
||||
}
|
||||
|
||||
FocusTarget::FocusTarget(nsIPresShell* aRootPresShell,
|
||||
uint64_t aFocusSequenceNumber)
|
||||
: mSequenceNumber(aFocusSequenceNumber)
|
||||
, mFocusHasKeyEventListeners(false)
|
||||
{
|
||||
MOZ_ASSERT(aRootPresShell);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Key events can be retargeted to a child PresShell when there is an iframe
|
||||
nsCOMPtr<nsIPresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
|
||||
|
||||
// Get the content that should be scrolled for this PresShell, which is
|
||||
// the current focused element or the current DOM selection
|
||||
nsCOMPtr<nsIContent> scrollTarget = presShell->GetContentForScrolling();
|
||||
|
||||
// Collect event listener information so we can track what is potentially focus
|
||||
// changing
|
||||
mFocusHasKeyEventListeners = HasListenersForKeyEvents(scrollTarget);
|
||||
|
||||
// Check if the scroll target is a remote browser
|
||||
if (TabParent* browserParent = TabParent::GetFrom(scrollTarget)) {
|
||||
RenderFrameParent* rfp = browserParent->GetRenderFrame();
|
||||
|
||||
// The globally focused element for scrolling is in a remote layer tree
|
||||
if (rfp) {
|
||||
mType = FocusTarget::eRefLayer;
|
||||
mData.mRefLayerId = rfp->GetLayersId();
|
||||
return;
|
||||
}
|
||||
|
||||
mType = FocusTarget::eNone;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the focus isn't on a remote browser then check for scrollable targets
|
||||
if (IsEditableNode(scrollTarget) ||
|
||||
IsEditableNode(presShell->GetDocument())) {
|
||||
mType = FocusTarget::eNone;
|
||||
return;
|
||||
}
|
||||
|
||||
// Gather the scrollable frames that would be scrolled in each direction
|
||||
// for this scroll target
|
||||
nsIScrollableFrame* horizontal =
|
||||
presShell->GetScrollableFrameToScrollForContent(scrollTarget.get(),
|
||||
nsIPresShell::eHorizontal);
|
||||
nsIScrollableFrame* vertical =
|
||||
presShell->GetScrollableFrameToScrollForContent(scrollTarget.get(),
|
||||
nsIPresShell::eVertical);
|
||||
|
||||
// We might have the globally focused element for scrolling. Gather a ViewID for
|
||||
// the horizontal and vertical scroll targets of this element.
|
||||
mType = FocusTarget::eScrollLayer;
|
||||
mData.mScrollTargets.mHorizontal =
|
||||
nsLayoutUtils::FindIDForScrollableFrame(horizontal);
|
||||
mData.mScrollTargets.mVertical =
|
||||
nsLayoutUtils::FindIDForScrollableFrame(vertical);
|
||||
}
|
||||
|
||||
bool
|
||||
FocusTarget::operator==(const FocusTarget& aRhs) const
|
||||
{
|
||||
if (mSequenceNumber != aRhs.mSequenceNumber ||
|
||||
mFocusHasKeyEventListeners != aRhs.mFocusHasKeyEventListeners ||
|
||||
mType != aRhs.mType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mType == FocusTarget::eRefLayer) {
|
||||
return mData.mRefLayerId == aRhs.mData.mRefLayerId;
|
||||
} else if (mType == FocusTarget::eScrollLayer) {
|
||||
return mData.mScrollTargets.mHorizontal == aRhs.mData.mScrollTargets.mHorizontal &&
|
||||
mData.mScrollTargets.mVertical == aRhs.mData.mScrollTargets.mVertical;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,73 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef mozilla_layers_FocusTarget_h
|
||||
#define mozilla_layers_FocusTarget_h
|
||||
|
||||
#include <stdint.h> // for int32_t, uint32_t
|
||||
|
||||
#include "FrameMetrics.h" // for FrameMetrics::ViewID
|
||||
|
||||
class nsIPresShell;
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* This class is used for communicating information about the currently focused
|
||||
* element of a document and the scrollable frames to use when keyboard scrolling
|
||||
* it. It is created on the main thread at paint-time, but is then passed over
|
||||
* IPC to the compositor/APZ code.
|
||||
*/
|
||||
class FocusTarget final
|
||||
{
|
||||
public:
|
||||
struct ScrollTargets
|
||||
{
|
||||
FrameMetrics::ViewID mHorizontal;
|
||||
FrameMetrics::ViewID mVertical;
|
||||
};
|
||||
|
||||
enum FocusTargetType
|
||||
{
|
||||
eNone,
|
||||
eRefLayer,
|
||||
eScrollLayer,
|
||||
|
||||
// Used as an upper bound for ContiguousEnumSerializer
|
||||
eSentinel,
|
||||
};
|
||||
union FocusTargetData
|
||||
{
|
||||
uint64_t mRefLayerId;
|
||||
ScrollTargets mScrollTargets;
|
||||
};
|
||||
|
||||
FocusTarget();
|
||||
|
||||
/**
|
||||
* Construct a focus target for the specified top level PresShell
|
||||
*/
|
||||
FocusTarget(nsIPresShell* aRootPresShell,
|
||||
uint64_t aFocusSequenceNumber);
|
||||
|
||||
bool operator==(const FocusTarget& aRhs) const;
|
||||
|
||||
public:
|
||||
// The content sequence number recorded at the time of this class's creation
|
||||
uint64_t mSequenceNumber;
|
||||
|
||||
// Whether there are keydown, keypress, or keyup event listeners
|
||||
// in the event target chain of the focused element
|
||||
bool mFocusHasKeyEventListeners;
|
||||
|
||||
FocusTargetType mType;
|
||||
FocusTargetData mData;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_FocusTarget_h
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#include "GenericScrollAnimation.h"
|
||||
|
||||
#include "AsyncPanZoomController.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
GenericScrollAnimation::GenericScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint& aInitialPosition)
|
||||
: AsyncScrollBase(aInitialPosition)
|
||||
, mApzc(aApzc)
|
||||
, mFinalDestination(aInitialPosition)
|
||||
, mForceVerticalOverscroll(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GenericScrollAnimation::UpdateDelta(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity)
|
||||
{
|
||||
mFinalDestination += aDelta;
|
||||
|
||||
Update(aTime, aCurrentVelocity);
|
||||
}
|
||||
|
||||
void
|
||||
GenericScrollAnimation::UpdateDestination(TimeStamp aTime, nsPoint aDestination, const nsSize& aCurrentVelocity)
|
||||
{
|
||||
mFinalDestination = aDestination;
|
||||
|
||||
Update(aTime, aCurrentVelocity);
|
||||
}
|
||||
|
||||
void
|
||||
GenericScrollAnimation::Update(TimeStamp aTime, const nsSize& aCurrentVelocity)
|
||||
{
|
||||
if (mIsFirstIteration) {
|
||||
InitializeHistory(aTime);
|
||||
}
|
||||
|
||||
// Clamp the final destination to the scrollable area.
|
||||
CSSPoint clamped = CSSPoint::FromAppUnits(mFinalDestination);
|
||||
clamped.x = mApzc.mX.ClampOriginToScrollableRect(clamped.x);
|
||||
clamped.y = mApzc.mY.ClampOriginToScrollableRect(clamped.y);
|
||||
mFinalDestination = CSSPoint::ToAppUnits(clamped);
|
||||
|
||||
AsyncScrollBase::Update(aTime, mFinalDestination, aCurrentVelocity);
|
||||
}
|
||||
|
||||
bool
|
||||
GenericScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta)
|
||||
{
|
||||
TimeStamp now = mApzc.GetFrameTime();
|
||||
CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
|
||||
|
||||
// If the animation is finished, make sure the final position is correct by
|
||||
// using one last displacement. Otherwise, compute the delta via the timing
|
||||
// function as normal.
|
||||
bool finished = IsFinished(now);
|
||||
nsPoint sampledDest = finished
|
||||
? mDestination
|
||||
: PositionAt(now);
|
||||
ParentLayerPoint displacement =
|
||||
(CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom;
|
||||
|
||||
if (finished) {
|
||||
mApzc.mX.SetVelocity(0);
|
||||
mApzc.mY.SetVelocity(0);
|
||||
} else if (!IsZero(displacement)) {
|
||||
// Velocity is measured in ParentLayerCoords / Milliseconds
|
||||
float xVelocity = displacement.x / aDelta.ToMilliseconds();
|
||||
float yVelocity = displacement.y / aDelta.ToMilliseconds();
|
||||
mApzc.mX.SetVelocity(xVelocity);
|
||||
mApzc.mY.SetVelocity(yVelocity);
|
||||
}
|
||||
|
||||
// Note: we ignore overscroll for generic animations.
|
||||
ParentLayerPoint adjustedOffset, overscroll;
|
||||
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
|
||||
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
|
||||
mForceVerticalOverscroll);
|
||||
|
||||
// If we expected to scroll, but there's no more scroll range on either axis,
|
||||
// then end the animation early. Note that the initial displacement could be 0
|
||||
// if the compositor ran very quickly (<1ms) after the animation was created.
|
||||
// When that happens we want to make sure the animation continues.
|
||||
if (!IsZero(displacement) && IsZero(adjustedOffset)) {
|
||||
// Nothing more to do - end the animation.
|
||||
return false;
|
||||
}
|
||||
|
||||
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
|
||||
return !finished;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_layers_GenericScrollAnimation_h_
|
||||
#define mozilla_layers_GenericScrollAnimation_h_
|
||||
|
||||
#include "AsyncPanZoomAnimation.h"
|
||||
#include "AsyncScrollBase.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class AsyncPanZoomController;
|
||||
|
||||
class GenericScrollAnimation
|
||||
: public AsyncPanZoomAnimation,
|
||||
public AsyncScrollBase
|
||||
{
|
||||
public:
|
||||
GenericScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint& aInitialPosition);
|
||||
|
||||
bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
|
||||
|
||||
void UpdateDelta(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity);
|
||||
void UpdateDestination(TimeStamp aTime, nsPoint aDestination, const nsSize& aCurrentVelocity);
|
||||
|
||||
CSSPoint GetDestination() const {
|
||||
return CSSPoint::FromAppUnits(mFinalDestination);
|
||||
}
|
||||
|
||||
private:
|
||||
void Update(TimeStamp aTime, const nsSize& aCurrentVelocity);
|
||||
|
||||
protected:
|
||||
AsyncPanZoomController& mApzc;
|
||||
nsPoint mFinalDestination;
|
||||
bool mForceVerticalOverscroll;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_GenericScrollAnimation_h_
|
|
@ -870,5 +870,10 @@ TouchBlockState::GetActiveTouchCount() const
|
|||
return mTouchCounter.GetActiveTouchCount();
|
||||
}
|
||||
|
||||
KeyboardBlockState::KeyboardBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc)
|
||||
: InputBlockState(aTargetApzc, true)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -27,6 +27,7 @@ class TouchBlockState;
|
|||
class WheelBlockState;
|
||||
class DragBlockState;
|
||||
class PanGestureBlockState;
|
||||
class KeyboardBlockState;
|
||||
|
||||
/**
|
||||
* A base class that stores state common to various input blocks.
|
||||
|
@ -68,6 +69,9 @@ public:
|
|||
virtual PanGestureBlockState* AsPanGestureBlock() {
|
||||
return nullptr;
|
||||
}
|
||||
virtual KeyboardBlockState* AsKeyboardBlock() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
|
||||
TargetConfirmationState aState,
|
||||
|
@ -486,6 +490,23 @@ private:
|
|||
TouchCounter& mTouchCounter;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class represents a set of keyboard inputs targeted at the same Apzc.
|
||||
*/
|
||||
class KeyboardBlockState : public InputBlockState
|
||||
{
|
||||
public:
|
||||
explicit KeyboardBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc);
|
||||
|
||||
KeyboardBlockState* AsKeyboardBlock() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool MustStayActive() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -56,6 +56,14 @@ InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
|
|||
return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
|
||||
}
|
||||
|
||||
case KEYBOARD_INPUT: {
|
||||
// Every keyboard input must have a confirmed target
|
||||
MOZ_ASSERT(aTarget && aTargetConfirmed);
|
||||
|
||||
const KeyboardInput& event = aEvent.AsKeyboardInput();
|
||||
return ReceiveKeyboardInput(aTarget, event, aOutInputBlockId);
|
||||
}
|
||||
|
||||
default:
|
||||
// The return value for non-touch input is only used by tests, so just pass
|
||||
// through the return value for now. This can be changed later if needed.
|
||||
|
@ -268,6 +276,39 @@ InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarge
|
|||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
InputQueue::ReceiveKeyboardInput(const RefPtr<AsyncPanZoomController>& aTarget,
|
||||
const KeyboardInput& aEvent,
|
||||
uint64_t* aOutInputBlockId) {
|
||||
KeyboardBlockState* block = mActiveKeyboardBlock.get();
|
||||
|
||||
// If the block is targeting a different Apzc than this keyboard event then
|
||||
// we'll create a new input block
|
||||
if (block && block->GetTargetApzc() != aTarget) {
|
||||
block = nullptr;
|
||||
}
|
||||
|
||||
if (!block) {
|
||||
block = new KeyboardBlockState(aTarget);
|
||||
INPQ_LOG("started new keyboard block %p id %" PRIu64 " for target %p\n",
|
||||
block, block->GetBlockId(), aTarget.get());
|
||||
|
||||
mActiveKeyboardBlock = block;
|
||||
} else {
|
||||
INPQ_LOG("received new event in block %p\n", block);
|
||||
}
|
||||
|
||||
if (aOutInputBlockId) {
|
||||
*aOutInputBlockId = block->GetBlockId();
|
||||
}
|
||||
|
||||
mQueuedInputs.AppendElement(MakeUnique<QueuedInput>(aEvent, *block));
|
||||
|
||||
ProcessQueue();
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
|
||||
PanGestureBlockState* aBlock)
|
||||
|
@ -461,6 +502,13 @@ InputQueue::GetCurrentPanGestureBlock() const
|
|||
return block ? block->AsPanGestureBlock() : mActivePanGestureBlock.get();
|
||||
}
|
||||
|
||||
KeyboardBlockState*
|
||||
InputQueue::GetCurrentKeyboardBlock() const
|
||||
{
|
||||
InputBlockState* block = GetCurrentBlock();
|
||||
return block ? block->AsKeyboardBlock() : mActiveKeyboardBlock.get();
|
||||
}
|
||||
|
||||
WheelBlockState*
|
||||
InputQueue::GetActiveWheelTransaction() const
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@ class TouchBlockState;
|
|||
class WheelBlockState;
|
||||
class DragBlockState;
|
||||
class PanGestureBlockState;
|
||||
class KeyboardBlockState;
|
||||
class AsyncDragMetrics;
|
||||
class QueuedInput;
|
||||
|
||||
|
@ -106,6 +107,7 @@ public:
|
|||
WheelBlockState* GetCurrentWheelBlock() const;
|
||||
DragBlockState* GetCurrentDragBlock() const;
|
||||
PanGestureBlockState* GetCurrentPanGestureBlock() const;
|
||||
KeyboardBlockState* GetCurrentKeyboardBlock() const;
|
||||
/**
|
||||
* Returns true iff the pending block at the head of the queue is a touch
|
||||
* block and is ready for handling.
|
||||
|
@ -169,6 +171,9 @@ private:
|
|||
bool aTargetConfirmed,
|
||||
const PanGestureInput& aEvent,
|
||||
uint64_t* aOutInputBlockId);
|
||||
nsEventStatus ReceiveKeyboardInput(const RefPtr<AsyncPanZoomController>& aTarget,
|
||||
const KeyboardInput& aEvent,
|
||||
uint64_t* aOutInputBlockId);
|
||||
|
||||
/**
|
||||
* Helper function that searches mQueuedInputs for the first block matching
|
||||
|
@ -202,6 +207,7 @@ private:
|
|||
RefPtr<WheelBlockState> mActiveWheelBlock;
|
||||
RefPtr<DragBlockState> mActiveDragBlock;
|
||||
RefPtr<PanGestureBlockState> mActivePanGestureBlock;
|
||||
RefPtr<KeyboardBlockState> mActiveKeyboardBlock;
|
||||
|
||||
// The APZC to which the last event was delivered
|
||||
RefPtr<AsyncPanZoomController> mLastActiveApzc;
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "mozilla/layers/KeyboardMap.h"
|
||||
|
||||
#include "mozilla/TextEvents.h" // for IgnoreModifierState, ShortcutKeyCandidate
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
KeyboardShortcut::KeyboardShortcut()
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
|
||||
uint32_t aKeyCode,
|
||||
uint32_t aCharCode,
|
||||
Modifiers aModifiers,
|
||||
Modifiers aModifiersMask,
|
||||
const KeyboardScrollAction& aAction)
|
||||
: mAction(aAction)
|
||||
, mKeyCode(aKeyCode)
|
||||
, mCharCode(aCharCode)
|
||||
, mModifiers(aModifiers)
|
||||
, mModifiersMask(aModifiersMask)
|
||||
, mEventType(aEventType)
|
||||
, mDispatchToContent(false)
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
|
||||
uint32_t aKeyCode,
|
||||
uint32_t aCharCode,
|
||||
Modifiers aModifiers,
|
||||
Modifiers aModifiersMask)
|
||||
: mKeyCode(aKeyCode)
|
||||
, mCharCode(aCharCode)
|
||||
, mModifiers(aModifiers)
|
||||
, mModifiersMask(aModifiersMask)
|
||||
, mEventType(aEventType)
|
||||
, mDispatchToContent(true)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
KeyboardShortcut::Matches(const KeyboardInput& aInput,
|
||||
const IgnoreModifierState& aIgnore,
|
||||
uint32_t aOverrideCharCode) const
|
||||
{
|
||||
return mEventType == aInput.mType &&
|
||||
MatchesKey(aInput, aOverrideCharCode) &&
|
||||
MatchesModifiers(aInput, aIgnore);
|
||||
}
|
||||
|
||||
bool
|
||||
KeyboardShortcut::MatchesKey(const KeyboardInput& aInput,
|
||||
uint32_t aOverrideCharCode) const
|
||||
{
|
||||
// Compare by the key code if we have one
|
||||
if (!mCharCode) {
|
||||
return mKeyCode == aInput.mKeyCode;
|
||||
}
|
||||
|
||||
// We are comparing by char code
|
||||
uint32_t charCode;
|
||||
|
||||
// If we are comparing against a shortcut candidate then we might
|
||||
// have an override char code
|
||||
if (aOverrideCharCode) {
|
||||
charCode = aOverrideCharCode;
|
||||
} else {
|
||||
charCode = aInput.mCharCode;
|
||||
}
|
||||
|
||||
// Both char codes must be in lowercase to compare correctly
|
||||
if (IS_IN_BMP(charCode)) {
|
||||
charCode = ToLowerCase(static_cast<char16_t>(charCode));
|
||||
}
|
||||
|
||||
return mCharCode == charCode;
|
||||
}
|
||||
|
||||
bool
|
||||
KeyboardShortcut::MatchesModifiers(const KeyboardInput& aInput,
|
||||
const IgnoreModifierState& aIgnore) const
|
||||
{
|
||||
Modifiers modifiersMask = mModifiersMask;
|
||||
|
||||
// If we are ignoring Shift or OS, then unset that part of the mask
|
||||
if (aIgnore.mOS) {
|
||||
modifiersMask &= ~MODIFIER_OS;
|
||||
}
|
||||
if (aIgnore.mShift) {
|
||||
modifiersMask &= ~MODIFIER_SHIFT;
|
||||
}
|
||||
|
||||
// Mask off the modifiers we are ignoring from the keyboard input
|
||||
return (aInput.modifiers & modifiersMask) == mModifiers;
|
||||
}
|
||||
|
||||
KeyboardMap::KeyboardMap(nsTArray<KeyboardShortcut>&& aShortcuts)
|
||||
: mShortcuts(aShortcuts)
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardMap::KeyboardMap()
|
||||
{
|
||||
}
|
||||
|
||||
Maybe<KeyboardShortcut>
|
||||
KeyboardMap::FindMatch(const KeyboardInput& aEvent) const
|
||||
{
|
||||
// If there are no shortcut candidates, then just search with with the
|
||||
// keyboard input
|
||||
if (aEvent.mShortcutCandidates.IsEmpty()) {
|
||||
return FindMatchInternal(aEvent, IgnoreModifierState());
|
||||
}
|
||||
|
||||
// Otherwise do a search with each shortcut candidate in order
|
||||
for (auto& key : aEvent.mShortcutCandidates) {
|
||||
IgnoreModifierState ignoreModifierState;
|
||||
ignoreModifierState.mShift = key.mIgnoreShift;
|
||||
|
||||
auto match = FindMatchInternal(aEvent, ignoreModifierState, key.mCharCode);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
Maybe<KeyboardShortcut>
|
||||
KeyboardMap::FindMatchInternal(const KeyboardInput& aEvent,
|
||||
const IgnoreModifierState& aIgnore,
|
||||
uint32_t aOverrideCharCode) const
|
||||
{
|
||||
for (auto& shortcut : mShortcuts) {
|
||||
if (shortcut.Matches(aEvent, aIgnore, aOverrideCharCode)) {
|
||||
return Some(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Windows native applications ignore Windows-Logo key state when checking
|
||||
// shortcut keys even if the key is pressed. Therefore, if there is no
|
||||
// shortcut key which exactly matches current modifier state, we should
|
||||
// retry to look for a shortcut key without the Windows-Logo key press.
|
||||
if (!aIgnore.mOS && (aEvent.modifiers & MODIFIER_OS)) {
|
||||
IgnoreModifierState ignoreModifierState(aIgnore);
|
||||
ignoreModifierState.mOS = true;
|
||||
return FindMatchInternal(aEvent, ignoreModifierState, aOverrideCharCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef mozilla_layers_KeyboardMap_h
|
||||
#define mozilla_layers_KeyboardMap_h
|
||||
|
||||
#include <stdint.h> // for uint32_t
|
||||
|
||||
#include "InputData.h" // for KeyboardInput
|
||||
#include "nsIScrollableFrame.h" // for nsIScrollableFrame::ScrollUnit
|
||||
#include "nsTArray.h" // for nsTArray
|
||||
#include "mozilla/Maybe.h" // for mozilla::Maybe
|
||||
#include "KeyboardScrollAction.h" // for KeyboardScrollAction
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct IgnoreModifierState;
|
||||
|
||||
namespace layers {
|
||||
|
||||
class KeyboardMap;
|
||||
|
||||
/**
|
||||
* This class is an off main-thread <xul:handler> for scrolling commands.
|
||||
*/
|
||||
class KeyboardShortcut final
|
||||
{
|
||||
public:
|
||||
KeyboardShortcut();
|
||||
|
||||
/**
|
||||
* Create a keyboard shortcut that when matched can be handled by executing
|
||||
* the specified keyboard action.
|
||||
*/
|
||||
KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
|
||||
uint32_t aKeyCode,
|
||||
uint32_t aCharCode,
|
||||
Modifiers aModifiers,
|
||||
Modifiers aModifiersMask,
|
||||
const KeyboardScrollAction& aAction);
|
||||
|
||||
/**
|
||||
* Create a keyboard shortcut that when matched should be handled by ignoring
|
||||
* the keyboard event and dispatching it to content.
|
||||
*/
|
||||
KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType,
|
||||
uint32_t aKeyCode,
|
||||
uint32_t aCharCode,
|
||||
Modifiers aModifiers,
|
||||
Modifiers aModifiersMask);
|
||||
|
||||
protected:
|
||||
friend mozilla::layers::KeyboardMap;
|
||||
|
||||
bool Matches(const KeyboardInput& aInput,
|
||||
const IgnoreModifierState& aIgnore,
|
||||
uint32_t aOverrideCharCode = 0) const;
|
||||
|
||||
private:
|
||||
bool MatchesKey(const KeyboardInput& aInput,
|
||||
uint32_t aOverrideCharCode) const;
|
||||
bool MatchesModifiers(const KeyboardInput& aInput,
|
||||
const IgnoreModifierState& aIgnore) const;
|
||||
|
||||
public:
|
||||
// The action to perform when this shortcut is matched,
|
||||
// and not flagged to be dispatched to content
|
||||
KeyboardScrollAction mAction;
|
||||
|
||||
// Only one of mKeyCode or mCharCode may be non-zero
|
||||
// whichever one is non-zero is the one to compare when matching
|
||||
uint32_t mKeyCode;
|
||||
uint32_t mCharCode;
|
||||
|
||||
// The modifiers that must be active for this shortcut
|
||||
Modifiers mModifiers;
|
||||
// The modifiers to compare when matching this shortcut
|
||||
Modifiers mModifiersMask;
|
||||
|
||||
// The type of keyboard event to match against
|
||||
KeyboardInput::KeyboardEventType mEventType;
|
||||
|
||||
// Whether events matched by this must be dispatched to content
|
||||
bool mDispatchToContent;
|
||||
};
|
||||
|
||||
/**
|
||||
* A keyboard map is an off main-thread <xul:binding> for scrolling commands.
|
||||
*/
|
||||
class KeyboardMap final
|
||||
{
|
||||
public:
|
||||
KeyboardMap();
|
||||
explicit KeyboardMap(nsTArray<KeyboardShortcut>&& aShortcuts);
|
||||
|
||||
const nsTArray<KeyboardShortcut>& Shortcuts() const { return mShortcuts; }
|
||||
|
||||
/**
|
||||
* Search through the internal list of shortcuts for a match for the input event
|
||||
*/
|
||||
Maybe<KeyboardShortcut> FindMatch(const KeyboardInput& aEvent) const;
|
||||
|
||||
private:
|
||||
Maybe<KeyboardShortcut> FindMatchInternal(const KeyboardInput& aEvent,
|
||||
const IgnoreModifierState& aIgnore,
|
||||
uint32_t aOverrideCharCode = 0) const;
|
||||
|
||||
nsTArray<KeyboardShortcut> mShortcuts;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_KeyboardMap_h
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#include "mozilla/layers/KeyboardScrollAction.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/* static */ nsIScrollableFrame::ScrollUnit
|
||||
KeyboardScrollAction::GetScrollUnit(KeyboardScrollAction::KeyboardScrollActionType aDeltaType)
|
||||
{
|
||||
switch (aDeltaType) {
|
||||
case KeyboardScrollAction::eScrollCharacter:
|
||||
return nsIScrollableFrame::LINES;
|
||||
case KeyboardScrollAction::eScrollLine:
|
||||
return nsIScrollableFrame::LINES;
|
||||
case KeyboardScrollAction::eScrollPage:
|
||||
return nsIScrollableFrame::PAGES;
|
||||
case KeyboardScrollAction::eScrollComplete:
|
||||
return nsIScrollableFrame::WHOLE;
|
||||
case KeyboardScrollAction::eSentinel:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid KeyboardScrollActionType.");
|
||||
return nsIScrollableFrame::WHOLE;
|
||||
}
|
||||
|
||||
// Silence an overzealous warning
|
||||
return nsIScrollableFrame::WHOLE;
|
||||
}
|
||||
|
||||
KeyboardScrollAction::KeyboardScrollAction()
|
||||
: mType(KeyboardScrollAction::eScrollCharacter)
|
||||
, mForward(false)
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardScrollAction::KeyboardScrollAction(KeyboardScrollActionType aType, bool aForward)
|
||||
: mType(aType)
|
||||
, mForward(aForward)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
#ifndef mozilla_layers_KeyboardScrollAction_h
|
||||
#define mozilla_layers_KeyboardScrollAction_h
|
||||
|
||||
#include "nsIScrollableFrame.h" // for nsIScrollableFrame::ScrollUnit
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* This class represents a scrolling action to be performed on a scrollable layer.
|
||||
*/
|
||||
struct KeyboardScrollAction final
|
||||
{
|
||||
public:
|
||||
enum KeyboardScrollActionType : uint8_t
|
||||
{
|
||||
eScrollCharacter,
|
||||
eScrollLine,
|
||||
eScrollPage,
|
||||
eScrollComplete,
|
||||
|
||||
// Used as an upper bound for ContiguousEnumSerializer
|
||||
eSentinel,
|
||||
};
|
||||
|
||||
static nsIScrollableFrame::ScrollUnit
|
||||
GetScrollUnit(KeyboardScrollActionType aDeltaType);
|
||||
|
||||
KeyboardScrollAction();
|
||||
KeyboardScrollAction(KeyboardScrollActionType aType, bool aForward);
|
||||
|
||||
// The type of scroll to perform for this action
|
||||
KeyboardScrollActionType mType;
|
||||
// Whether to scroll forward or backward along the axis of this action type
|
||||
bool mForward;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_KeyboardScrollAction_h
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#include "KeyboardScrollAnimation.h"
|
||||
|
||||
#include "gfxPrefs.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
KeyboardScrollAnimation::KeyboardScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint& aInitialPosition,
|
||||
KeyboardScrollAction::KeyboardScrollActionType aType)
|
||||
: GenericScrollAnimation(aApzc, aInitialPosition)
|
||||
{
|
||||
switch (aType) {
|
||||
case KeyboardScrollAction::eScrollCharacter:
|
||||
case KeyboardScrollAction::eScrollLine: {
|
||||
mOriginMaxMS = clamped(gfxPrefs::LineSmoothScrollMaxDurationMs(), 0, 10000);
|
||||
mOriginMinMS = clamped(gfxPrefs::LineSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollPage: {
|
||||
mOriginMaxMS = clamped(gfxPrefs::PageSmoothScrollMaxDurationMs(), 0, 10000);
|
||||
mOriginMinMS = clamped(gfxPrefs::PageSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eScrollComplete: {
|
||||
mOriginMaxMS = clamped(gfxPrefs::OtherSmoothScrollMaxDurationMs(), 0, 10000);
|
||||
mOriginMinMS = clamped(gfxPrefs::OtherSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
|
||||
break;
|
||||
}
|
||||
case KeyboardScrollAction::eSentinel: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid KeyboardScrollActionType.");
|
||||
}
|
||||
}
|
||||
|
||||
// The pref is 100-based int percentage, while mIntervalRatio is 1-based ratio
|
||||
mIntervalRatio = ((double)gfxPrefs::SmoothScrollDurationToIntervalRatio()) / 100.0;
|
||||
mIntervalRatio = std::max(1.0, mIntervalRatio);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_layers_KeyboardScrollAnimation_h_
|
||||
#define mozilla_layers_KeyboardScrollAnimation_h_
|
||||
|
||||
#include "GenericScrollAnimation.h"
|
||||
#include "mozilla/layers/KeyboardMap.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class AsyncPanZoomController;
|
||||
|
||||
class KeyboardScrollAnimation
|
||||
: public GenericScrollAnimation
|
||||
{
|
||||
public:
|
||||
KeyboardScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint& aInitialPosition,
|
||||
KeyboardScrollAction::KeyboardScrollActionType aType);
|
||||
|
||||
KeyboardScrollAnimation* AsKeyboardScrollAnimation() override {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_KeyboardScrollAnimation_h_
|
|
@ -38,6 +38,12 @@ QueuedInput::QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aB
|
|||
{
|
||||
}
|
||||
|
||||
QueuedInput::QueuedInput(const KeyboardInput& aInput, KeyboardBlockState& aBlock)
|
||||
: mInput(MakeUnique<KeyboardInput>(aInput))
|
||||
, mBlock(&aBlock)
|
||||
{
|
||||
}
|
||||
|
||||
InputData*
|
||||
QueuedInput::Input()
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ class MultiTouchInput;
|
|||
class ScrollWheelInput;
|
||||
class MouseInput;
|
||||
class PanGestureInput;
|
||||
class KeyboardInput;
|
||||
|
||||
namespace layers {
|
||||
|
||||
|
@ -25,6 +26,7 @@ class TouchBlockState;
|
|||
class WheelBlockState;
|
||||
class DragBlockState;
|
||||
class PanGestureBlockState;
|
||||
class KeyboardBlockState;
|
||||
|
||||
/**
|
||||
* This lightweight class holds a pointer to an input event that has not yet
|
||||
|
@ -38,6 +40,7 @@ public:
|
|||
QueuedInput(const ScrollWheelInput& aInput, WheelBlockState& aBlock);
|
||||
QueuedInput(const MouseInput& aInput, DragBlockState& aBlock);
|
||||
QueuedInput(const PanGestureInput& aInput, PanGestureBlockState& aBlock);
|
||||
QueuedInput(const KeyboardInput& aInput, KeyboardBlockState& aBlock);
|
||||
|
||||
InputData* Input();
|
||||
InputBlockState* Block();
|
||||
|
|
|
@ -16,83 +16,11 @@ namespace layers {
|
|||
WheelScrollAnimation::WheelScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint& aInitialPosition,
|
||||
ScrollWheelInput::ScrollDeltaType aDeltaType)
|
||||
: AsyncScrollBase(aInitialPosition)
|
||||
, mApzc(aApzc)
|
||||
, mFinalDestination(aInitialPosition)
|
||||
, mDeltaType(aDeltaType)
|
||||
: GenericScrollAnimation(aApzc, aInitialPosition)
|
||||
{
|
||||
}
|
||||
mForceVerticalOverscroll = !mApzc.mScrollMetadata.AllowVerticalScrollWithWheel();
|
||||
|
||||
void
|
||||
WheelScrollAnimation::Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity)
|
||||
{
|
||||
InitPreferences(aTime);
|
||||
|
||||
mFinalDestination += aDelta;
|
||||
|
||||
// Clamp the final destination to the scrollable area.
|
||||
CSSPoint clamped = CSSPoint::FromAppUnits(mFinalDestination);
|
||||
clamped.x = mApzc.mX.ClampOriginToScrollableRect(clamped.x);
|
||||
clamped.y = mApzc.mY.ClampOriginToScrollableRect(clamped.y);
|
||||
mFinalDestination = CSSPoint::ToAppUnits(clamped);
|
||||
|
||||
AsyncScrollBase::Update(aTime, mFinalDestination, aCurrentVelocity);
|
||||
}
|
||||
|
||||
bool
|
||||
WheelScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta)
|
||||
{
|
||||
TimeStamp now = mApzc.GetFrameTime();
|
||||
CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
|
||||
|
||||
// If the animation is finished, make sure the final position is correct by
|
||||
// using one last displacement. Otherwise, compute the delta via the timing
|
||||
// function as normal.
|
||||
bool finished = IsFinished(now);
|
||||
nsPoint sampledDest = finished
|
||||
? mDestination
|
||||
: PositionAt(now);
|
||||
ParentLayerPoint displacement =
|
||||
(CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom;
|
||||
|
||||
if (finished) {
|
||||
mApzc.mX.SetVelocity(0);
|
||||
mApzc.mY.SetVelocity(0);
|
||||
} else if (!IsZero(displacement)) {
|
||||
// Velocity is measured in ParentLayerCoords / Milliseconds
|
||||
float xVelocity = displacement.x / aDelta.ToMilliseconds();
|
||||
float yVelocity = displacement.y / aDelta.ToMilliseconds();
|
||||
mApzc.mX.SetVelocity(xVelocity);
|
||||
mApzc.mY.SetVelocity(yVelocity);
|
||||
}
|
||||
|
||||
// Note: we ignore overscroll for wheel animations.
|
||||
ParentLayerPoint adjustedOffset, overscroll;
|
||||
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
|
||||
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
|
||||
!mApzc.mScrollMetadata.AllowVerticalScrollWithWheel());
|
||||
|
||||
// If we expected to scroll, but there's no more scroll range on either axis,
|
||||
// then end the animation early. Note that the initial displacement could be 0
|
||||
// if the compositor ran very quickly (<1ms) after the animation was created.
|
||||
// When that happens we want to make sure the animation continues.
|
||||
if (!IsZero(displacement) && IsZero(adjustedOffset)) {
|
||||
// Nothing more to do - end the animation.
|
||||
return false;
|
||||
}
|
||||
|
||||
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
|
||||
return !finished;
|
||||
}
|
||||
|
||||
void
|
||||
WheelScrollAnimation::InitPreferences(TimeStamp aTime)
|
||||
{
|
||||
if (!mIsFirstIteration) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mDeltaType) {
|
||||
switch (aDeltaType) {
|
||||
case ScrollWheelInput::SCROLLDELTA_PAGE:
|
||||
mOriginMaxMS = clamped(gfxPrefs::PageSmoothScrollMaxDurationMs(), 0, 10000);
|
||||
mOriginMinMS = clamped(gfxPrefs::PageSmoothScrollMinDurationMs(), 0, mOriginMaxMS);
|
||||
|
@ -112,8 +40,6 @@ WheelScrollAnimation::InitPreferences(TimeStamp aTime)
|
|||
// The pref is 100-based int percentage, while mIntervalRatio is 1-based ratio
|
||||
mIntervalRatio = ((double)gfxPrefs::SmoothScrollDurationToIntervalRatio()) / 100.0;
|
||||
mIntervalRatio = std::max(1.0, mIntervalRatio);
|
||||
|
||||
InitializeHistory(aTime);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
#ifndef mozilla_layers_WheelScrollAnimation_h_
|
||||
#define mozilla_layers_WheelScrollAnimation_h_
|
||||
|
||||
#include "AsyncPanZoomAnimation.h"
|
||||
#include "AsyncScrollBase.h"
|
||||
#include "GenericScrollAnimation.h"
|
||||
#include "InputData.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -17,32 +16,16 @@ namespace layers {
|
|||
class AsyncPanZoomController;
|
||||
|
||||
class WheelScrollAnimation
|
||||
: public AsyncPanZoomAnimation,
|
||||
public AsyncScrollBase
|
||||
: public GenericScrollAnimation
|
||||
{
|
||||
public:
|
||||
WheelScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint& aInitialPosition,
|
||||
ScrollWheelInput::ScrollDeltaType aDeltaType);
|
||||
|
||||
bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
|
||||
void Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity);
|
||||
|
||||
WheelScrollAnimation* AsWheelScrollAnimation() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
CSSPoint GetDestination() const {
|
||||
return CSSPoint::FromAppUnits(mFinalDestination);
|
||||
}
|
||||
|
||||
private:
|
||||
void InitPreferences(TimeStamp aTime);
|
||||
|
||||
private:
|
||||
AsyncPanZoomController& mApzc;
|
||||
nsPoint mFinalDestination;
|
||||
ScrollWheelInput::ScrollDeltaType mDeltaType;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -51,6 +51,16 @@ enum class ScrollInputMethod {
|
|||
// Autoscrolling
|
||||
MainThreadAutoscrolling, // autoscrolling
|
||||
|
||||
// Async Keyboard
|
||||
ApzScrollLine, // line scrolling
|
||||
// (generally triggered by up/down arrow keys)
|
||||
ApzScrollCharacter, // character scrolling
|
||||
// (generally triggered by left/right arrow keys)
|
||||
ApzScrollPage, // page scrolling
|
||||
// (generally triggered by PageUp/PageDown keys)
|
||||
ApzCompleteScroll, // scrolling to the end of the scroll range
|
||||
// (generally triggered by Home/End keys)
|
||||
|
||||
// New input methods can be added at the end, up to a maximum of 64.
|
||||
// They should only be added at the end, to preserve the numerical values
|
||||
// of the existing enumerators.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "nsRect.h" // for mozilla::gfx::IntRect
|
||||
#include "nsRegion.h" // for nsIntRegion
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/gfx/Helpers.h"
|
||||
#include "nsDisplayList.h" // for nsDisplayItem
|
||||
#include "nsCaret.h"
|
||||
|
||||
|
|
|
@ -811,6 +811,12 @@ ClientLayerManager::SetIsFirstPaint()
|
|||
mForwarder->SetIsFirstPaint();
|
||||
}
|
||||
|
||||
void
|
||||
ClientLayerManager::SetFocusTarget(const FocusTarget& aFocusTarget)
|
||||
{
|
||||
mForwarder->SetFocusTarget(aFocusTarget);
|
||||
}
|
||||
|
||||
void
|
||||
ClientLayerManager::ClearCachedResources(Layer* aSubtree)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/WidgetUtils.h" // for ScreenRotation
|
||||
#include "mozilla/gfx/Rect.h" // for Rect
|
||||
#include "mozilla/layers/CompositorTypes.h"
|
||||
#include "mozilla/layers/FocusTarget.h" // for FocusTarget
|
||||
#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
|
||||
#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
|
||||
#include "mozilla/layers/APZTestData.h" // for APZTestData
|
||||
|
@ -133,6 +134,8 @@ public:
|
|||
|
||||
virtual void SetIsFirstPaint() override;
|
||||
|
||||
virtual void SetFocusTarget(const FocusTarget& aFocusTarget) override;
|
||||
|
||||
/**
|
||||
* Pass through call to the forwarder for nsPresContext's
|
||||
* CollectPluginGeometryUpdates. Passes widget configuration information
|
||||
|
|
|
@ -120,7 +120,7 @@ protected:
|
|||
uint32_t GetPaintFlags();
|
||||
void UpdateContentClient(PaintState& aState);
|
||||
bool UpdatePaintRegion(PaintState& aState);
|
||||
void PaintOffMainThread(DrawTargetCapture* aCapture);
|
||||
void PaintOffMainThread(gfx::DrawTargetCapture* aCapture);
|
||||
already_AddRefed<gfx::DrawTargetCapture> CapturePaintedContent();
|
||||
|
||||
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include <string>
|
||||
|
||||
|
|
|
@ -119,6 +119,20 @@ APZCTreeManagerChild::ReceiveInputEvent(
|
|||
event = processedEvent;
|
||||
return res;
|
||||
}
|
||||
case KEYBOARD_INPUT: {
|
||||
KeyboardInput& event = aEvent.AsKeyboardInput();
|
||||
KeyboardInput processedEvent;
|
||||
|
||||
nsEventStatus res;
|
||||
SendReceiveKeyboardInputEvent(event,
|
||||
&res,
|
||||
&processedEvent,
|
||||
aOutTargetGuid,
|
||||
aOutInputBlockId);
|
||||
|
||||
event = processedEvent;
|
||||
return res;
|
||||
}
|
||||
default: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid InputData type.");
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
|
@ -126,6 +140,12 @@ APZCTreeManagerChild::ReceiveInputEvent(
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManagerChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap)
|
||||
{
|
||||
SendSetKeyboardMap(aKeyboardMap);
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManagerChild::ZoomToRect(
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
|
@ -207,11 +227,15 @@ APZCTreeManagerChild::UpdateWheelTransaction(
|
|||
SendUpdateWheelTransaction(aRefPoint, aEventMessage);
|
||||
}
|
||||
|
||||
void APZCTreeManagerChild::TransformEventRefPoint(
|
||||
void APZCTreeManagerChild::ProcessUnhandledEvent(
|
||||
LayoutDeviceIntPoint* aRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid)
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber)
|
||||
{
|
||||
SendTransformEventRefPoint(*aRefPoint, aRefPoint, aOutTargetGuid);
|
||||
SendProcessUnhandledEvent(*aRefPoint,
|
||||
aRefPoint,
|
||||
aOutTargetGuid,
|
||||
aOutFocusSequenceNumber);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
|
|
|
@ -30,6 +30,9 @@ public:
|
|||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId) override;
|
||||
|
||||
void
|
||||
SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
|
||||
|
||||
void
|
||||
ZoomToRect(
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
|
@ -74,9 +77,10 @@ public:
|
|||
ProcessTouchVelocity(uint32_t aTimestampMs, float aSpeedY) override;
|
||||
|
||||
void
|
||||
TransformEventRefPoint(
|
||||
ProcessUnhandledEvent(
|
||||
LayoutDeviceIntPoint* aRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid) override;
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber) override;
|
||||
|
||||
void
|
||||
UpdateWheelTransaction(
|
||||
|
|
|
@ -144,6 +144,33 @@ APZCTreeManagerParent::RecvReceiveScrollWheelInputEvent(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
APZCTreeManagerParent::RecvReceiveKeyboardInputEvent(
|
||||
const KeyboardInput& aEvent,
|
||||
nsEventStatus* aOutStatus,
|
||||
KeyboardInput* aOutEvent,
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId)
|
||||
{
|
||||
KeyboardInput event = aEvent;
|
||||
|
||||
*aOutStatus = mTreeManager->ReceiveInputEvent(
|
||||
event,
|
||||
aOutTargetGuid,
|
||||
aOutInputBlockId);
|
||||
*aOutEvent = event;
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
APZCTreeManagerParent::RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap)
|
||||
{
|
||||
mTreeManager->SetKeyboardMap(aKeyboardMap);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
APZCTreeManagerParent::RecvZoomToRect(
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
|
@ -299,13 +326,14 @@ APZCTreeManagerParent::RecvUpdateWheelTransaction(
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
APZCTreeManagerParent::RecvTransformEventRefPoint(
|
||||
APZCTreeManagerParent::RecvProcessUnhandledEvent(
|
||||
const LayoutDeviceIntPoint& aRefPoint,
|
||||
LayoutDeviceIntPoint* aOutRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid)
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber)
|
||||
{
|
||||
LayoutDeviceIntPoint refPoint = aRefPoint;
|
||||
mTreeManager->TransformEventRefPoint(&refPoint, aOutTargetGuid);
|
||||
mTreeManager->ProcessUnhandledEvent(&refPoint, aOutTargetGuid, aOutFocusSequenceNumber);
|
||||
*aOutRefPoint = refPoint;
|
||||
|
||||
return IPC_OK();
|
||||
|
|
|
@ -78,6 +78,17 @@ public:
|
|||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvReceiveKeyboardInputEvent(
|
||||
const KeyboardInput& aEvent,
|
||||
nsEventStatus* aOutStatus,
|
||||
KeyboardInput* aOutEvent,
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvZoomToRect(
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
|
@ -129,10 +140,11 @@ public:
|
|||
const EventMessage& aEventMessage) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvTransformEventRefPoint(
|
||||
RecvProcessUnhandledEvent(
|
||||
const LayoutDeviceIntPoint& aRefPoint,
|
||||
LayoutDeviceIntPoint* aOutRefPoint,
|
||||
ScrollableLayerGuid* aOutTargetGuid) override;
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutFocusSequenceNumber) override;
|
||||
|
||||
void
|
||||
ActorDestroy(ActorDestroyReason aWhy) override { }
|
||||
|
|
|
@ -568,12 +568,13 @@ CompositorBridgeChild::RecvReleaseSharedCompositorFrameMetrics(
|
|||
const ViewID& aId,
|
||||
const uint32_t& aAPZCId)
|
||||
{
|
||||
SharedFrameMetricsData* data = mFrameMetricsTable.Get(aId);
|
||||
if (auto entry = mFrameMetricsTable.Lookup(aId)) {
|
||||
// The SharedFrameMetricsData may have been removed previously if
|
||||
// a SharedFrameMetricsData with the same ViewID but later APZCId had
|
||||
// been store and over wrote it.
|
||||
if (data && (data->GetAPZCId() == aAPZCId)) {
|
||||
mFrameMetricsTable.Remove(aId);
|
||||
if (entry.Data()->GetAPZCId() == aAPZCId) {
|
||||
entry.Remove();
|
||||
}
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -885,24 +886,18 @@ CompositorBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient
|
|||
void
|
||||
CompositorBridgeChild::NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId)
|
||||
{
|
||||
RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
if (aFwdTransactionId < client->GetLastFwdTransactionId()) {
|
||||
if (auto entry = mTexturesWaitingRecycled.Lookup(aTextureId)) {
|
||||
if (aFwdTransactionId < entry.Data()->GetLastFwdTransactionId()) {
|
||||
// Released on host side, but client already requested newer use texture.
|
||||
return;
|
||||
}
|
||||
mTexturesWaitingRecycled.Remove(aTextureId);
|
||||
entry.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeChild::CancelWaitForRecycle(uint64_t aTextureId)
|
||||
{
|
||||
RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
mTexturesWaitingRecycled.Remove(aTextureId);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "mozilla/layers/TextureForwarder.h" // for TextureForwarder
|
||||
#include "mozilla/webrender/WebRenderTypes.h"
|
||||
#include "nsClassHashtable.h" // for nsClassHashtable
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsCOMPtr.h" // for nsCOMPtr
|
||||
#include "nsHashKeys.h" // for nsUint64HashKey
|
||||
#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
|
||||
|
@ -318,7 +319,7 @@ private:
|
|||
* Hold TextureClients refs until end of their usages on host side.
|
||||
* It defer calling of TextureClient recycle callback.
|
||||
*/
|
||||
nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
|
||||
nsRefPtrHashtable<nsUint64HashKey, TextureClient> mTexturesWaitingRecycled;
|
||||
|
||||
MessageLoop* mMessageLoop;
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче