зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to m-i
MozReview-Commit-ID: 8nqgw9Q3gSY
This commit is contained in:
Коммит
435a27119b
|
@ -673,7 +673,7 @@ ia2Accessible::get_selectionRanges(IA2Range** aRanges,
|
|||
{
|
||||
A11Y_TRYBLOCK_BEGIN
|
||||
|
||||
if (!aRanges || !aNRanges || aNRanges <= 0)
|
||||
if (!aRanges || !aNRanges)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*aNRanges = 0;
|
||||
|
|
|
@ -496,8 +496,27 @@ var LightWeightThemeWebInstaller = {
|
|||
return;
|
||||
}
|
||||
|
||||
let uri = makeURI(baseURI);
|
||||
|
||||
// A notification bar with the option to undo is normally shown after a
|
||||
// theme is installed. But the discovery pane served from the url(s)
|
||||
// below has its own toggle switch for quick undos, so don't show the
|
||||
// notification in that case.
|
||||
let notify = uri.prePath != "https://discovery.addons.mozilla.org";
|
||||
if (notify) {
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("extensions.webapi.testing")
|
||||
&& (uri.prePath == "https://discovery.addons.allizom.org"
|
||||
|| uri.prePath == "https://discovery.addons-dev.allizom.org")) {
|
||||
notify = false;
|
||||
}
|
||||
} catch (e) {
|
||||
// getBoolPref() throws if the testing pref isn't set. ignore it.
|
||||
}
|
||||
}
|
||||
|
||||
if (this._isAllowed(baseURI)) {
|
||||
this._install(data);
|
||||
this._install(data, notify);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -507,12 +526,12 @@ var LightWeightThemeWebInstaller = {
|
|||
gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
|
||||
let message =
|
||||
gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
|
||||
[makeURI(baseURI).host]);
|
||||
[uri.host]);
|
||||
let buttons = [{
|
||||
label: allowButtonText,
|
||||
accessKey: allowButtonAccesskey,
|
||||
callback: function () {
|
||||
LightWeightThemeWebInstaller._install(data);
|
||||
LightWeightThemeWebInstaller._install(data, notify);
|
||||
}
|
||||
}];
|
||||
|
||||
|
@ -526,7 +545,7 @@ var LightWeightThemeWebInstaller = {
|
|||
notificationBar.persistence = 1;
|
||||
},
|
||||
|
||||
_install: function (newLWTheme) {
|
||||
_install: function (newLWTheme, notify) {
|
||||
let previousLWTheme = this._manager.currentTheme;
|
||||
|
||||
let listener = {
|
||||
|
@ -556,7 +575,9 @@ var LightWeightThemeWebInstaller = {
|
|||
},
|
||||
|
||||
onEnabled: function(aAddon) {
|
||||
LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
|
||||
if (notify) {
|
||||
LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -520,6 +520,10 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
|
|||
transition: none;
|
||||
}
|
||||
|
||||
#DateTimePickerPanel {
|
||||
-moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup");
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
|
||||
#urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
|
||||
#urlbar[pageproxystate="valid"] > #urlbar-go-button,
|
||||
|
|
|
@ -141,7 +141,12 @@
|
|||
<tooltip id="remoteBrowserTooltip"/>
|
||||
|
||||
<!-- for search and content formfill/pw manager -->
|
||||
<panel type="autocomplete-richlistbox" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
|
||||
|
||||
<panel type="autocomplete-richlistbox"
|
||||
id="PopupAutoComplete"
|
||||
noautofocus="true"
|
||||
hidden="true"
|
||||
norolluponanchor="true" />
|
||||
|
||||
<!-- for search with one-off buttons -->
|
||||
<panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
|
||||
|
@ -155,10 +160,14 @@
|
|||
level="parent"/>
|
||||
|
||||
<panel id="DateTimePickerPanel"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
orient="vertical"
|
||||
noautofocus="true"
|
||||
consumeoutsideclicks="false"
|
||||
level="parent"/>
|
||||
level="parent">
|
||||
<iframe id="dateTimePopupFrame"/>
|
||||
</panel>
|
||||
|
||||
<!-- for select dropdowns. The menupopup is what shows the list of options,
|
||||
and the popuponly menulist makes things like the menuactive attributes
|
||||
|
|
|
@ -1372,15 +1372,15 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
<body><![CDATA[
|
||||
this._oneOffSearchesEnabled = enable;
|
||||
if (enable) {
|
||||
this.oneOffSearchButtons.telemetryOrigin = "urlbar";
|
||||
this.oneOffSearchButtons.style.display = "-moz-box";
|
||||
this.oneOffSearchButtons.popup = this;
|
||||
this.oneOffSearchButtons.textbox = this.input;
|
||||
this.oneOffSearchButtons.telemetryOrigin = "urlbar";
|
||||
} else {
|
||||
this.oneOffSearchButtons.telemetryOrigin = null;
|
||||
this.oneOffSearchButtons.style.display = "none";
|
||||
this.oneOffSearchButtons.popup = null;
|
||||
this.oneOffSearchButtons.textbox = null;
|
||||
this.oneOffSearchButtons.telemetryOrigin = null;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* eslint no-undef:2 */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
@ -231,7 +233,7 @@ function openPreferences() {
|
|||
.createInstance(Components.interfaces.nsISupportsString);
|
||||
wuri.data = "about:preferences";
|
||||
|
||||
sa.appendElement(wuri, /*weak =*/ false);
|
||||
args.appendElement(wuri, /*weak =*/ false);
|
||||
|
||||
Services.ww.openWindow(null, gBrowserContentHandler.chromeURL,
|
||||
"_blank",
|
||||
|
|
|
@ -976,10 +976,12 @@
|
|||
);
|
||||
this.style.minWidth = minWidth + "px";
|
||||
|
||||
// Set the origin before assigning the popup, as the assignment does
|
||||
// a rebuild and would miss the origin.
|
||||
this.oneOffButtons.telemetryOrigin = "searchbar";
|
||||
// Set popup after setting the minWidth since it builds the buttons.
|
||||
this.oneOffButtons.popup = this;
|
||||
this.oneOffButtons.textbox = this.input;
|
||||
this.oneOffButtons.telemetryOrigin = "searchbar";
|
||||
}
|
||||
|
||||
// First handle deciding if we are showing the reduced version of the
|
||||
|
@ -2026,17 +2028,19 @@
|
|||
if (anonid == "search-one-offs-context-set-default") {
|
||||
let currentEngine = Services.search.currentEngine;
|
||||
|
||||
// Make the target button of the context menu reflect the current
|
||||
// search engine first. Doing this as opposed to rebuilding all the
|
||||
// one-off buttons avoids flicker.
|
||||
let button = this._buttonForEngine(this._contextEngine);
|
||||
button.id = this._buttonIDForEngine(currentEngine);
|
||||
let uri = "chrome://browser/skin/search-engine-placeholder.png";
|
||||
if (currentEngine.iconURI)
|
||||
uri = currentEngine.iconURI.spec;
|
||||
button.setAttribute("image", uri);
|
||||
button.setAttribute("tooltiptext", currentEngine.name);
|
||||
button.engine = currentEngine;
|
||||
if (!this.getAttribute("includecurrentengine")) {
|
||||
// Make the target button of the context menu reflect the current
|
||||
// search engine first. Doing this as opposed to rebuilding all the
|
||||
// one-off buttons avoids flicker.
|
||||
let button = this._buttonForEngine(this._contextEngine);
|
||||
button.id = this._buttonIDForEngine(currentEngine);
|
||||
let uri = "chrome://browser/skin/search-engine-placeholder.png";
|
||||
if (currentEngine.iconURI)
|
||||
uri = currentEngine.iconURI.spec;
|
||||
button.setAttribute("image", uri);
|
||||
button.setAttribute("tooltiptext", currentEngine.name);
|
||||
button.engine = currentEngine;
|
||||
}
|
||||
|
||||
Services.search.currentEngine = this._contextEngine;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ skip-if = os == "mac" # bug 967013
|
|||
[browser_hiddenOneOffs_cleanup.js]
|
||||
[browser_hiddenOneOffs_diacritics.js]
|
||||
[browser_oneOffContextMenu.js]
|
||||
[browser_oneOffContextMenu_setDefault.js]
|
||||
[browser_oneOffHeader.js]
|
||||
[browser_private_search_perwindowpb.js]
|
||||
[browser_yahoo.js]
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
"use strict";
|
||||
|
||||
const TEST_ENGINE_NAME = "Foo";
|
||||
const TEST_ENGINE_BASENAME = "testEngine.xml";
|
||||
const SEARCHBAR_BASE_ID = "searchbar-engine-one-off-item-";
|
||||
const URLBAR_BASE_ID = "urlbar-engine-one-off-item-";
|
||||
|
||||
const searchbar = document.getElementById("searchbar");
|
||||
const urlbar = document.getElementById("urlbar");
|
||||
const searchPopup = document.getElementById("PopupSearchAutoComplete");
|
||||
const urlbarPopup = document.getElementById("PopupAutoCompleteRichResult");
|
||||
const searchIcon = document.getAnonymousElementByAttribute(
|
||||
searchbar, "anonid", "searchbar-search-button"
|
||||
);
|
||||
const searchOneOffBinding = document.getAnonymousElementByAttribute(
|
||||
searchPopup, "anonid", "search-one-off-buttons"
|
||||
);
|
||||
const urlBarOneOffBinding = document.getAnonymousElementByAttribute(
|
||||
urlbarPopup, "anonid", "one-off-search-buttons"
|
||||
);
|
||||
|
||||
let originalEngine = Services.search.currentEngine;
|
||||
|
||||
function resetEngine() {
|
||||
Services.search.currentEngine = originalEngine;
|
||||
}
|
||||
|
||||
registerCleanupFunction(resetEngine);
|
||||
|
||||
add_task(function* init() {
|
||||
yield promiseNewEngine(TEST_ENGINE_BASENAME, {
|
||||
setAsCurrent: false,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_searchBarChangeEngine() {
|
||||
let oneOffButton = yield openPopupAndGetEngineButton(true, searchPopup,
|
||||
searchOneOffBinding,
|
||||
SEARCHBAR_BASE_ID);
|
||||
|
||||
const setDefaultEngineMenuItem = document.getAnonymousElementByAttribute(
|
||||
searchOneOffBinding, "anonid", "search-one-offs-context-set-default"
|
||||
);
|
||||
|
||||
// Click the set default engine menu item.
|
||||
let promise = promiseCurrentEngineChanged();
|
||||
EventUtils.synthesizeMouseAtCenter(setDefaultEngineMenuItem, {});
|
||||
|
||||
// This also checks the engine correctly changed.
|
||||
yield promise;
|
||||
|
||||
Assert.equal(oneOffButton.id, SEARCHBAR_BASE_ID + originalEngine.name,
|
||||
"Should now have the original engine's id for the button");
|
||||
Assert.equal(oneOffButton.getAttribute("tooltiptext"), originalEngine.name,
|
||||
"Should now have the original engine's name for the tooltip");
|
||||
Assert.equal(oneOffButton.image, originalEngine.iconURI.spec,
|
||||
"Should now have the original engine's uri for the image");
|
||||
|
||||
yield promiseClosePopup(searchPopup);
|
||||
});
|
||||
|
||||
add_task(function* test_urlBarChangeEngine() {
|
||||
// Ensure the engine is reset.
|
||||
resetEngine();
|
||||
|
||||
let oneOffButton = yield openPopupAndGetEngineButton(false, urlbarPopup,
|
||||
urlBarOneOffBinding,
|
||||
URLBAR_BASE_ID);
|
||||
|
||||
const setDefaultEngineMenuItem = document.getAnonymousElementByAttribute(
|
||||
urlBarOneOffBinding, "anonid", "search-one-offs-context-set-default"
|
||||
);
|
||||
|
||||
// Click the set default engine menu item.
|
||||
let promise = promiseCurrentEngineChanged();
|
||||
EventUtils.synthesizeMouseAtCenter(setDefaultEngineMenuItem, {});
|
||||
|
||||
// This also checks the engine correctly changed.
|
||||
yield promise;
|
||||
|
||||
let currentEngine = Services.search.currentEngine;
|
||||
|
||||
// For the urlbar, we should keep the new engine's icon.
|
||||
Assert.equal(oneOffButton.id, URLBAR_BASE_ID + currentEngine.name,
|
||||
"Should now have the original engine's id for the button");
|
||||
Assert.equal(oneOffButton.getAttribute("tooltiptext"), currentEngine.name,
|
||||
"Should now have the original engine's name for the tooltip");
|
||||
Assert.equal(oneOffButton.image, currentEngine.iconURI.spec,
|
||||
"Should now have the original engine's uri for the image");
|
||||
|
||||
yield promiseClosePopup(urlbarPopup);
|
||||
});
|
||||
|
||||
/**
|
||||
* Promises that an engine change has happened for the current engine, which
|
||||
* has resulted in the test engine now being the current engine.
|
||||
*
|
||||
* @return {Promise} Resolved once the test engine is set as the current engine.
|
||||
*/
|
||||
function promiseCurrentEngineChanged() {
|
||||
return new Promise(resolve => {
|
||||
function observer(aSub, aTopic, aData) {
|
||||
if (aData == "engine-current") {
|
||||
Assert.ok(Services.search.currentEngine.name, TEST_ENGINE_NAME, "currentEngine set");
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the specified urlbar/search popup and gets the test engine from the
|
||||
* one-off buttons.
|
||||
*
|
||||
* @param {Boolean} isSearch true if the search popup should be opened; false
|
||||
* for the urlbar popup.
|
||||
* @param {Object} popup The expected popup.
|
||||
* @param {Object} oneOffBinding The expected one-off-binding for the popup.
|
||||
* @param {String} baseId The expected string for the id of the current
|
||||
* engine button, without the engine name.
|
||||
* @return {Object} Returns an object that represents the one off button for the
|
||||
* test engine.
|
||||
*/
|
||||
function* openPopupAndGetEngineButton(isSearch, popup, oneOffBinding, baseId) {
|
||||
// Open the popup.
|
||||
let promise = promiseEvent(popup, "popupshown");
|
||||
info("Opening panel");
|
||||
|
||||
// We have to open the popups in differnt ways.
|
||||
if (isSearch) {
|
||||
// Use the search icon to avoid hitting the network.
|
||||
EventUtils.synthesizeMouseAtCenter(searchIcon, {});
|
||||
} else {
|
||||
// There's no history at this stage, so we need to press a key.
|
||||
urlbar.focus();
|
||||
EventUtils.synthesizeKey("a", {});
|
||||
}
|
||||
yield promise;
|
||||
|
||||
const contextMenu = document.getAnonymousElementByAttribute(
|
||||
oneOffBinding, "anonid", "search-one-offs-context-menu"
|
||||
);
|
||||
const oneOffButtons = document.getAnonymousElementByAttribute(
|
||||
oneOffBinding, "anonid", "search-panel-one-offs"
|
||||
);
|
||||
|
||||
// Get the one-off button for the test engine.
|
||||
let oneOffButton;
|
||||
for (let node of oneOffButtons.childNodes) {
|
||||
if (node.engine && node.engine.name == TEST_ENGINE_NAME) {
|
||||
oneOffButton = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.notEqual(oneOffButton, undefined,
|
||||
"One-off for test engine should exist");
|
||||
Assert.equal(oneOffButton.getAttribute("tooltiptext"), TEST_ENGINE_NAME,
|
||||
"One-off should have the tooltip set to the engine name");
|
||||
Assert.equal(oneOffButton.id, baseId + TEST_ENGINE_NAME,
|
||||
"Should have the correct id");
|
||||
|
||||
// Open the context menu on the one-off.
|
||||
promise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(oneOffButton, {
|
||||
type: "contextmenu",
|
||||
button: 2,
|
||||
});
|
||||
yield promise;
|
||||
|
||||
return oneOffButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the popup and moves the mouse away from it.
|
||||
*
|
||||
* @param {Button} popup The popup to close.
|
||||
*/
|
||||
function* promiseClosePopup(popup) {
|
||||
// close the panel using the escape key.
|
||||
let promise = promiseEvent(popup, "popuphidden");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield promise;
|
||||
|
||||
// Move the cursor out of the panel area to avoid messing with other tests.
|
||||
yield EventUtils.synthesizeNativeMouseMove(popup);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"rules": {
|
||||
"no-unused-vars": ["error", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$",
|
||||
"args": "none"
|
||||
}]
|
||||
}
|
||||
};
|
|
@ -44,7 +44,6 @@ const MANIFEST_VERSION = 1;
|
|||
const CACHE_VERSION = 1;
|
||||
|
||||
const KEEP_HISTORY_N_DAYS = 180;
|
||||
const MIN_EXPERIMENT_ACTIVE_SECONDS = 60;
|
||||
|
||||
const PREF_BRANCH = "experiments.";
|
||||
const PREF_ENABLED = "enabled"; // experiments.enabled
|
||||
|
@ -1217,7 +1216,6 @@ Experiments.Experiments.prototype = {
|
|||
|
||||
let activeExperiment = this._getActiveExperiment();
|
||||
let activeChanged = false;
|
||||
let now = this._policy.now();
|
||||
|
||||
if (!activeExperiment) {
|
||||
// Avoid this pref staying out of sync if there were e.g. crashes.
|
||||
|
@ -1277,7 +1275,6 @@ Experiments.Experiments.prototype = {
|
|||
|
||||
if (!applicable && reason && reason[0] != "was-active") {
|
||||
// Report this from here to avoid over-reporting.
|
||||
let desc = TELEMETRY_LOG.ACTIVATION;
|
||||
let data = [TELEMETRY_LOG.ACTIVATION.REJECTED, id];
|
||||
data = data.concat(reason);
|
||||
const key = TELEMETRY_LOG.ACTIVATION_KEY;
|
||||
|
@ -1343,7 +1340,7 @@ Experiments.Experiments.prototype = {
|
|||
time = now + 1000 * CACHE_WRITE_RETRY_DELAY_SEC;
|
||||
}
|
||||
|
||||
for (let [id, experiment] of this._experiments) {
|
||||
for (let [, experiment] of this._experiments) {
|
||||
let scheduleTime = experiment.getScheduleTime();
|
||||
if (scheduleTime > now) {
|
||||
if (time !== null) {
|
||||
|
@ -1644,7 +1641,6 @@ Experiments.ExperimentEntry.prototype = {
|
|||
let data = this._manifestData;
|
||||
|
||||
let now = this._policy.now() / 1000; // The manifest times are in seconds.
|
||||
let minActive = MIN_EXPERIMENT_ACTIVE_SECONDS;
|
||||
let maxActive = data.maxActiveSeconds || 0;
|
||||
let startSec = (this.startDate || 0) / 1000;
|
||||
|
||||
|
@ -2069,10 +2065,6 @@ Experiments.ExperimentEntry.prototype = {
|
|||
throw new Error("shouldStop must not be called on disabled experiments.");
|
||||
}
|
||||
|
||||
let data = this._manifestData;
|
||||
let now = this._policy.now() / 1000; // The manifest times are in seconds.
|
||||
let maxActiveSec = data.maxActiveSeconds || 0;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
this.isApplicable().then(
|
||||
() => deferred.resolve({shouldStop: false}),
|
||||
|
@ -2097,7 +2089,6 @@ Experiments.ExperimentEntry.prototype = {
|
|||
*/
|
||||
getScheduleTime: function () {
|
||||
if (this._enabled) {
|
||||
let now = this._policy.now();
|
||||
let startTime = this._startDate.getTime();
|
||||
let maxActiveTime = startTime + 1000 * this._manifestData.maxActiveSeconds;
|
||||
return Math.min(1000 * this._manifestData.endTime, maxActiveTime);
|
||||
|
@ -2228,7 +2219,7 @@ this.Experiments.PreviousExperimentProvider.prototype = Object.freeze({
|
|||
for (let id of removed) {
|
||||
this._log.trace("updateExperimentList() - removing " + id);
|
||||
let wrapper = new PreviousExperimentAddon(oldMap.get(id));
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", plugin, false);
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
|
||||
}
|
||||
|
||||
this._experimentList = list;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* exported startup, shutdown, install, uninstall */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
|
|
@ -3,5 +3,13 @@
|
|||
module.exports = {
|
||||
"extends": [
|
||||
"../../../../testing/xpcshell/xpcshell.eslintrc.js"
|
||||
]
|
||||
],
|
||||
|
||||
"rules": {
|
||||
"no-unused-vars": ["error", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS|run_test)$",
|
||||
"args": "none"
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* exported PREF_EXPERIMENTS_ENABLED, PREF_LOGGING_LEVEL, PREF_LOGGING_DUMP
|
||||
PREF_MANIFEST_URI, PREF_FETCHINTERVAL, EXPERIMENT1_ID,
|
||||
EXPERIMENT1_NAME, EXPERIMENT1_XPI_SHA1, EXPERIMENT1A_NAME,
|
||||
EXPERIMENT1A_XPI_SHA1, EXPERIMENT2_ID, EXPERIMENT2_XPI_SHA1,
|
||||
EXPERIMENT3_ID, EXPERIMENT4_ID, FAKE_EXPERIMENTS_1,
|
||||
FAKE_EXPERIMENTS_2, gAppInfo, removeCacheFile, defineNow,
|
||||
futureDate, dateToSeconds, loadAddonManager, promiseRestartManager,
|
||||
startAddonManagerOnly, getExperimentAddons, replaceExperiments */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gPolicy = null;
|
||||
|
@ -32,7 +30,6 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
gPolicy = new Experiments.Policy();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
|
@ -49,8 +46,6 @@ add_task(function* test_setup() {
|
|||
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
|
||||
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
|
||||
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
|
||||
|
||||
let experiments = new Experiments.Experiments();
|
||||
});
|
||||
|
||||
function isApplicable(experiment) {
|
||||
|
|
|
@ -9,13 +9,11 @@ Cu.import("resource://testing-common/AddonManagerTesting.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
|
||||
"resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const MANIFEST_HANDLER = "manifests/handler";
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gDataRoot = null;
|
||||
|
@ -47,7 +45,6 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
|
@ -162,7 +159,7 @@ add_task(function* test_getExperiments() {
|
|||
Assert.equal(addons.length, 0, "Precondition: No experiment add-ons are installed.");
|
||||
|
||||
try {
|
||||
let b = yield experiments.getExperimentBranch();
|
||||
yield experiments.getExperimentBranch();
|
||||
Assert.ok(false, "getExperimentBranch should fail with no experiment");
|
||||
}
|
||||
catch (e) {
|
||||
|
|
|
@ -12,14 +12,12 @@ const MANIFEST_HANDLER = "manifests/handler";
|
|||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gDataRoot = null;
|
||||
var gPolicy = null;
|
||||
var gManifestObject = null;
|
||||
var gManifestHandlerURI = null;
|
||||
var gTimerScheduleOffset = -1;
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
|
@ -27,7 +25,6 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
yield removeCacheFile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
|
@ -54,7 +51,7 @@ add_task(function* test_setup() {
|
|||
gPolicy = new Experiments.Policy();
|
||||
patchPolicy(gPolicy, {
|
||||
updatechannel: () => "nightly",
|
||||
oneshotTimer: (callback, timeout, thisObj, name) => gTimerScheduleOffset = timeout,
|
||||
oneshotTimer: (callback, timeout, thisObj, name) => {},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ const MANIFEST_HANDLER = "manifests/handler";
|
|||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gDataRoot = null;
|
||||
|
@ -25,7 +24,6 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
yield removeCacheFile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
|
|
|
@ -7,16 +7,10 @@
|
|||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gPolicy = null;
|
||||
|
||||
|
||||
function ManifestEntry(data) {
|
||||
this.id = EXPERIMENT1_ID;
|
||||
this.xpiURL = "http://localhost:1/dummy.xpi";
|
||||
|
@ -50,7 +44,7 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
createAppInfo();
|
||||
gProfileDir = do_get_profile();
|
||||
do_get_profile();
|
||||
startAddonManagerOnly();
|
||||
yield TelemetryController.testSetup();
|
||||
gPolicy = new Experiments.Policy();
|
||||
|
|
|
@ -9,13 +9,11 @@ Cu.import("resource://testing-common/AddonManagerTesting.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
|
||||
"resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const MANIFEST_HANDLER = "manifests/handler";
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gDataRoot = null;
|
||||
|
@ -29,7 +27,6 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
|
|
|
@ -8,14 +8,12 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gPolicy = new Experiments.Policy();
|
||||
|
||||
function run_test() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
|
@ -68,4 +66,3 @@ add_task(function* test_fetchInvalid() {
|
|||
|
||||
yield promiseRestartManager();
|
||||
});
|
||||
|
||||
|
|
|
@ -8,14 +8,12 @@ Cu.import("resource://gre/modules/TelemetryLog.jsm");
|
|||
var bsp = Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const MANIFEST_HANDLER = "manifests/handler";
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
|
||||
var gProfileDir = null;
|
||||
var gHttpServer = null;
|
||||
var gHttpRoot = null;
|
||||
var gDataRoot = null;
|
||||
|
@ -48,7 +46,6 @@ function run_test() {
|
|||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
|
@ -87,8 +84,6 @@ add_task(function* test_telemetryBasics() {
|
|||
// Check TelemetryLog instead of TelemetrySession.getPayload().log because
|
||||
// TelemetrySession gets Experiments.instance() and side-effects log entries.
|
||||
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
let observerFireCount = 0;
|
||||
let expectedLogLength = 0;
|
||||
|
||||
// Dates the following tests are based on.
|
||||
|
@ -127,21 +122,6 @@ add_task(function* test_telemetryBasics() {
|
|||
],
|
||||
};
|
||||
|
||||
// Data to compare the result of Experiments.getExperiments() against.
|
||||
|
||||
let experimentListData = [
|
||||
{
|
||||
id: EXPERIMENT2_ID,
|
||||
name: "Test experiment 2",
|
||||
description: "And yet another experiment that experiments experimentally.",
|
||||
},
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
name: EXPERIMENT1_NAME,
|
||||
description: "Yet another experiment that experiments experimentally.",
|
||||
},
|
||||
];
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set to before any activation.
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
"use strict";
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
var cacheData = {
|
||||
_enabled: true,
|
||||
_manifestData: {
|
||||
|
|
|
@ -114,6 +114,16 @@ this.TabCrashHandler = {
|
|||
this.unseenCrashedChildIDs.shift();
|
||||
}
|
||||
}
|
||||
|
||||
// check for environment affecting crash reporting
|
||||
let env = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment);
|
||||
let shutdown = env.exists("MOZ_CRASHREPORTER_SHUTDOWN");
|
||||
|
||||
if (shutdown) {
|
||||
Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "oop-frameloader-crashed": {
|
||||
|
|
|
@ -918,6 +918,10 @@ notification[value="translation"] menulist > .menulist-dropmarker {
|
|||
|
||||
%include ../shared/autocomplete.inc.css
|
||||
|
||||
#PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] {
|
||||
border-top: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
#treecolAutoCompleteImage {
|
||||
max-width : 36px;
|
||||
}
|
||||
|
|
|
@ -1752,6 +1752,10 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
|
|||
|
||||
%include ../shared/autocomplete.inc.css
|
||||
|
||||
#PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] {
|
||||
border-top: 1px solid #C7C7C7;
|
||||
}
|
||||
|
||||
#treecolAutoCompleteImage {
|
||||
max-width: 36px;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#PopupAutoComplete > richlistbox > richlistitem {
|
||||
height: 20px;
|
||||
min-height: 20px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
padding: 0px 1px 0px 1px;
|
||||
}
|
||||
|
||||
#PopupAutoComplete > richlistbox > richlistitem > .ac-title {
|
||||
|
|
|
@ -1534,6 +1534,10 @@ html|*.urlbar-input:-moz-lwtheme::placeholder,
|
|||
|
||||
%include ../shared/autocomplete.inc.css
|
||||
|
||||
#PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] {
|
||||
border-top: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
#treecolAutoCompleteImage {
|
||||
max-width: 36px;
|
||||
}
|
||||
|
|
|
@ -159,15 +159,20 @@ js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
|
|||
@depends('--with-toolchain-prefix', target, host, cross_compiling)
|
||||
def toolchain_prefix(value, target, host, cross_compiling):
|
||||
if value:
|
||||
return value[0]
|
||||
# Special case x86-64 <-> x86 cross compiling until we have the right tests
|
||||
# in moz.configure land.
|
||||
if cross_compiling and not all(i.cpu in ('x86_64', 'x86')
|
||||
for i in (target, host)):
|
||||
return '%s-' % target.toolchain
|
||||
return tuple(value)
|
||||
if cross_compiling:
|
||||
return ('%s-' % target.toolchain, '%s-' % target.alias)
|
||||
|
||||
set_config('TOOLCHAIN_PREFIX', toolchain_prefix)
|
||||
add_old_configure_assignment('TOOLCHAIN_PREFIX', toolchain_prefix)
|
||||
@depends(toolchain_prefix, target)
|
||||
def first_toolchain_prefix(toolchain_prefix, target):
|
||||
# Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
|
||||
# command line/environment (in which case there's only one value in the tuple),
|
||||
# or when cross-compiling for Android.
|
||||
if toolchain_prefix and (target.os == 'Android' or len(toolchain_prefix) == 1):
|
||||
return toolchain_prefix[0]
|
||||
|
||||
set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix)
|
||||
add_old_configure_assignment('TOOLCHAIN_PREFIX', first_toolchain_prefix)
|
||||
|
||||
|
||||
# Compilers
|
||||
|
@ -463,15 +468,17 @@ def default_c_compilers(host_or_target):
|
|||
'''
|
||||
assert host_or_target in (host, target)
|
||||
|
||||
@depends(host_or_target, host, toolchain_prefix)
|
||||
def default_c_compilers(host_or_target, host, toolchain_prefix):
|
||||
@depends(host_or_target, target, toolchain_prefix)
|
||||
def default_c_compilers(host_or_target, target, toolchain_prefix):
|
||||
gcc = ('gcc',)
|
||||
if toolchain_prefix and host_or_target is target:
|
||||
gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc
|
||||
|
||||
if host_or_target.kernel == 'WINNT':
|
||||
return ('cl', 'clang-cl', 'gcc', 'clang')
|
||||
return ('cl', 'clang-cl') + gcc + ('clang',)
|
||||
if host_or_target.kernel == 'Darwin':
|
||||
return ('clang',)
|
||||
if host_or_target != host: # cross compilation
|
||||
return ('%sgcc' % toolchain_prefix, 'gcc', 'clang')
|
||||
return ('gcc', 'clang')
|
||||
return gcc + ('clang',)
|
||||
|
||||
return default_c_compilers
|
||||
|
||||
|
|
|
@ -3,20 +3,60 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Test global exit button
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8,";
|
||||
|
||||
addRDMTask(TEST_URL, function* ({ ui, manager }) {
|
||||
let { toolWindow } = ui;
|
||||
let { store } = toolWindow;
|
||||
// Test global exit button
|
||||
addRDMTask(TEST_URL, function* (...args) {
|
||||
yield testExitButton(...args);
|
||||
});
|
||||
|
||||
// Wait until the viewport has been added
|
||||
// Test global exit button on detached tab.
|
||||
// See Bug 1262806
|
||||
add_task(function* () {
|
||||
let tab = yield addTab(TEST_URL);
|
||||
let { ui, manager } = yield openRDM(tab);
|
||||
|
||||
yield waitBootstrap(ui);
|
||||
|
||||
let waitTabIsDetached = Promise.all([
|
||||
once(tab, "TabClose"),
|
||||
once(tab.linkedBrowser, "SwapDocShells")
|
||||
]);
|
||||
|
||||
// Detach the tab with RDM open.
|
||||
let newWindow = gBrowser.replaceTabWithWindow(tab);
|
||||
|
||||
// Waiting the tab is detached.
|
||||
yield waitTabIsDetached;
|
||||
|
||||
// Get the new tab instance.
|
||||
tab = newWindow.gBrowser.tabs[0];
|
||||
|
||||
// Detaching a tab closes RDM.
|
||||
ok(!manager.isActiveForTab(tab),
|
||||
"Responsive Design Mode is not active for the tab");
|
||||
|
||||
// Reopen the RDM and test the exit button again.
|
||||
yield testExitButton(yield openRDM(tab));
|
||||
yield BrowserTestUtils.closeWindow(newWindow);
|
||||
});
|
||||
|
||||
function* waitBootstrap(ui) {
|
||||
let { toolWindow, tab } = ui;
|
||||
let { store } = toolWindow;
|
||||
let url = String(tab.linkedBrowser.currentURI.spec);
|
||||
|
||||
// Wait until the viewport has been added.
|
||||
yield waitUntilState(store, state => state.viewports.length == 1);
|
||||
|
||||
let exitButton = toolWindow.document.getElementById("global-exit-button");
|
||||
// Wait until the document has been loaded.
|
||||
yield waitForFrameLoad(ui, url);
|
||||
}
|
||||
|
||||
yield waitForFrameLoad(ui, TEST_URL);
|
||||
function* testExitButton({ui, manager}) {
|
||||
yield waitBootstrap(ui);
|
||||
|
||||
let exitButton = ui.toolWindow.document.getElementById("global-exit-button");
|
||||
|
||||
ok(manager.isActiveForTab(ui.tab),
|
||||
"Responsive Design Mode active for the tab");
|
||||
|
@ -27,4 +67,4 @@ addRDMTask(TEST_URL, function* ({ ui, manager }) {
|
|||
|
||||
ok(!manager.isActiveForTab(ui.tab),
|
||||
"Responsive Design Mode is not active for the tab");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ var global = this;
|
|||
function startResponsiveMode({data:data}) {
|
||||
debug("START");
|
||||
if (active) {
|
||||
debug("ALREADY STARTED, ABORT");
|
||||
debug("ALREADY STARTED");
|
||||
sendAsyncMessage("ResponsiveMode:Start:Done");
|
||||
return;
|
||||
}
|
||||
addMessageListener("ResponsiveMode:RequestScreenshot", screenshot);
|
||||
|
|
|
@ -291,7 +291,7 @@ KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
|
|||
if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
|
||||
KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
|
||||
mEffectOptions.mPacedProperty,
|
||||
computedValues);
|
||||
computedValues, aStyleContext);
|
||||
}
|
||||
|
||||
properties =
|
||||
|
|
|
@ -417,7 +417,8 @@ PaceRange(const Range<Keyframe>& aKeyframes,
|
|||
|
||||
static nsTArray<double>
|
||||
GetCumulativeDistances(const nsTArray<ComputedKeyframeValues>& aValues,
|
||||
nsCSSPropertyID aProperty);
|
||||
nsCSSPropertyID aProperty,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
|
@ -481,7 +482,8 @@ KeyframeUtils::GetKeyframesFromObject(JSContext* aCx,
|
|||
KeyframeUtils::ApplySpacing(nsTArray<Keyframe>& aKeyframes,
|
||||
SpacingMode aSpacingMode,
|
||||
nsCSSPropertyID aProperty,
|
||||
nsTArray<ComputedKeyframeValues>& aComputedValues)
|
||||
nsTArray<ComputedKeyframeValues>& aComputedValues,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
if (aKeyframes.IsEmpty()) {
|
||||
return;
|
||||
|
@ -492,7 +494,8 @@ KeyframeUtils::ApplySpacing(nsTArray<Keyframe>& aKeyframes,
|
|||
MOZ_ASSERT(IsAnimatableProperty(aProperty),
|
||||
"Paced property should be animatable");
|
||||
|
||||
cumulativeDistances = GetCumulativeDistances(aComputedValues, aProperty);
|
||||
cumulativeDistances = GetCumulativeDistances(aComputedValues, aProperty,
|
||||
aStyleContext);
|
||||
// Reset the computed offsets if using paced spacing.
|
||||
for (Keyframe& keyframe : aKeyframes) {
|
||||
keyframe.mComputedOffset = Keyframe::kComputedOffsetNotSet;
|
||||
|
@ -583,7 +586,7 @@ KeyframeUtils::ApplyDistributeSpacing(nsTArray<Keyframe>& aKeyframes)
|
|||
{
|
||||
nsTArray<ComputedKeyframeValues> emptyArray;
|
||||
ApplySpacing(aKeyframes, SpacingMode::distribute, eCSSProperty_UNKNOWN,
|
||||
emptyArray);
|
||||
emptyArray, nullptr);
|
||||
}
|
||||
|
||||
/* static */ nsTArray<ComputedKeyframeValues>
|
||||
|
@ -1561,12 +1564,14 @@ PaceRange(const Range<Keyframe>& aKeyframes,
|
|||
*
|
||||
* @param aValues The computed values returned by GetComputedKeyframeValues.
|
||||
* @param aPacedProperty The paced property.
|
||||
* @param aStyleContext The style context for computing distance on transform.
|
||||
* @return The cumulative distances for the paced property. The length will be
|
||||
* the same as aValues.
|
||||
*/
|
||||
static nsTArray<double>
|
||||
GetCumulativeDistances(const nsTArray<ComputedKeyframeValues>& aValues,
|
||||
nsCSSPropertyID aPacedProperty)
|
||||
nsCSSPropertyID aPacedProperty,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
// a) If aPacedProperty is a shorthand property, get its components.
|
||||
// Otherwise, just add the longhand property into the set.
|
||||
|
@ -1634,6 +1639,7 @@ GetCumulativeDistances(const nsTArray<ComputedKeyframeValues>& aValues,
|
|||
prop,
|
||||
prevPacedValues[propIdx].mValue,
|
||||
pacedValues[propIdx].mValue,
|
||||
aStyleContext,
|
||||
componentDistance)) {
|
||||
dist += componentDistance * componentDistance;
|
||||
}
|
||||
|
@ -1647,6 +1653,7 @@ GetCumulativeDistances(const nsTArray<ComputedKeyframeValues>& aValues,
|
|||
StyleAnimationValue::ComputeDistance(aPacedProperty,
|
||||
prevPacedValues[0].mValue,
|
||||
pacedValues[0].mValue,
|
||||
aStyleContext,
|
||||
dist);
|
||||
}
|
||||
cumulativeDistances[i] = cumulativeDistances[preIdx] + dist;
|
||||
|
|
|
@ -98,11 +98,14 @@ public:
|
|||
* GetComputedKeyframeValues. Only used when |aSpacingMode| is
|
||||
* SpacingMode::paced. In all other cases this parameter is unused and may
|
||||
* be any value including an empty array.
|
||||
* @param aStyleContext The style context used for calculating paced spacing
|
||||
* on transform.
|
||||
*/
|
||||
static void ApplySpacing(nsTArray<Keyframe>& aKeyframes,
|
||||
SpacingMode aSpacingMode,
|
||||
nsCSSPropertyID aProperty,
|
||||
nsTArray<ComputedKeyframeValues>& aComputedValues);
|
||||
nsTArray<ComputedKeyframeValues>& aComputedValues,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
/**
|
||||
* Wrapper for ApplySpacing to simplify using distribute spacing.
|
||||
|
|
|
@ -44,6 +44,7 @@ support-files =
|
|||
mozilla/file_hide_and_show.html
|
||||
mozilla/file_partial_keyframes.html
|
||||
mozilla/file_spacing_property_order.html
|
||||
mozilla/file_spacing_transform.html
|
||||
mozilla/file_transform_limits.html
|
||||
mozilla/file_underlying-discrete-value.html
|
||||
mozilla/file_set-easing.html
|
||||
|
@ -100,6 +101,7 @@ skip-if = (toolkit == 'gonk' && debug)
|
|||
[mozilla/test_partial_keyframes.html]
|
||||
[mozilla/test_set-easing.html]
|
||||
[mozilla/test_spacing_property_order.html]
|
||||
[mozilla/test_spacing_transform.html]
|
||||
[mozilla/test_transform_limits.html]
|
||||
[mozilla/test_underlying-discrete-value.html]
|
||||
[style/test_animation-seeking-with-current-time.html]
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
const pi = Math.PI;
|
||||
const cos = Math.cos;
|
||||
const sin = Math.sin;
|
||||
const tan = Math.tan;
|
||||
const sqrt = Math.sqrt;
|
||||
|
||||
// Help function for testing the computed offsets by the distance array.
|
||||
function assert_animation_offsets(anim, dist) {
|
||||
const epsilon = 0.00000001;
|
||||
const frames = anim.effect.getKeyframes();
|
||||
const cumDist = dist.reduce( (prev, curr) => {
|
||||
prev.push(prev.length == 0 ? curr : curr + prev[prev.length - 1]);
|
||||
return prev;
|
||||
}, []);
|
||||
|
||||
const total = cumDist[cumDist.length - 1];
|
||||
for (var i = 0; i < frames.length; ++i) {
|
||||
assert_approx_equals(frames[i].computedOffset, cumDist[i] / total,
|
||||
epsilon, 'computedOffset of frame ' + i);
|
||||
}
|
||||
}
|
||||
|
||||
function getAngleDist(rotate1, rotate2) {
|
||||
function quaternion(axis, angle) {
|
||||
var x = axis[0] * sin(angle/2.0);
|
||||
var y = axis[1] * sin(angle/2.0);
|
||||
var z = axis[2] * sin(angle/2.0);
|
||||
var w = cos(angle/2.0);
|
||||
return { 'x': x, 'y': y, 'z': z, 'w': w };
|
||||
}
|
||||
var q1 = quaternion(rotate1.axis, rotate1.angle);
|
||||
var q2 = quaternion(rotate2.axis, rotate2.angle);
|
||||
var dotProduct = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
|
||||
return 2.0 * Math.acos(dotProduct);
|
||||
}
|
||||
|
||||
function createMatrix(elements, Is3D) {
|
||||
return (Is3D ? "matrix3d" : "matrix") + "(" + elements.join() + ")";
|
||||
}
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "none" },
|
||||
{ transform: "translate(-20px)" },
|
||||
{ transform: "translate(100px)" },
|
||||
{ transform: "translate(50px)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
assert_animation_offsets(anim, [ 0, 20, 120, 50 ]);
|
||||
}, 'Test spacing on translate' );
|
||||
|
||||
test(function(t) {
|
||||
var anim =
|
||||
addDiv(t).animate([ { transform: "none" },
|
||||
{ transform: "translate3d(-20px, 10px, 100px)" },
|
||||
{ transform: "translate3d(100px, 200px, 50px)" },
|
||||
{ transform: "translate(50px, -10px)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
var dist = [ 0,
|
||||
Math.sqrt(20 * 20 + 10 * 10 + 100 * 100),
|
||||
Math.sqrt(120 * 120 + 190 * 190 + 50 * 50),
|
||||
Math.sqrt(50 * 50 + 210 * 210 + 50 * 50) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on translate3d' );
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "scale(0.5)" },
|
||||
{ transform: "scale(4.5)" },
|
||||
{ transform: "scale(2.5)" },
|
||||
{ transform: "none"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
assert_animation_offsets(anim, [ 0, 4.0, 2.0, 1.5 ]);
|
||||
}, 'Test spacing on scale' );
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "scale(0.5, 0.5)" },
|
||||
{ transform: "scale3d(4.5, 5.0, 2.5)" },
|
||||
{ transform: "scale3d(2.5, 1.0, 2.0)" },
|
||||
{ transform: "scale3d(1, 0.5, 1.0)"} ],
|
||||
{ spacing:"paced(transform)" });
|
||||
var dist = [ 0,
|
||||
Math.sqrt(4.0 * 4.0 + 4.5 * 4.5 + 1.5 * 1.5),
|
||||
Math.sqrt(2.0 * 2.0 + 4.0 * 4.0 + 0.5 * 0.5),
|
||||
Math.sqrt(1.5 * 1.5 + 0.5 * 0.5 + 1.0 * 1.0) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on scale3d' );
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "rotate(60deg)" },
|
||||
{ transform: "none" },
|
||||
{ transform: "rotate(720deg)" },
|
||||
{ transform: "rotate(-360deg)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
assert_animation_offsets(anim, [ 0, 60, 720, 1080 ]);
|
||||
}, 'Test spacing on rotate' );
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "rotate3d(1,0,0,60deg)" },
|
||||
{ transform: "rotate3d(1,0,0,70deg)" },
|
||||
{ transform: "rotate3d(0,0,1,-110deg)" },
|
||||
{ transform: "rotate3d(1,0,0,219deg)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
var dist = [ 0,
|
||||
getAngleDist({ axis: [1,0,0], angle: 60 * pi / 180 },
|
||||
{ axis: [1,0,0], angle: 70 * pi / 180 }),
|
||||
getAngleDist({ axis: [0,1,0], angle: 70 * pi / 180 },
|
||||
{ axis: [0,0,1], angle: -110 * pi / 180 }),
|
||||
getAngleDist({ axis: [0,0,1], angle: -110 * pi / 180 },
|
||||
{ axis: [1,0,0], angle: 219 * pi / 180 }) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on rotate3d' );
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "skew(60deg)" },
|
||||
{ transform: "none" },
|
||||
{ transform: "skew(-90deg)" },
|
||||
{ transform: "skew(90deg)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
assert_animation_offsets(anim, [ 0, 60, 90, 180 ]);
|
||||
}, 'Test spacing on skew' );
|
||||
|
||||
test(function(t) {
|
||||
var anim = addDiv(t).animate([ { transform: "skew(60deg, 30deg)" },
|
||||
{ transform: "none" },
|
||||
{ transform: "skew(-90deg, 60deg)" },
|
||||
{ transform: "skew(90deg, 60deg)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
var dist = [ 0,
|
||||
sqrt(60 * 60 + 30 * 30),
|
||||
sqrt(90 * 90 + 60 * 60),
|
||||
sqrt(180 * 180 + 0) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on skew along both X and Y' );
|
||||
|
||||
test(function(t) {
|
||||
// We calculate the distance of two perspective functions by converting them
|
||||
// into two matrix3ds, and then do matrix decomposition to get two
|
||||
// perspective vectors, so the equivalent perspective vectors are:
|
||||
// perspective 1: (0, 0, -1/128, 1);
|
||||
// perspective 2: (0, 0, -1/infinity = 0, 1);
|
||||
// perspective 3: (0, 0, -1/1024, 1);
|
||||
// perspective 4: (0, 0, -1/32, 1);
|
||||
var anim = addDiv(t).animate([ { transform: "perspective(128px)" },
|
||||
{ transform: "none" },
|
||||
{ transform: "perspective(1024px)" },
|
||||
{ transform: "perspective(32px)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
assert_animation_offsets(anim,
|
||||
[ 0, 1/128, 1/1024, 1/32 - 1/1024 ]);
|
||||
}, 'Test spacing on perspective' );
|
||||
|
||||
test(function(t) {
|
||||
var anim =
|
||||
addDiv(t).animate([ { transform: "none" },
|
||||
{ transform: "rotate(180deg) translate(0px)" },
|
||||
{ transform: "rotate(180deg) translate(1000px)" },
|
||||
{ transform: "rotate(360deg) translate(1000px)"} ],
|
||||
{ spacing: "paced(transform)" });
|
||||
var dist = [ 0,
|
||||
sqrt(pi * pi + 0),
|
||||
sqrt(1000 * 1000),
|
||||
sqrt(pi * pi + 0) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on matched transform lists' );
|
||||
|
||||
test(function(t) {
|
||||
// matrix1 => translate(100px, 50px), skewX(60deg).
|
||||
// matrix2 => translate(1000px), rotate(180deg).
|
||||
// matrix3 => translate(1000px), scale(1.5, 0.7).
|
||||
const matrix1 = createMatrix([ 1, 0, tan(pi/4.0), 1, 100, 50 ]);
|
||||
const matrix2 = createMatrix([ cos(pi), sin(pi),
|
||||
-sin(pi), cos(pi),
|
||||
1000, 0 ]);
|
||||
const matrix3 = createMatrix([ 1.5, 0, 0, 0.7, 1000, 0 ]);
|
||||
var anim = addDiv(t).animate([ { transform: "none" },
|
||||
{ transform: matrix1 },
|
||||
{ transform: matrix2 },
|
||||
{ transform: matrix3 } ],
|
||||
{ spacing: "paced(transform)" });
|
||||
var dist = [ 0,
|
||||
sqrt(100 * 100 + 50 * 50 + pi/4 * pi/4),
|
||||
sqrt(900 * 900 + 50 * 50 + pi * pi + pi/4 * pi/4),
|
||||
sqrt(pi * pi + 0.5 * 0.5 + 0.3 * 0.3) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on matrix' );
|
||||
|
||||
test(function(t) {
|
||||
// matrix1 => translate3d(100px, 50px, -10px), skew(60deg).
|
||||
// matrix2 => translate3d(1000px, 0, 0), rotate3d(1, 0, 0, 180deg).
|
||||
// matrix3 => translate3d(1000px, 0, 0), scale3d(1.5, 0.7, 2.2).
|
||||
const matrix1 = createMatrix([ 1, 0, 0, 0,
|
||||
tan(pi/4.0), 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
100, 50, -10, 1 ], true);
|
||||
const matrix2 = createMatrix([ 1, 0, 0, 0,
|
||||
0, cos(pi), sin(pi), 0,
|
||||
0, -sin(pi), cos(pi), 0,
|
||||
1000, 0, 0, 1 ], true);
|
||||
const matrix3 = createMatrix([ 1.5, 0, 0, 0,
|
||||
0, 0.7, 0, 0,
|
||||
0, 0, 2.2, 0,
|
||||
1000, 0, 0, 1 ], true);
|
||||
var anim = addDiv(t).animate([ { transform: "none" },
|
||||
{ transform: matrix1 },
|
||||
{ transform: matrix2 },
|
||||
{ transform: matrix3 } ],
|
||||
{ spacing: "paced(transform)" });
|
||||
var dist = [ 0,
|
||||
sqrt(100 * 100 + 50 * 50 + 10 * 10 + pi/4 * pi/4),
|
||||
sqrt(900 * 900 + 50 * 50 + 10 * 10 + pi/4 * pi/4 + pi * pi),
|
||||
sqrt(0.5 * 0.5 + 0.3 * 0.3 + 1.2 * 1.2 + pi * pi) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on matrix3d' );
|
||||
|
||||
test(function(t) {
|
||||
var anim =
|
||||
addDiv(t).animate([ { transform: "none" },
|
||||
{ transform: "translate(100px, 50px) skew(45deg)" },
|
||||
{ transform: "translate(1000px) " +
|
||||
"rotate3d(1, 0, 0, 180deg)" },
|
||||
{ transform: "translate(1000px) " +
|
||||
"scale3d(2.5, 0.5, 0.7)" } ],
|
||||
{ spacing: "paced(transform)" });
|
||||
|
||||
var dist = [ 0,
|
||||
sqrt(100 * 100 + 50 * 50 + pi/4 * pi/4),
|
||||
sqrt(900 * 900 + 50 * 50 + pi/4 * pi/4 + pi * pi),
|
||||
sqrt(1.5 * 1.5 + 0.5 * 0.5 + 0.3 * 0.3 + pi * pi) ];
|
||||
assert_animation_offsets(anim, dist);
|
||||
}, 'Test spacing on mismatched transform list' );
|
||||
|
||||
done();
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_spacing_transform.html");
|
||||
});
|
||||
</script>
|
|
@ -53,6 +53,7 @@
|
|||
#include "mozilla/Hal.h"
|
||||
#include "nsISiteSpecificUserAgent.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/SSE.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "Connection.h"
|
||||
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
|
||||
|
|
|
@ -2703,13 +2703,19 @@ nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement,
|
|||
"should not have shorthand");
|
||||
|
||||
StyleAnimationValue v1, v2;
|
||||
Element* element = content->AsElement();
|
||||
if (property == eCSSProperty_UNKNOWN ||
|
||||
!ComputeAnimationValue(property, content->AsElement(), aValue1, v1) ||
|
||||
!ComputeAnimationValue(property, content->AsElement(), aValue2, v2)) {
|
||||
!ComputeAnimationValue(property, element, aValue1, v1) ||
|
||||
!ComputeAnimationValue(property, element, aValue2, v2)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
if (!StyleAnimationValue::ComputeDistance(property, v1, v2, *aResult)) {
|
||||
nsIPresShell* shell = element->GetUncomposedDoc()->GetShell();
|
||||
RefPtr<nsStyleContext> styleContext = shell
|
||||
? nsComputedDOMStyle::GetStyleContextForElement(element, nullptr, shell)
|
||||
: nullptr;
|
||||
if (!StyleAnimationValue::ComputeDistance(property, v1, v2, styleContext,
|
||||
*aResult)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -4396,9 +4396,6 @@ void HTMLMediaElement::DecodeError(const MediaResult& aError)
|
|||
if (mDecoder) {
|
||||
ShutdownDecoder();
|
||||
}
|
||||
RemoveMediaElementFromURITable();
|
||||
mLoadingSrc = nullptr;
|
||||
mMediaSource = nullptr;
|
||||
AudioTracks()->EmptyTracks();
|
||||
VideoTracks()->EmptyTracks();
|
||||
if (mIsLoadingFromSourceChildren) {
|
||||
|
|
|
@ -31,9 +31,12 @@ LazyLogModule gCubebLog("cubeb");
|
|||
|
||||
void CubebLogCallback(const char* aFmt, ...)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
va_list arglist;
|
||||
va_start(arglist, aFmt);
|
||||
MOZ_LOG(gCubebLog, LogLevel::Verbose, (aFmt, arglist));
|
||||
VsprintfLiteral (buffer, aFmt, arglist);
|
||||
MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s", buffer));
|
||||
va_end(arglist);
|
||||
}
|
||||
|
||||
|
|
|
@ -997,9 +997,6 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
|||
mInfo = aInfo.forget();
|
||||
|
||||
Invalidate();
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->FirstFrameLoaded();
|
||||
}
|
||||
|
||||
// This can run cache callbacks.
|
||||
mResource->EnsureCacheUpToDate();
|
||||
|
@ -1015,6 +1012,12 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
|||
// Run NotifySuspendedStatusChanged now to give us a chance to notice
|
||||
// that autoplay should run.
|
||||
NotifySuspendedStatusChanged();
|
||||
|
||||
// mOwner->FirstFrameLoaded() might call us back. Put it at the bottom of
|
||||
// this function to avoid unexpected shutdown from reentrant calls.
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->FirstFrameLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -229,16 +229,11 @@ protected:
|
|||
MediaDecoderReaderWrapper* Reader() const { return mMaster->mReader; }
|
||||
const MediaInfo& Info() const { return mMaster->Info(); }
|
||||
|
||||
public:
|
||||
// TODO: this function is public because VisibilityChanged() calls it.
|
||||
// We should handle visibility changes in state objects so this function
|
||||
// can be made protected again.
|
||||
//
|
||||
// Note this function will delete the current state object.
|
||||
// Don't access members to avoid UAF after this call.
|
||||
template <class S, typename... Ts>
|
||||
auto SetState(Ts&&... aArgs)
|
||||
-> decltype(DeclVal<S>().Enter(Forward<Ts>(aArgs)...))
|
||||
auto SetState(Ts... aArgs)
|
||||
-> decltype(DeclVal<S>().Enter(Move(aArgs)...))
|
||||
{
|
||||
// keep mMaster in a local object because mMaster will become invalid after
|
||||
// the current state object is deleted.
|
||||
|
@ -253,16 +248,11 @@ public:
|
|||
|
||||
Exit();
|
||||
|
||||
// Note |aArgs| might reference data members of |this|. We need to keep
|
||||
// |this| alive until |s->Enter()| returns.
|
||||
UniquePtr<StateObject> deathGrip(master->mStateObj.release());
|
||||
|
||||
master->mState = s->GetState();
|
||||
master->mStateObj.reset(s);
|
||||
return s->Enter(Forward<Ts>(aArgs)...);
|
||||
return s->Enter(Move(aArgs)...);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Take a raw pointer in order not to change the life cycle of MDSM.
|
||||
// It is guaranteed to be valid by MDSM.
|
||||
Master* mMaster;
|
||||
|
@ -988,6 +978,9 @@ public:
|
|||
|
||||
void Enter()
|
||||
{
|
||||
// We've decoded all samples. We don't need decoders anymore.
|
||||
Reader()->ReleaseResources();
|
||||
|
||||
mMaster->ScheduleStateMachine();
|
||||
}
|
||||
|
||||
|
@ -1291,8 +1284,7 @@ DormantState::HandleDormant(bool aDormant)
|
|||
{
|
||||
if (!aDormant) {
|
||||
MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy);
|
||||
SeekJob seekJob = Move(mPendingSeek);
|
||||
SetState<DecodingFirstFrameState>(Move(seekJob));
|
||||
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1301,11 +1293,10 @@ bool
|
|||
MediaDecoderStateMachine::
|
||||
WaitForCDMState::HandleCDMProxyReady()
|
||||
{
|
||||
SeekJob seekJob = Move(mPendingSeek);
|
||||
if (mPendingDormant) {
|
||||
SetState<DormantState>(Move(seekJob));
|
||||
SetState<DormantState>(Move(mPendingSeek));
|
||||
} else {
|
||||
SetState<DecodingFirstFrameState>(Move(seekJob));
|
||||
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1366,8 +1357,7 @@ MediaDecoderStateMachine::
|
|||
DecodingFirstFrameState::HandleDormant(bool aDormant)
|
||||
{
|
||||
if (aDormant) {
|
||||
SeekJob seekJob = Move(mPendingSeek);
|
||||
SetState<DormantState>(Move(seekJob));
|
||||
SetState<DormantState>(Move(mPendingSeek));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1386,8 +1376,7 @@ DecodingFirstFrameState::MaybeFinishDecodeFirstFrame()
|
|||
mMaster->FinishDecodeFirstFrame();
|
||||
|
||||
if (mPendingSeek.Exists()) {
|
||||
SeekJob seekJob = Move(mPendingSeek);
|
||||
SetState<SeekingState>(Move(seekJob));
|
||||
SetState<SeekingState>(Move(mPendingSeek));
|
||||
} else {
|
||||
SetState<DecodingState>();
|
||||
}
|
||||
|
@ -1497,8 +1486,7 @@ SeekingState::HandleDormant(bool aDormant)
|
|||
mSeekJob.mTarget.SetType(SeekTarget::Accurate);
|
||||
mSeekJob.mTarget.SetVideoOnly(false);
|
||||
}
|
||||
SeekJob seekJob = Move(mSeekJob);
|
||||
SetState<DormantState>(Move(seekJob));
|
||||
SetState<DormantState>(Move(mSeekJob));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,11 @@ static float ExponentialInterpolate(double t0, float v0, double t1, float v1, do
|
|||
|
||||
static float ExponentialApproach(double t0, double v0, float v1, double timeConstant, double t)
|
||||
{
|
||||
return v1 + (v0 - v1) * expf(-(t - t0) / timeConstant);
|
||||
if (!mozilla::dom::WebAudioUtils::FuzzyEqual(timeConstant, 0.0)) {
|
||||
return v1 + (v0 - v1) * expf(-(t - t0) / timeConstant);
|
||||
} else {
|
||||
return v1;
|
||||
}
|
||||
}
|
||||
|
||||
static float ExtractValueFromCurve(double startTime, float* aCurve, uint32_t aCurveLength, double duration, double t)
|
||||
|
@ -33,7 +37,15 @@ static float ExtractValueFromCurve(double startTime, float* aCurve, uint32_t aCu
|
|||
if (ratio >= 1.0) {
|
||||
return aCurve[aCurveLength - 1];
|
||||
}
|
||||
return aCurve[uint32_t(aCurveLength * ratio)];
|
||||
uint32_t current = uint32_t(aCurveLength * ratio);
|
||||
uint32_t next = current + 1;
|
||||
if (next < aCurveLength) {
|
||||
double t0 = double(current) / double(aCurveLength) * duration ;
|
||||
double t1 = double(next) / double(aCurveLength) * duration ;
|
||||
return LinearInterpolate(t0, aCurve[current], t1, aCurve[next], t - startTime);
|
||||
} else {
|
||||
return aCurve[current];
|
||||
}
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -69,12 +81,6 @@ AudioEventTimeline::ValidateEvent(AudioTimelineEvent& aEvent,
|
|||
}
|
||||
}
|
||||
|
||||
if (aEvent.mType == AudioTimelineEvent::SetTarget &&
|
||||
WebAudioUtils::FuzzyEqual(aEvent.mTimeConstant, 0.0)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool timeAndValueValid = IsValid(aEvent.mValue) &&
|
||||
IsValid(aEvent.mDuration);
|
||||
if (!timeAndValueValid) {
|
||||
|
|
|
@ -403,7 +403,7 @@ void TestSetTargetZeroTimeConstant()
|
|||
ErrorResultMock rv;
|
||||
|
||||
timeline.SetTargetAtTime(20.0f, 1.0, 0.0, rv);
|
||||
is(rv, NS_ERROR_DOM_SYNTAX_ERR, "Correct error code returned");
|
||||
is(timeline.GetValueAtTime(1.0), 20.0f, "Should get the correct value when t0 == t1");
|
||||
}
|
||||
|
||||
void TestExponentialInvalidPreviousZeroValue()
|
||||
|
|
|
@ -66,6 +66,7 @@ tags=capturestream
|
|||
[test_audioParamSetCurveAtTimeTwice.html]
|
||||
[test_audioParamSetCurveAtTimeZeroDuration.html]
|
||||
[test_audioParamSetTargetAtTime.html]
|
||||
[test_audioParamSetTargetAtTimeZeroTimeConstant.html]
|
||||
[test_audioParamSetValueAtTime.html]
|
||||
[test_audioParamTimelineDestinationOffset.html]
|
||||
[test_badConnect.html]
|
||||
|
|
|
@ -16,17 +16,10 @@ var gTest = {
|
|||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
var source = context.createConstantSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueCurveAtTime(this.curve, T0, this.duration);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
|
@ -34,14 +27,19 @@ var gTest = {
|
|||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
this.duration = 1024 / context.sampleRate;
|
||||
this.curve = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
this.curve = new Float32Array([1.0, 0.5, 0.75, 0.25]);
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
var data = expectedBuffer.getChannelData(0);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = this.curve[Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / this.duration)))];
|
||||
if (i < 256) {
|
||||
data[i] = 1.0 - 0.5*i/256;
|
||||
} else if (i < 512) {
|
||||
data[i] = 0.5 + 0.25*(i - 256)/256;
|
||||
} else if (i < 768) {
|
||||
data[i] = 0.75 - 0.5*(i - 512)/256;
|
||||
} else {
|
||||
data[i] = 0.25;
|
||||
}
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<title>Test AudioParam.setValueCurveAtTime twice</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
@ -10,28 +10,28 @@
|
|||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
function linearInterpolate(t0, v0, t1, v1, t)
|
||||
{
|
||||
return v0 + (v1 - v0) * ((t - t0) / (t1 - t0));
|
||||
}
|
||||
|
||||
var T0 = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var curve2 = new Float32Array(100);
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
curve2[i] = Math.sin(220 * 6 * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
var source = context.createConstantSource();
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueCurveAtTime(curve2, T0, this.duration/2);
|
||||
//Set a diffrent curve from the first one
|
||||
//Set a different curve from the first one
|
||||
gain.gain.setValueCurveAtTime(this.curve, T0, this.duration);
|
||||
|
||||
source.connect(gain);
|
||||
|
@ -48,7 +48,15 @@ var gTest = {
|
|||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = this.curve[Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / this.duration)))];
|
||||
var current = Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / this.duration)));
|
||||
var next = current + 1;
|
||||
if (next < this.curve.length) {
|
||||
var t0 = current / this.curve.length * this.duration;
|
||||
var t1 = next / this.curve.length * this.duration;
|
||||
expectedBuffer.getChannelData(0)[i] = linearInterpolate(t0, this.curve[current], t1, this.curve[next], t);
|
||||
} else {
|
||||
expectedBuffer.getChannelData(0)[i] = this.curve[current];
|
||||
}
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.setTargetAtTime with zero time constant</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var V0 = 0.9;
|
||||
var V1 = 0.1;
|
||||
var T0 = 0;
|
||||
var TimeConstant = 0;
|
||||
|
||||
var gTest = {
|
||||
length: 2048,
|
||||
numberOfChannels: 1,
|
||||
createGraph: function(context) {
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.value = V0;
|
||||
gain.gain.setTargetAtTime(V1, T0, TimeConstant);
|
||||
|
||||
source.connect(gain);
|
||||
|
||||
source.start(0);
|
||||
return gain;
|
||||
},
|
||||
createExpectedBuffers: function(context) {
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V1;
|
||||
}
|
||||
return expectedBuffer;
|
||||
},
|
||||
};
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -278,6 +278,7 @@ nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
|
|||
|
||||
return StyleAnimationValue::ComputeDistance(toWrapper->mPropID,
|
||||
*fromCSSValue, *toCSSValue,
|
||||
nullptr,
|
||||
aDistance) ?
|
||||
NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ dictionary RTCIceServer {
|
|||
};
|
||||
|
||||
enum RTCIceTransportPolicy {
|
||||
"none",
|
||||
"relay",
|
||||
"all"
|
||||
};
|
||||
|
|
|
@ -150,8 +150,8 @@ ChangeStyleTransaction::DoTransaction()
|
|||
nsGkAtoms::style);
|
||||
|
||||
nsAutoString values;
|
||||
nsresult result = cssDecl->GetPropertyValue(propertyNameString, values);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
nsresult rv = cssDecl->GetPropertyValue(propertyNameString, values);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mUndoValue.Assign(values);
|
||||
|
||||
// Does this property accept more than one value? (bug 62682)
|
||||
|
@ -168,18 +168,17 @@ ChangeStyleTransaction::DoTransaction()
|
|||
RemoveValueFromListOfValues(values, NS_LITERAL_STRING("none"));
|
||||
RemoveValueFromListOfValues(values, mValue);
|
||||
if (values.IsEmpty()) {
|
||||
result = cssDecl->RemoveProperty(propertyNameString, returnString);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = cssDecl->RemoveProperty(propertyNameString, returnString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
nsAutoString priority;
|
||||
cssDecl->GetPropertyPriority(propertyNameString, priority);
|
||||
result = cssDecl->SetProperty(propertyNameString, values,
|
||||
priority);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = cssDecl->SetProperty(propertyNameString, values, priority);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
} else {
|
||||
result = cssDecl->RemoveProperty(propertyNameString, returnString);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = cssDecl->RemoveProperty(propertyNameString, returnString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
} else {
|
||||
nsAutoString priority;
|
||||
|
@ -194,18 +193,17 @@ ChangeStyleTransaction::DoTransaction()
|
|||
} else {
|
||||
values.Assign(mValue);
|
||||
}
|
||||
result = cssDecl->SetProperty(propertyNameString, values,
|
||||
priority);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = cssDecl->SetProperty(propertyNameString, values, priority);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Let's be sure we don't keep an empty style attribute
|
||||
uint32_t length;
|
||||
result = cssDecl->GetLength(&length);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = cssDecl->GetLength(&length);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!length) {
|
||||
result = mElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = mElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
mRedoAttributeWasSet = true;
|
||||
}
|
||||
|
@ -217,7 +215,6 @@ nsresult
|
|||
ChangeStyleTransaction::SetStyle(bool aAttributeWasSet,
|
||||
nsAString& aValue)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
if (aAttributeWasSet) {
|
||||
// The style attribute was not empty, let's recreate the declaration
|
||||
nsAutoString propertyNameString;
|
||||
|
@ -230,18 +227,14 @@ ChangeStyleTransaction::SetStyle(bool aAttributeWasSet,
|
|||
if (aValue.IsEmpty()) {
|
||||
// An empty value means we have to remove the property
|
||||
nsAutoString returnString;
|
||||
result = cssDecl->RemoveProperty(propertyNameString, returnString);
|
||||
} else {
|
||||
// Let's recreate the declaration as it was
|
||||
nsAutoString priority;
|
||||
cssDecl->GetPropertyPriority(propertyNameString, priority);
|
||||
result = cssDecl->SetProperty(propertyNameString, aValue, priority);
|
||||
return cssDecl->RemoveProperty(propertyNameString, returnString);
|
||||
}
|
||||
} else {
|
||||
result = mElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true);
|
||||
// Let's recreate the declaration as it was
|
||||
nsAutoString priority;
|
||||
cssDecl->GetPropertyPriority(propertyNameString, priority);
|
||||
return cssDecl->SetProperty(propertyNameString, aValue, priority);
|
||||
}
|
||||
|
||||
return result;
|
||||
return mElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -33,53 +33,53 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
|||
NS_IMETHODIMP
|
||||
EditAggregateTransaction::DoTransaction()
|
||||
{
|
||||
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
|
||||
// FYI: It's legal (but not very useful) to have an empty child list.
|
||||
for (uint32_t i = 0, length = mChildren.Length(); i < length; ++i) {
|
||||
nsITransaction *txn = mChildren[i];
|
||||
if (!txn) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
result = txn->DoTransaction();
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
nsresult rv = txn->DoTransaction();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditAggregateTransaction::UndoTransaction()
|
||||
{
|
||||
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
|
||||
// undo goes through children backwards
|
||||
// FYI: It's legal (but not very useful) to have an empty child list.
|
||||
// Undo goes through children backwards.
|
||||
for (uint32_t i = mChildren.Length(); i--; ) {
|
||||
nsITransaction *txn = mChildren[i];
|
||||
if (!txn) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
result = txn->UndoTransaction();
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
nsresult rv = txn->UndoTransaction();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditAggregateTransaction::RedoTransaction()
|
||||
{
|
||||
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
|
||||
// It's legal (but not very useful) to have an empty child list.
|
||||
for (uint32_t i = 0, length = mChildren.Length(); i < length; ++i) {
|
||||
nsITransaction *txn = mChildren[i];
|
||||
if (!txn) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
result = txn->RedoTransaction();
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
nsresult rv = txn->RedoTransaction();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -1451,14 +1451,14 @@ EditorBase::SplitNode(nsIContent& aNode,
|
|||
|
||||
mRangeUpdater.SelAdjSplitNode(aNode, aOffset, newNode);
|
||||
|
||||
nsresult result = aResult.StealNSResult();
|
||||
nsresult rv = aResult.StealNSResult();
|
||||
for (auto& listener : mActionListeners) {
|
||||
listener->DidSplitNode(aNode.AsDOMNode(), aOffset, GetAsDOMNode(newNode),
|
||||
result);
|
||||
rv);
|
||||
}
|
||||
// Note: result might be a success code, so we can't use Throw() to
|
||||
// set it on aResult.
|
||||
aResult = result;
|
||||
aResult = rv;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
@ -1495,11 +1495,11 @@ EditorBase::JoinNodes(nsINode& aLeftNode,
|
|||
parent->AsDOMNode());
|
||||
}
|
||||
|
||||
nsresult result = NS_OK;
|
||||
nsresult rv = NS_OK;
|
||||
RefPtr<JoinNodeTransaction> transaction =
|
||||
CreateTxnForJoinNode(aLeftNode, aRightNode);
|
||||
if (transaction) {
|
||||
result = DoTransaction(transaction);
|
||||
rv = DoTransaction(transaction);
|
||||
}
|
||||
|
||||
mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset,
|
||||
|
@ -1507,10 +1507,10 @@ EditorBase::JoinNodes(nsINode& aLeftNode,
|
|||
|
||||
for (auto& listener : mActionListeners) {
|
||||
listener->DidJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(),
|
||||
parent->AsDOMNode(), result);
|
||||
parent->AsDOMNode(), rv);
|
||||
}
|
||||
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -258,16 +258,16 @@ HTMLEditor::Init(nsIDOMDocument* aDoc,
|
|||
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
|
||||
MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported");
|
||||
|
||||
nsresult result = NS_OK, rulesRes = NS_OK;
|
||||
nsresult rulesRv = NS_OK;
|
||||
|
||||
{
|
||||
// block to scope AutoEditInitRulesTrigger
|
||||
AutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
|
||||
AutoEditInitRulesTrigger rulesTrigger(this, rulesRv);
|
||||
|
||||
// Init the plaintext editor
|
||||
result = TextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue);
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
nsresult rv = TextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Init mutation observer
|
||||
|
@ -317,9 +317,9 @@ HTMLEditor::Init(nsIDOMDocument* aDoc,
|
|||
}
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rulesRv, rulesRv);
|
||||
|
||||
NS_ENSURE_SUCCESS(rulesRes, rulesRes);
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1553,8 +1553,8 @@ HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsresult result = DeleteSelectionAndPrepareToCreateNode();
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
nsresult rv = DeleteSelectionAndPrepareToCreateNode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// If deleting, selection will be collapsed.
|
||||
|
@ -3637,7 +3637,6 @@ HTMLEditor::IsTextPropertySetByContent(nsIDOMNode* aNode,
|
|||
bool& aIsSet,
|
||||
nsAString* outValue)
|
||||
{
|
||||
nsresult result;
|
||||
aIsSet = false; // must be initialized to false for code below to work
|
||||
nsAutoString propName;
|
||||
aProperty->ToString(propName);
|
||||
|
@ -3680,8 +3679,7 @@ HTMLEditor::IsTextPropertySetByContent(nsIDOMNode* aNode,
|
|||
}
|
||||
}
|
||||
nsCOMPtr<nsIDOMNode>temp;
|
||||
result = node->GetParentNode(getter_AddRefs(temp));
|
||||
if (NS_SUCCEEDED(result) && temp) {
|
||||
if (NS_SUCCEEDED(node->GetParentNode(getter_AddRefs(temp))) && temp) {
|
||||
node = temp;
|
||||
} else {
|
||||
node = nullptr;
|
||||
|
@ -3760,10 +3758,10 @@ HTMLEditor::CollapseAdjacentTextNodes(nsRange* aInRange)
|
|||
|
||||
|
||||
// build a list of editable text nodes
|
||||
nsresult result;
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
nsCOMPtr<nsIContentIterator> iter =
|
||||
do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &result);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
iter->Init(aInRange);
|
||||
|
||||
|
@ -3788,22 +3786,21 @@ HTMLEditor::CollapseAdjacentTextNodes(nsRange* aInRange)
|
|||
|
||||
// get the prev sibling of the right node, and see if its leftTextNode
|
||||
nsCOMPtr<nsIDOMNode> prevSibOfRightNode;
|
||||
result =
|
||||
rightTextNode->GetPreviousSibling(getter_AddRefs(prevSibOfRightNode));
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = rightTextNode->GetPreviousSibling(getter_AddRefs(prevSibOfRightNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (prevSibOfRightNode && prevSibOfRightNode == leftTextNode) {
|
||||
nsCOMPtr<nsIDOMNode> parent;
|
||||
result = rightTextNode->GetParentNode(getter_AddRefs(parent));
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = rightTextNode->GetParentNode(getter_AddRefs(parent));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
|
||||
result = JoinNodes(leftTextNode, rightTextNode, parent);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = JoinNodes(leftTextNode, rightTextNode, parent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
textNodes.RemoveElementAt(0); // remove the leftmost text node from the list
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -346,8 +346,8 @@ RangeUpdater::SelAdjSplitNode(nsIContent& aOldRightNode,
|
|||
int32_t offset = parent ? parent->IndexOf(&aOldRightNode) : -1;
|
||||
|
||||
// first part is same as inserting aNewLeftnode
|
||||
nsresult result = SelAdjInsertNode(parent, offset - 1);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
nsresult rv = SelAdjInsertNode(parent, offset - 1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// next step is to check for range enpoints inside aOldRightNode
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
|
|
|
@ -565,8 +565,6 @@ nsresult
|
|||
TextEditor::ExtendSelectionForDelete(Selection* aSelection,
|
||||
nsIEditor::EDirection* aAction)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
|
||||
bool bCollapsed = aSelection->Collapsed();
|
||||
|
||||
if (*aAction == eNextWord ||
|
||||
|
@ -579,19 +577,20 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection,
|
|||
GetSelectionController(getter_AddRefs(selCont));
|
||||
NS_ENSURE_TRUE(selCont, NS_ERROR_NO_INTERFACE);
|
||||
|
||||
nsresult rv;
|
||||
switch (*aAction) {
|
||||
case eNextWord:
|
||||
result = selCont->WordExtendForDelete(true);
|
||||
rv = selCont->WordExtendForDelete(true);
|
||||
// DeleteSelectionImpl doesn't handle these actions
|
||||
// because it's inside batching, so don't confuse it:
|
||||
*aAction = eNone;
|
||||
break;
|
||||
case ePreviousWord:
|
||||
result = selCont->WordExtendForDelete(false);
|
||||
rv = selCont->WordExtendForDelete(false);
|
||||
*aAction = eNone;
|
||||
break;
|
||||
case eNext:
|
||||
result = selCont->CharacterExtendForDelete();
|
||||
rv = selCont->CharacterExtendForDelete();
|
||||
// Don't set aAction to eNone (see Bug 502259)
|
||||
break;
|
||||
case ePrevious: {
|
||||
|
@ -602,8 +601,8 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection,
|
|||
// typed character.
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
int32_t offset;
|
||||
result = GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
|
||||
|
||||
// node might be anonymous DIV, so we find better text node
|
||||
|
@ -613,15 +612,15 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection,
|
|||
nsCOMPtr<nsIDOMCharacterData> charData = do_QueryInterface(node);
|
||||
if (charData) {
|
||||
nsAutoString data;
|
||||
result = charData->GetData(data);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
rv = charData->GetData(data);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if ((offset > 1 &&
|
||||
NS_IS_LOW_SURROGATE(data[offset - 1]) &&
|
||||
NS_IS_HIGH_SURROGATE(data[offset - 2])) ||
|
||||
(offset > 0 &&
|
||||
gfxFontUtils::IsVarSelector(data[offset - 1]))) {
|
||||
result = selCont->CharacterExtendForBackspace();
|
||||
rv = selCont->CharacterExtendForBackspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -629,19 +628,20 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection,
|
|||
}
|
||||
case eToBeginningOfLine:
|
||||
selCont->IntraLineMove(true, false); // try to move to end
|
||||
result = selCont->IntraLineMove(false, true); // select to beginning
|
||||
rv = selCont->IntraLineMove(false, true); // select to beginning
|
||||
*aAction = eNone;
|
||||
break;
|
||||
case eToEndOfLine:
|
||||
result = selCont->IntraLineMove(true, true);
|
||||
rv = selCont->IntraLineMove(true, true);
|
||||
*aAction = eNext;
|
||||
break;
|
||||
default: // avoid several compiler warnings
|
||||
result = NS_OK;
|
||||
rv = NS_OK;
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -657,8 +657,6 @@ TextEditor::DeleteSelection(EDirection aAction,
|
|||
// Protect the edit rules object from dying
|
||||
nsCOMPtr<nsIEditRules> rules(mRules);
|
||||
|
||||
nsresult result;
|
||||
|
||||
// delete placeholder txns merge.
|
||||
AutoPlaceHolderBatch batch(this, nsGkAtoms::DeleteTxnName);
|
||||
AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
|
||||
|
@ -676,8 +674,8 @@ TextEditor::DeleteSelection(EDirection aAction,
|
|||
(aAction == eNextWord || aAction == ePreviousWord ||
|
||||
aAction == eToBeginningOfLine || aAction == eToEndOfLine)) {
|
||||
if (mCaretStyle == 1) {
|
||||
result = selection->CollapseToStart();
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
nsresult rv = selection->CollapseToStart();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
aAction = eNone;
|
||||
}
|
||||
|
@ -687,17 +685,16 @@ TextEditor::DeleteSelection(EDirection aAction,
|
|||
ruleInfo.collapsedAction = aAction;
|
||||
ruleInfo.stripWrappers = aStripWrappers;
|
||||
bool cancel, handled;
|
||||
result = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!cancel && !handled) {
|
||||
result = DeleteSelectionImpl(aAction, aStripWrappers);
|
||||
rv = DeleteSelectionImpl(aAction, aStripWrappers);
|
||||
}
|
||||
if (!cancel) {
|
||||
// post-process
|
||||
result = rules->DidDoAction(selection, &ruleInfo, result);
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
}
|
||||
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1091,15 +1088,15 @@ TextEditor::Undo(uint32_t aCount)
|
|||
TextRulesInfo ruleInfo(EditAction::undo);
|
||||
RefPtr<Selection> selection = GetSelection();
|
||||
bool cancel, handled;
|
||||
nsresult result = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
|
||||
if (!cancel && NS_SUCCEEDED(result)) {
|
||||
result = EditorBase::Undo(aCount);
|
||||
result = rules->DidDoAction(selection, &ruleInfo, result);
|
||||
if (!cancel && NS_SUCCEEDED(rv)) {
|
||||
rv = EditorBase::Undo(aCount);
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
}
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfEnd);
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1119,15 +1116,15 @@ TextEditor::Redo(uint32_t aCount)
|
|||
TextRulesInfo ruleInfo(EditAction::redo);
|
||||
RefPtr<Selection> selection = GetSelection();
|
||||
bool cancel, handled;
|
||||
nsresult result = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
|
||||
|
||||
if (!cancel && NS_SUCCEEDED(result)) {
|
||||
result = EditorBase::Redo(aCount);
|
||||
result = rules->DidDoAction(selection, &ruleInfo, result);
|
||||
if (!cancel && NS_SUCCEEDED(rv)) {
|
||||
rv = EditorBase::Redo(aCount);
|
||||
rv = rules->DidDoAction(selection, &ruleInfo, rv);
|
||||
}
|
||||
|
||||
NotifyEditorObservers(eNotifyEditorObserversOfEnd);
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -48,110 +48,112 @@ void TextEditorTest::Run(nsIEditor *aEditor, int32_t *outNumTests, int32_t *outN
|
|||
|
||||
nsresult TextEditorTest::RunUnitTest(int32_t *outNumTests, int32_t *outNumTestsFailed)
|
||||
{
|
||||
nsresult result;
|
||||
|
||||
NS_ENSURE_TRUE(outNumTests && outNumTestsFailed, NS_ERROR_NULL_POINTER);
|
||||
|
||||
*outNumTests = 0;
|
||||
*outNumTestsFailed = 0;
|
||||
|
||||
result = InitDoc();
|
||||
TEST_RESULT(result);
|
||||
nsresult rv = InitDoc();
|
||||
TEST_RESULT(rv);
|
||||
// shouldn't we just bail on error here?
|
||||
|
||||
// insert some simple text
|
||||
result = mTextEditor->InsertText(NS_LITERAL_STRING("1234567890abcdefghij1234567890"));
|
||||
TEST_RESULT(result);
|
||||
rv = mTextEditor->InsertText(NS_LITERAL_STRING("1234567890abcdefghij1234567890"));
|
||||
TEST_RESULT(rv);
|
||||
(*outNumTests)++;
|
||||
if (NS_FAILED(result))
|
||||
if (NS_FAILED(rv)) {
|
||||
++(*outNumTestsFailed);
|
||||
}
|
||||
|
||||
// insert some more text
|
||||
result = mTextEditor->InsertText(NS_LITERAL_STRING("Moreover, I am cognizant of the interrelatedness of all communities and states. I cannot sit idly by in Atlanta and not be concerned about what happens in Birmingham. Injustice anywhere is a threat to justice everywhere"));
|
||||
TEST_RESULT(result);
|
||||
rv = mTextEditor->InsertText(NS_LITERAL_STRING("Moreover, I am cognizant of the interrelatedness of all communities and states. I cannot sit idly by in Atlanta and not be concerned about what happens in Birmingham. Injustice anywhere is a threat to justice everywhere"));
|
||||
TEST_RESULT(rv);
|
||||
(*outNumTests)++;
|
||||
if (NS_FAILED(result))
|
||||
if (NS_FAILED(rv)) {
|
||||
++(*outNumTestsFailed);
|
||||
}
|
||||
|
||||
result = TestInsertBreak();
|
||||
TEST_RESULT(result);
|
||||
rv = TestInsertBreak();
|
||||
TEST_RESULT(rv);
|
||||
(*outNumTests)++;
|
||||
if (NS_FAILED(result))
|
||||
if (NS_FAILED(rv)) {
|
||||
++(*outNumTestsFailed);
|
||||
}
|
||||
|
||||
result = TestTextProperties();
|
||||
TEST_RESULT(result);
|
||||
rv = TestTextProperties();
|
||||
TEST_RESULT(rv);
|
||||
(*outNumTests)++;
|
||||
if (NS_FAILED(result))
|
||||
if (NS_FAILED(rv)) {
|
||||
++(*outNumTestsFailed);
|
||||
}
|
||||
|
||||
// get us back to the original document
|
||||
result = mEditor->Undo(12);
|
||||
TEST_RESULT(result);
|
||||
rv = mEditor->Undo(12);
|
||||
TEST_RESULT(rv);
|
||||
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult TextEditorTest::InitDoc()
|
||||
{
|
||||
nsresult result = mEditor->SelectAll();
|
||||
TEST_RESULT(result);
|
||||
result = mEditor->DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
|
||||
TEST_RESULT(result);
|
||||
return result;
|
||||
nsresult rv = mEditor->SelectAll();
|
||||
TEST_RESULT(rv);
|
||||
rv = mEditor->DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
|
||||
TEST_RESULT(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult TextEditorTest::TestInsertBreak()
|
||||
{
|
||||
nsCOMPtr<nsISelection>selection;
|
||||
nsresult result = mEditor->GetSelection(getter_AddRefs(selection));
|
||||
TEST_RESULT(result);
|
||||
nsresult rv = mEditor->GetSelection(getter_AddRefs(selection));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(selection.get());
|
||||
nsCOMPtr<nsIDOMNode>anchor;
|
||||
result = selection->GetAnchorNode(getter_AddRefs(anchor));
|
||||
TEST_RESULT(result);
|
||||
rv = selection->GetAnchorNode(getter_AddRefs(anchor));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(anchor.get());
|
||||
selection->Collapse(anchor, 0);
|
||||
// insert one break
|
||||
printf("inserting a break\n");
|
||||
result = mTextEditor->InsertLineBreak();
|
||||
TEST_RESULT(result);
|
||||
rv = mTextEditor->InsertLineBreak();
|
||||
TEST_RESULT(rv);
|
||||
mEditor->DebugDumpContent();
|
||||
|
||||
// insert a second break adjacent to the first
|
||||
printf("inserting a second break\n");
|
||||
result = mTextEditor->InsertLineBreak();
|
||||
TEST_RESULT(result);
|
||||
rv = mTextEditor->InsertLineBreak();
|
||||
TEST_RESULT(rv);
|
||||
mEditor->DebugDumpContent();
|
||||
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult TextEditorTest::TestTextProperties()
|
||||
{
|
||||
nsCOMPtr<nsIDOMDocument>doc;
|
||||
nsresult result = mEditor->GetDocument(getter_AddRefs(doc));
|
||||
TEST_RESULT(result);
|
||||
nsresult rv = mEditor->GetDocument(getter_AddRefs(doc));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(doc.get());
|
||||
nsCOMPtr<nsIDOMNodeList>nodeList;
|
||||
// XXX This is broken, text nodes are not elements.
|
||||
nsAutoString textTag(NS_LITERAL_STRING("#text"));
|
||||
result = doc->GetElementsByTagName(textTag, getter_AddRefs(nodeList));
|
||||
TEST_RESULT(result);
|
||||
rv = doc->GetElementsByTagName(textTag, getter_AddRefs(nodeList));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(nodeList.get());
|
||||
uint32_t count;
|
||||
nodeList->GetLength(&count);
|
||||
NS_ASSERTION(0!=count, "there are no text nodes in the document!");
|
||||
nsCOMPtr<nsIDOMNode>textNode;
|
||||
result = nodeList->Item(count-1, getter_AddRefs(textNode));
|
||||
TEST_RESULT(result);
|
||||
rv = nodeList->Item(count - 1, getter_AddRefs(textNode));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(textNode.get());
|
||||
|
||||
// set the whole text node to bold
|
||||
printf("set the whole first text node to bold\n");
|
||||
nsCOMPtr<nsISelection>selection;
|
||||
result = mEditor->GetSelection(getter_AddRefs(selection));
|
||||
TEST_RESULT(result);
|
||||
rv = mEditor->GetSelection(getter_AddRefs(selection));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(selection.get());
|
||||
nsCOMPtr<nsIDOMCharacterData>textData;
|
||||
textData = do_QueryInterface(textNode);
|
||||
|
@ -169,17 +171,17 @@ nsresult TextEditorTest::TestTextProperties()
|
|||
|
||||
const nsAFlatString& empty = EmptyString();
|
||||
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(false==first, "first should be false");
|
||||
NS_ASSERTION(false==any, "any should be false");
|
||||
NS_ASSERTION(false==all, "all should be false");
|
||||
result = htmlEditor->SetInlineProperty(nsGkAtoms::b, empty, empty);
|
||||
TEST_RESULT(result);
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->SetInlineProperty(nsGkAtoms::b, empty, empty);
|
||||
TEST_RESULT(rv);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(true==first, "first should be true");
|
||||
NS_ASSERTION(true==any, "any should be true");
|
||||
NS_ASSERTION(true==all, "all should be true");
|
||||
|
@ -187,11 +189,11 @@ nsresult TextEditorTest::TestTextProperties()
|
|||
|
||||
// remove the bold we just set
|
||||
printf("set the whole first text node to not bold\n");
|
||||
result = htmlEditor->RemoveInlineProperty(nsGkAtoms::b, empty);
|
||||
TEST_RESULT(result);
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->RemoveInlineProperty(nsGkAtoms::b, empty);
|
||||
TEST_RESULT(rv);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(false==first, "first should be false");
|
||||
NS_ASSERTION(false==any, "any should be false");
|
||||
NS_ASSERTION(false==all, "all should be false");
|
||||
|
@ -201,61 +203,57 @@ nsresult TextEditorTest::TestTextProperties()
|
|||
printf("set the first text node (1, length-1) to bold and italic, and (2, length-1) to underline.\n");
|
||||
selection->Collapse(textNode, 1);
|
||||
selection->Extend(textNode, length-1);
|
||||
result = htmlEditor->SetInlineProperty(nsGkAtoms::b, empty, empty);
|
||||
TEST_RESULT(result);
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->SetInlineProperty(nsGkAtoms::b, empty, empty);
|
||||
TEST_RESULT(rv);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(true==first, "first should be true");
|
||||
NS_ASSERTION(true==any, "any should be true");
|
||||
NS_ASSERTION(true==all, "all should be true");
|
||||
mEditor->DebugDumpContent();
|
||||
// make all that same text italic
|
||||
result = htmlEditor->SetInlineProperty(nsGkAtoms::i, empty, empty);
|
||||
TEST_RESULT(result);
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::i, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->SetInlineProperty(nsGkAtoms::i, empty, empty);
|
||||
TEST_RESULT(rv);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::i, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(true==first, "first should be true");
|
||||
NS_ASSERTION(true==any, "any should be true");
|
||||
NS_ASSERTION(true==all, "all should be true");
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::b, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(true==first, "first should be true");
|
||||
NS_ASSERTION(true==any, "any should be true");
|
||||
NS_ASSERTION(true==all, "all should be true");
|
||||
mEditor->DebugDumpContent();
|
||||
|
||||
// make all the text underlined, except for the first 2 and last 2 characters
|
||||
result = doc->GetElementsByTagName(textTag, getter_AddRefs(nodeList));
|
||||
TEST_RESULT(result);
|
||||
rv = doc->GetElementsByTagName(textTag, getter_AddRefs(nodeList));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(nodeList.get());
|
||||
nodeList->GetLength(&count);
|
||||
NS_ASSERTION(0!=count, "there are no text nodes in the document!");
|
||||
result = nodeList->Item(count-2, getter_AddRefs(textNode));
|
||||
TEST_RESULT(result);
|
||||
rv = nodeList->Item(count-2, getter_AddRefs(textNode));
|
||||
TEST_RESULT(rv);
|
||||
TEST_POINTER(textNode.get());
|
||||
textData = do_QueryInterface(textNode);
|
||||
textData->GetLength(&length);
|
||||
NS_ASSERTION(length==915, "wrong text node");
|
||||
selection->Collapse(textNode, 1);
|
||||
selection->Extend(textNode, length-2);
|
||||
result = htmlEditor->SetInlineProperty(nsGkAtoms::u, empty, empty);
|
||||
TEST_RESULT(result);
|
||||
result = htmlEditor->GetInlineProperty(nsGkAtoms::u, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(result);
|
||||
rv = htmlEditor->SetInlineProperty(nsGkAtoms::u, empty, empty);
|
||||
TEST_RESULT(rv);
|
||||
rv = htmlEditor->GetInlineProperty(nsGkAtoms::u, empty, empty, &first,
|
||||
&any, &all);
|
||||
TEST_RESULT(rv);
|
||||
NS_ASSERTION(true==first, "first should be true");
|
||||
NS_ASSERTION(true==any, "any should be true");
|
||||
NS_ASSERTION(true==all, "all should be true");
|
||||
mEditor->DebugDumpContent();
|
||||
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -93,11 +93,11 @@ TypeInState::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
|
|||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
int32_t selOffset = 0;
|
||||
|
||||
nsresult result =
|
||||
nsresult rv =
|
||||
EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
|
||||
&selOffset);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (selNode &&
|
||||
selNode == mLastSelectionContainer &&
|
||||
|
|
|
@ -96,8 +96,6 @@ nsTransactionItem::GetIsBatch(bool *aIsBatch)
|
|||
nsresult
|
||||
nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren)
|
||||
{
|
||||
nsresult result;
|
||||
|
||||
NS_ENSURE_TRUE(aNumChildren, NS_ERROR_NULL_POINTER);
|
||||
|
||||
*aNumChildren = 0;
|
||||
|
@ -105,13 +103,13 @@ nsTransactionItem::GetNumberOfChildren(int32_t *aNumChildren)
|
|||
int32_t ui = 0;
|
||||
int32_t ri = 0;
|
||||
|
||||
result = GetNumberOfUndoItems(&ui);
|
||||
nsresult rv = GetNumberOfUndoItems(&ui);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
result = GetNumberOfRedoItems(&ri);
|
||||
rv = GetNumberOfRedoItems(&ri);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aNumChildren = ui + ri;
|
||||
|
||||
|
@ -126,21 +124,22 @@ nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
|
|||
*aChild = 0;
|
||||
|
||||
int32_t numItems = 0;
|
||||
nsresult result = GetNumberOfChildren(&numItems);
|
||||
nsresult rv = GetNumberOfChildren(&numItems);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aIndex < 0 || aIndex >= numItems)
|
||||
if (aIndex < 0 || aIndex >= numItems) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Children are expected to be in the order they were added,
|
||||
// so the child first added would be at the bottom of the undo
|
||||
// stack, or if there are no items on the undo stack, it would
|
||||
// be at the top of the redo stack.
|
||||
|
||||
result = GetNumberOfUndoItems(&numItems);
|
||||
rv = GetNumberOfUndoItems(&numItems);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (numItems > 0 && aIndex < numItems) {
|
||||
NS_ENSURE_TRUE(mUndoStack, NS_ERROR_FAILURE);
|
||||
|
@ -154,9 +153,9 @@ nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
|
|||
|
||||
aIndex -= numItems;
|
||||
|
||||
result = GetNumberOfRedoItems(&numItems);
|
||||
rv = GetNumberOfRedoItems(&numItems);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(mRedoStack && numItems != 0 && aIndex < numItems, NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -168,29 +167,31 @@ nsTransactionItem::GetChild(int32_t aIndex, nsTransactionItem **aChild)
|
|||
nsresult
|
||||
nsTransactionItem::DoTransaction()
|
||||
{
|
||||
if (mTransaction)
|
||||
if (mTransaction) {
|
||||
return mTransaction->DoTransaction();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::UndoTransaction(nsTransactionManager *aTxMgr)
|
||||
{
|
||||
nsresult result = UndoChildren(aTxMgr);
|
||||
nsresult rv = UndoChildren(aTxMgr);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
if (NS_FAILED(rv)) {
|
||||
RecoverFromUndoError(aTxMgr);
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!mTransaction)
|
||||
if (!mTransaction) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = mTransaction->UndoTransaction();
|
||||
rv = mTransaction->UndoTransaction();
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
if (NS_FAILED(rv)) {
|
||||
RecoverFromUndoError(aTxMgr);
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -200,7 +201,6 @@ nsresult
|
|||
nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
||||
{
|
||||
RefPtr<nsTransactionItem> item;
|
||||
nsresult result = NS_OK;
|
||||
int32_t sz = 0;
|
||||
|
||||
if (mUndoStack) {
|
||||
|
@ -211,6 +211,7 @@ nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
|||
/* Undo all of the transaction items children! */
|
||||
sz = mUndoStack->GetSize();
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
while (sz-- > 0) {
|
||||
item = mUndoStack->Peek();
|
||||
|
||||
|
@ -222,51 +223,53 @@ nsTransactionItem::UndoChildren(nsTransactionManager *aTxMgr)
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = aTxMgr->WillUndoNotify(t, &doInterrupt);
|
||||
rv = aTxMgr->WillUndoNotify(t, &doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = item->UndoTransaction(aTxMgr);
|
||||
rv = item->UndoTransaction(aTxMgr);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
item = mUndoStack->Pop();
|
||||
mRedoStack->Push(item.forget());
|
||||
}
|
||||
|
||||
nsresult result2 = aTxMgr->DidUndoNotify(t, result);
|
||||
nsresult rv2 = aTxMgr->DidUndoNotify(t, rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
}
|
||||
// XXX NS_OK if there is no Undo items or all methods work fine, otherwise,
|
||||
// the result of the last item's UndoTransaction() or
|
||||
// DidUndoNotify() if UndoTransaction() succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionItem::RedoTransaction(nsTransactionManager *aTxMgr)
|
||||
{
|
||||
nsresult result;
|
||||
|
||||
nsCOMPtr<nsITransaction> transaction(mTransaction);
|
||||
if (transaction) {
|
||||
result = transaction->RedoTransaction();
|
||||
nsresult rv = transaction->RedoTransaction();
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
result = RedoChildren(aTxMgr);
|
||||
nsresult rv = RedoChildren(aTxMgr);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
if (NS_FAILED(rv)) {
|
||||
RecoverFromRedoError(aTxMgr);
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -276,14 +279,15 @@ nsresult
|
|||
nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
||||
{
|
||||
RefPtr<nsTransactionItem> item;
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (!mRedoStack)
|
||||
if (!mRedoStack) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* Redo all of the transaction items children! */
|
||||
int32_t sz = mRedoStack->GetSize();
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
while (sz-- > 0) {
|
||||
item = mRedoStack->Peek();
|
||||
|
||||
|
@ -295,31 +299,34 @@ nsTransactionItem::RedoChildren(nsTransactionManager *aTxMgr)
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = aTxMgr->WillRedoNotify(t, &doInterrupt);
|
||||
rv = aTxMgr->WillRedoNotify(t, &doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = item->RedoTransaction(aTxMgr);
|
||||
rv = item->RedoTransaction(aTxMgr);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
item = mRedoStack->Pop();
|
||||
mUndoStack->Push(item.forget());
|
||||
}
|
||||
|
||||
nsresult result2 = aTxMgr->DidUndoNotify(t, result);
|
||||
// XXX Shouldn't this DidRedoNotify()? (bug 1311626)
|
||||
nsresult rv2 = aTxMgr->DidUndoNotify(t, rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
// XXX NS_OK if there is no Redo items or all methods work fine, otherwise,
|
||||
// the result of the last item's RedoTransaction() or
|
||||
// DidUndoNotify() if UndoTransaction() succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -371,16 +378,15 @@ nsTransactionItem::RecoverFromRedoError(nsTransactionManager *aTxMgr)
|
|||
// then undo the transaction item itself.
|
||||
//
|
||||
|
||||
nsresult result;
|
||||
nsresult rv = UndoChildren(aTxMgr);
|
||||
|
||||
result = UndoChildren(aTxMgr);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!mTransaction)
|
||||
if (!mTransaction) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mTransaction->UndoTransaction();
|
||||
}
|
||||
|
|
|
@ -49,14 +49,13 @@ NS_IMETHODIMP nsTransactionList::GetNumItems(int32_t *aNumItems)
|
|||
|
||||
NS_ENSURE_TRUE(txMgr, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (mTxnStack)
|
||||
if (mTxnStack) {
|
||||
*aNumItems = mTxnStack->GetSize();
|
||||
else if (mTxnItem)
|
||||
result = mTxnItem->GetNumberOfChildren(aNumItems);
|
||||
} else if (mTxnItem) {
|
||||
return mTxnItem->GetNumberOfChildren(aNumItems);
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsTransactionList::ItemIsBatch(int32_t aIndex, bool *aIsBatch)
|
||||
|
@ -71,14 +70,12 @@ NS_IMETHODIMP nsTransactionList::ItemIsBatch(int32_t aIndex, bool *aIsBatch)
|
|||
|
||||
RefPtr<nsTransactionItem> item;
|
||||
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (mTxnStack)
|
||||
if (mTxnStack) {
|
||||
item = mTxnStack->GetItem(aIndex);
|
||||
else if (mTxnItem)
|
||||
result = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
} else if (mTxnItem) {
|
||||
nsresult rv = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -98,8 +95,8 @@ NS_IMETHODIMP nsTransactionList::GetData(int32_t aIndex,
|
|||
if (mTxnStack) {
|
||||
item = mTxnStack->GetItem(aIndex);
|
||||
} else if (mTxnItem) {
|
||||
nsresult result = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
nsresult rv = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMArray<nsISupports>& data = item->GetData();
|
||||
|
@ -129,14 +126,12 @@ NS_IMETHODIMP nsTransactionList::GetItem(int32_t aIndex, nsITransaction **aItem)
|
|||
|
||||
RefPtr<nsTransactionItem> item;
|
||||
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (mTxnStack)
|
||||
if (mTxnStack) {
|
||||
item = mTxnStack->GetItem(aIndex);
|
||||
else if (mTxnItem)
|
||||
result = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
} else if (mTxnItem) {
|
||||
nsresult rv = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -157,14 +152,12 @@ NS_IMETHODIMP nsTransactionList::GetNumChildrenForItem(int32_t aIndex, int32_t *
|
|||
|
||||
RefPtr<nsTransactionItem> item;
|
||||
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (mTxnStack)
|
||||
if (mTxnStack) {
|
||||
item = mTxnStack->GetItem(aIndex);
|
||||
else if (mTxnItem)
|
||||
result = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
} else if (mTxnItem) {
|
||||
nsresult rv = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -183,14 +176,12 @@ NS_IMETHODIMP nsTransactionList::GetChildListForItem(int32_t aIndex, nsITransact
|
|||
|
||||
RefPtr<nsTransactionItem> item;
|
||||
|
||||
nsresult result = NS_OK;
|
||||
|
||||
if (mTxnStack)
|
||||
if (mTxnStack) {
|
||||
item = mTxnStack->GetItem(aIndex);
|
||||
else if (mTxnItem)
|
||||
result = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
} else if (mTxnItem) {
|
||||
nsresult rv = mTxnItem->GetChild(aIndex, getter_AddRefs(item));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
|
||||
|
||||
|
@ -202,4 +193,3 @@ NS_IMETHODIMP nsTransactionList::GetChildListForItem(int32_t aIndex, nsITransact
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,45 +59,43 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransactionManager)
|
|||
NS_IMETHODIMP
|
||||
nsTransactionManager::DoTransaction(nsITransaction *aTransaction)
|
||||
{
|
||||
nsresult result;
|
||||
|
||||
NS_ENSURE_TRUE(aTransaction, NS_ERROR_NULL_POINTER);
|
||||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = WillDoNotify(aTransaction, &doInterrupt);
|
||||
nsresult rv = WillDoNotify(aTransaction, &doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = BeginTransaction(aTransaction, nullptr);
|
||||
rv = BeginTransaction(aTransaction, nullptr);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
DidDoNotify(aTransaction, result);
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
DidDoNotify(aTransaction, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
result = EndTransaction(false);
|
||||
rv = EndTransaction(false);
|
||||
|
||||
nsresult result2 = DidDoNotify(aTransaction, result);
|
||||
nsresult rv2 = DidDoNotify(aTransaction, rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
|
||||
return result;
|
||||
// XXX The result of EndTransaction() or DidDoNotify() if EndTransaction()
|
||||
// succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::UndoTransaction()
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
|
||||
// It is illegal to call UndoTransaction() while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method! If this happens,
|
||||
// the UndoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
|
||||
|
@ -119,37 +117,37 @@ nsTransactionManager::UndoTransaction()
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = WillUndoNotify(t, &doInterrupt);
|
||||
nsresult rv = WillUndoNotify(t, &doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = tx->UndoTransaction(this);
|
||||
rv = tx->UndoTransaction(this);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
tx = mUndoStack.Pop();
|
||||
mRedoStack.Push(tx.forget());
|
||||
}
|
||||
|
||||
nsresult result2 = DidUndoNotify(t, result);
|
||||
nsresult rv2 = DidUndoNotify(t, rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
|
||||
return result;
|
||||
// XXX The result of UndoTransaction() or DidUndoNotify() if UndoTransaction()
|
||||
// succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::RedoTransaction()
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
|
||||
// It is illegal to call RedoTransaction() while the transaction manager is
|
||||
// executing a transaction's DoTransaction() method! If this happens,
|
||||
// the RedoTransaction() request is ignored, and we return NS_ERROR_FAILURE.
|
||||
|
@ -171,53 +169,49 @@ nsTransactionManager::RedoTransaction()
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = WillRedoNotify(t, &doInterrupt);
|
||||
nsresult rv = WillRedoNotify(t, &doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = tx->RedoTransaction(this);
|
||||
rv = tx->RedoTransaction(this);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
tx = mRedoStack.Pop();
|
||||
mUndoStack.Push(tx.forget());
|
||||
}
|
||||
|
||||
nsresult result2 = DidRedoNotify(t, result);
|
||||
nsresult rv2 = DidRedoNotify(t, rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
|
||||
return result;
|
||||
// XXX The result of RedoTransaction() or DidRedoNotify() if RedoTransaction()
|
||||
// succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::Clear()
|
||||
{
|
||||
nsresult result;
|
||||
nsresult rv = ClearRedoStack();
|
||||
|
||||
result = ClearRedoStack();
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
result = ClearUndoStack();
|
||||
|
||||
return result;
|
||||
return ClearUndoStack();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::BeginBatch(nsISupports* aData)
|
||||
{
|
||||
nsresult result;
|
||||
|
||||
// We can batch independent transactions together by simply pushing
|
||||
// a dummy transaction item on the do stack. This dummy transaction item
|
||||
// will be popped off the do stack, and then pushed on the undo stack
|
||||
|
@ -225,33 +219,32 @@ nsTransactionManager::BeginBatch(nsISupports* aData)
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = WillBeginBatchNotify(&doInterrupt);
|
||||
nsresult rv = WillBeginBatchNotify(&doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = BeginTransaction(0, aData);
|
||||
rv = BeginTransaction(0, aData);
|
||||
|
||||
nsresult result2 = DidBeginBatchNotify(result);
|
||||
nsresult rv2 = DidBeginBatchNotify(rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
|
||||
return result;
|
||||
// XXX The result of BeginTransaction() or DidBeginBatchNotify() if
|
||||
// BeginTransaction() succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTransactionManager::EndBatch(bool aAllowEmpty)
|
||||
{
|
||||
nsCOMPtr<nsITransaction> ti;
|
||||
nsresult result;
|
||||
|
||||
// XXX: Need to add some mechanism to detect the case where the transaction
|
||||
// at the top of the do stack isn't the dummy transaction, so we can
|
||||
// throw an error!! This can happen if someone calls EndBatch() within
|
||||
|
@ -265,6 +258,7 @@ nsTransactionManager::EndBatch(bool aAllowEmpty)
|
|||
|
||||
RefPtr<nsTransactionItem> tx = mDoStack.Peek();
|
||||
|
||||
nsCOMPtr<nsITransaction> ti;
|
||||
if (tx) {
|
||||
ti = tx->GetTransaction();
|
||||
}
|
||||
|
@ -275,25 +269,27 @@ nsTransactionManager::EndBatch(bool aAllowEmpty)
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = WillEndBatchNotify(&doInterrupt);
|
||||
nsresult rv = WillEndBatchNotify(&doInterrupt);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
return result;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (doInterrupt) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
result = EndTransaction(aAllowEmpty);
|
||||
rv = EndTransaction(aAllowEmpty);
|
||||
|
||||
nsresult result2 = DidEndBatchNotify(result);
|
||||
nsresult rv2 = DidEndBatchNotify(rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
|
||||
return result;
|
||||
// XXX The result of EndTransaction() or DidEndBatchNotify() if
|
||||
// EndTransaction() succeeded.
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -470,7 +466,7 @@ nsTransactionManager::BatchTopUndo()
|
|||
previousUndo = mUndoStack.Peek();
|
||||
MOZ_ASSERT(previousUndo, "There should be at least two transactions.");
|
||||
|
||||
nsresult result = previousUndo->AddChild(lastUndo);
|
||||
nsresult rv = previousUndo->AddChild(lastUndo);
|
||||
|
||||
// Transfer data from the transactions that is going to be
|
||||
// merged to the transaction that it is being merged with.
|
||||
|
@ -479,7 +475,7 @@ nsTransactionManager::BatchTopUndo()
|
|||
NS_ENSURE_TRUE(previousData.AppendObjects(lastData), NS_ERROR_UNEXPECTED);
|
||||
lastData.Clear();
|
||||
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -526,210 +522,199 @@ nsTransactionManager::ClearRedoStack()
|
|||
nsresult
|
||||
nsTransactionManager::WillDoNotify(nsITransaction *aTransaction, bool *aInterrupt)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->WillDo(this, aTransaction, aInterrupt);
|
||||
nsresult rv = listener->WillDo(this, aTransaction, aInterrupt);
|
||||
|
||||
if (NS_FAILED(result) || *aInterrupt) {
|
||||
break;
|
||||
if (NS_FAILED(rv) || *aInterrupt) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidDoNotify(nsITransaction *aTransaction, nsresult aDoResult)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->DidDo(this, aTransaction, aDoResult);
|
||||
nsresult rv = listener->DidDo(this, aTransaction, aDoResult);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillUndoNotify(nsITransaction *aTransaction, bool *aInterrupt)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->WillUndo(this, aTransaction, aInterrupt);
|
||||
nsresult rv = listener->WillUndo(this, aTransaction, aInterrupt);
|
||||
|
||||
if (NS_FAILED(result) || *aInterrupt) {
|
||||
break;
|
||||
if (NS_FAILED(rv) || *aInterrupt) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidUndoNotify(nsITransaction *aTransaction, nsresult aUndoResult)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->DidUndo(this, aTransaction, aUndoResult);
|
||||
nsresult rv = listener->DidUndo(this, aTransaction, aUndoResult);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillRedoNotify(nsITransaction *aTransaction, bool *aInterrupt)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->WillRedo(this, aTransaction, aInterrupt);
|
||||
nsresult rv = listener->WillRedo(this, aTransaction, aInterrupt);
|
||||
|
||||
if (NS_FAILED(result) || *aInterrupt) {
|
||||
break;
|
||||
if (NS_FAILED(rv) || *aInterrupt) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidRedoNotify(nsITransaction *aTransaction, nsresult aRedoResult)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->DidRedo(this, aTransaction, aRedoResult);
|
||||
nsresult rv = listener->DidRedo(this, aTransaction, aRedoResult);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillBeginBatchNotify(bool *aInterrupt)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->WillBeginBatch(this, aInterrupt);
|
||||
nsresult rv = listener->WillBeginBatch(this, aInterrupt);
|
||||
|
||||
if (NS_FAILED(result) || *aInterrupt) {
|
||||
break;
|
||||
if (NS_FAILED(rv) || *aInterrupt) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidBeginBatchNotify(nsresult aResult)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->DidBeginBatch(this, aResult);
|
||||
nsresult rv = listener->DidBeginBatch(this, aResult);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillEndBatchNotify(bool *aInterrupt)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->WillEndBatch(this, aInterrupt);
|
||||
nsresult rv = listener->WillEndBatch(this, aInterrupt);
|
||||
|
||||
if (NS_FAILED(result) || *aInterrupt) {
|
||||
break;
|
||||
if (NS_FAILED(rv) || *aInterrupt) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::DidEndBatchNotify(nsresult aResult)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->DidEndBatch(this, aResult);
|
||||
nsresult rv = listener->DidEndBatch(this, aResult);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::WillMergeNotify(nsITransaction *aTop, nsITransaction *aTransaction, bool *aInterrupt)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->WillMerge(this, aTop, aTransaction, aInterrupt);
|
||||
nsresult rv = listener->WillMerge(this, aTop, aTransaction, aInterrupt);
|
||||
|
||||
if (NS_FAILED(result) || *aInterrupt) {
|
||||
break;
|
||||
if (NS_FAILED(rv) || *aInterrupt) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -738,28 +723,26 @@ nsTransactionManager::DidMergeNotify(nsITransaction *aTop,
|
|||
bool aDidMerge,
|
||||
nsresult aMergeResult)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
for (int32_t i = 0, lcount = mListeners.Count(); i < lcount; i++) {
|
||||
nsITransactionListener *listener = mListeners[i];
|
||||
nsITransactionListener* listener = mListeners[i];
|
||||
|
||||
NS_ENSURE_TRUE(listener, NS_ERROR_FAILURE);
|
||||
|
||||
result = listener->DidMerge(this, aTop, aTransaction, aDidMerge, aMergeResult);
|
||||
nsresult rv =
|
||||
listener->DidMerge(this, aTop, aTransaction, aDidMerge, aMergeResult);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
break;
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTransactionManager::BeginTransaction(nsITransaction *aTransaction,
|
||||
nsISupports *aData)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
|
||||
// XXX: POSSIBLE OPTIMIZATION
|
||||
// We could use a factory that pre-allocates/recycles transaction items.
|
||||
RefPtr<nsTransactionItem> tx = new nsTransactionItem(aTransaction);
|
||||
|
@ -775,11 +758,11 @@ nsTransactionManager::BeginTransaction(nsITransaction *aTransaction,
|
|||
|
||||
mDoStack.Push(tx);
|
||||
|
||||
result = tx->DoTransaction();
|
||||
nsresult rv = tx->DoTransaction();
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
if (NS_FAILED(rv)) {
|
||||
tx = mDoStack.Pop();
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -788,8 +771,6 @@ nsTransactionManager::BeginTransaction(nsITransaction *aTransaction,
|
|||
nsresult
|
||||
nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
||||
{
|
||||
nsresult result = NS_OK;
|
||||
|
||||
RefPtr<nsTransactionItem> tx = mDoStack.Pop();
|
||||
|
||||
if (!tx) {
|
||||
|
@ -807,7 +788,7 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
|||
tx->GetNumberOfChildren(&nc);
|
||||
|
||||
if (!nc) {
|
||||
return result;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,15 +796,15 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
|||
// more to do, just return.
|
||||
|
||||
bool isTransient = false;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (tint) {
|
||||
result = tint->GetIsTransient(&isTransient);
|
||||
rv = tint->GetIsTransient(&isTransient);
|
||||
}
|
||||
|
||||
if (NS_FAILED(result) || isTransient || !mMaxTransactionCount) {
|
||||
if (NS_FAILED(rv) || isTransient || !mMaxTransactionCount) {
|
||||
// XXX: Should we be clearing the redo stack if the transaction
|
||||
// is transient and there is nothing on the do stack?
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Check if there is a transaction on the do stack. If there is,
|
||||
|
@ -832,18 +813,14 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
|||
|
||||
RefPtr<nsTransactionItem> top = mDoStack.Peek();
|
||||
if (top) {
|
||||
result = top->AddChild(tx);
|
||||
|
||||
// XXX: What do we do if this fails?
|
||||
|
||||
return result;
|
||||
return top->AddChild(tx); // XXX: What do we do if this fails?
|
||||
}
|
||||
|
||||
// The transaction succeeded, so clear the redo stack.
|
||||
|
||||
result = ClearRedoStack();
|
||||
rv = ClearRedoStack();
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
if (NS_FAILED(rv)) {
|
||||
// XXX: What do we do if this fails?
|
||||
}
|
||||
|
||||
|
@ -860,25 +837,25 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
|||
|
||||
bool doInterrupt = false;
|
||||
|
||||
result = WillMergeNotify(topTransaction, tint, &doInterrupt);
|
||||
rv = WillMergeNotify(topTransaction, tint, &doInterrupt);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!doInterrupt) {
|
||||
result = topTransaction->Merge(tint, &didMerge);
|
||||
rv = topTransaction->Merge(tint, &didMerge);
|
||||
|
||||
nsresult result2 = DidMergeNotify(topTransaction, tint, didMerge, result);
|
||||
nsresult rv2 = DidMergeNotify(topTransaction, tint, didMerge, rv);
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
result = result2;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = rv2;
|
||||
}
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
if (NS_FAILED(rv)) {
|
||||
// XXX: What do we do if this fails?
|
||||
}
|
||||
|
||||
if (didMerge) {
|
||||
return result;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -899,4 +876,3 @@ nsTransactionManager::EndTransaction(bool aAllowEmpty)
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -463,6 +463,11 @@ public:
|
|||
, _41(a41), _42(a42), _43(a43), _44(a44)
|
||||
{}
|
||||
|
||||
explicit Matrix4x4Typed(const Float aArray[16])
|
||||
{
|
||||
memcpy(components, aArray, sizeof(components));
|
||||
}
|
||||
|
||||
Matrix4x4Typed(const Matrix4x4Typed& aOther)
|
||||
{
|
||||
memcpy(this, &aOther, sizeof(*this));
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "GLContextProvider.h"
|
||||
#include "GLContextCGL.h"
|
||||
#include "TextureImageCGL.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsIWidget.h"
|
||||
#include <OpenGL/gl.h>
|
||||
|
|
|
@ -15,9 +15,6 @@
|
|||
#include "GfxTexturesReporter.h"
|
||||
|
||||
#include "TextureImageEGL.h"
|
||||
#ifdef XP_MACOSX
|
||||
#include "TextureImageCGL.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
|
@ -33,10 +30,6 @@ CreateTextureImage(GLContext* gl,
|
|||
TextureImage::ImageFormat aImageFormat)
|
||||
{
|
||||
switch (gl->GetContextType()) {
|
||||
#ifdef XP_MACOSX
|
||||
case GLContextType::CGL:
|
||||
return CreateTextureImageCGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
|
||||
#endif
|
||||
case GLContextType::EGL:
|
||||
return CreateTextureImageEGL(gl, aSize, aContentType, aWrapMode, aFlags, aImageFormat);
|
||||
default: {
|
||||
|
@ -61,10 +54,6 @@ TileGenFunc(GLContext* gl,
|
|||
TextureImage::ImageFormat aImageFormat)
|
||||
{
|
||||
switch (gl->GetContextType()) {
|
||||
#ifdef XP_MACOSX
|
||||
case GLContextType::CGL:
|
||||
return TileGenFuncCGL(gl, aSize, aContentType, aFlags);
|
||||
#endif
|
||||
case GLContextType::EGL:
|
||||
return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat);
|
||||
default:
|
||||
|
@ -131,77 +120,6 @@ BasicTextureImage::~BasicTextureImage()
|
|||
}
|
||||
}
|
||||
|
||||
gfx::DrawTarget*
|
||||
BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?");
|
||||
|
||||
// determine the region the client will need to repaint
|
||||
if (CanUploadSubTextures(mGLContext)) {
|
||||
GetUpdateRegion(aRegion);
|
||||
} else {
|
||||
aRegion = IntRect(IntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
mUpdateRegion = aRegion;
|
||||
|
||||
IntRect rgnSize = mUpdateRegion.GetBounds();
|
||||
if (!IntRect(IntPoint(0, 0), mSize).Contains(rgnSize)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat format =
|
||||
(GetContentType() == gfxContentType::COLOR) ?
|
||||
gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8;
|
||||
mUpdateDrawTarget =
|
||||
GetDrawTargetForUpdate(gfx::IntSize(rgnSize.width, rgnSize.height), format);
|
||||
|
||||
return mUpdateDrawTarget;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
|
||||
{
|
||||
// if the texture hasn't been initialized yet, or something important
|
||||
// changed, we need to recreate our backing surface and force the
|
||||
// client to paint everything
|
||||
if (mTextureState != Valid) {
|
||||
aForRegion = IntRect(IntPoint(0, 0), mSize);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::EndUpdate()
|
||||
{
|
||||
NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?");
|
||||
|
||||
// FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
|
||||
|
||||
RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
|
||||
RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
|
||||
|
||||
bool relative = FinishedSurfaceUpdate();
|
||||
bool needInit = mTextureState == Created;
|
||||
size_t uploadSize;
|
||||
mTextureFormat =
|
||||
UploadSurfaceToTexture(mGLContext,
|
||||
updateData,
|
||||
mUpdateRegion,
|
||||
mTexture,
|
||||
&uploadSize,
|
||||
needInit,
|
||||
mUpdateOffset,
|
||||
relative);
|
||||
FinishedSurfaceUpload();
|
||||
if (uploadSize > 0) {
|
||||
UpdateUploadSize(uploadSize);
|
||||
}
|
||||
|
||||
mUpdateDrawTarget = nullptr;
|
||||
mTextureState = Valid;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::BindTexture(GLenum aTextureUnit)
|
||||
{
|
||||
|
@ -210,46 +128,28 @@ BasicTextureImage::BindTexture(GLenum aTextureUnit)
|
|||
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DrawTarget>
|
||||
BasicTextureImage::GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt)
|
||||
{
|
||||
return gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, aSize, aFmt);
|
||||
}
|
||||
|
||||
bool
|
||||
BasicTextureImage::FinishedSurfaceUpdate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::FinishedSurfaceUpload()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0, 0) */)
|
||||
{
|
||||
IntRect bounds = aRegion.GetBounds();
|
||||
nsIntRegion region;
|
||||
if (mTextureState != Valid) {
|
||||
bounds = IntRect(0, 0, mSize.width, mSize.height);
|
||||
region = nsIntRegion(bounds);
|
||||
} else {
|
||||
if (mTextureState == Valid) {
|
||||
region = aRegion;
|
||||
} else {
|
||||
region = nsIntRegion(IntRect(0, 0, mSize.width, mSize.height));
|
||||
}
|
||||
|
||||
size_t uploadSize;
|
||||
bool needInit = mTextureState == Created;
|
||||
size_t uploadSize;
|
||||
|
||||
mTextureFormat =
|
||||
UploadSurfaceToTexture(mGLContext,
|
||||
aSurf,
|
||||
region,
|
||||
mTexture,
|
||||
mSize,
|
||||
&uploadSize,
|
||||
needInit,
|
||||
bounds.TopLeft() + IntPoint(aFrom.x, aFrom.y),
|
||||
false);
|
||||
aFrom);
|
||||
|
||||
if (uploadSize > 0) {
|
||||
UpdateUploadSize(uploadSize);
|
||||
}
|
||||
|
@ -260,8 +160,6 @@ BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion
|
|||
void
|
||||
BasicTextureImage::Resize(const gfx::IntSize& aSize)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?");
|
||||
|
||||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
||||
|
||||
// This matches the logic in UploadImageDataToTexture so that
|
||||
|
@ -317,7 +215,6 @@ BasicTextureImage::BasicTextureImage(GLuint aTexture,
|
|||
, mTexture(aTexture)
|
||||
, mTextureState(Created)
|
||||
, mGLContext(aContext)
|
||||
, mUpdateOffset(0, 0)
|
||||
{}
|
||||
|
||||
static bool
|
||||
|
@ -347,7 +244,6 @@ TiledTextureImage::TiledTextureImage(GLContext* aGL,
|
|||
, mCurrentImage(0)
|
||||
, mIterationCallback(nullptr)
|
||||
, mIterationCallbackData(nullptr)
|
||||
, mInUpdate(false)
|
||||
, mRows(0)
|
||||
, mColumns(0)
|
||||
, mGL(aGL)
|
||||
|
@ -398,13 +294,7 @@ TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion
|
|||
if (tileRegion.IsEmpty())
|
||||
continue;
|
||||
|
||||
if (CanUploadSubTextures(mGL)) {
|
||||
tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
|
||||
} else {
|
||||
// If sub-textures are unsupported, expand to tile boundaries
|
||||
tileRect.x = tileRect.y = 0;
|
||||
tileRegion = nsIntRegion(tileRect);
|
||||
}
|
||||
tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
|
||||
|
||||
result &= mImages[mCurrentImage]->
|
||||
DirectUpdate(aSurf, tileRegion, aFrom + gfx::IntPoint(xPos, yPos));
|
||||
|
@ -426,153 +316,6 @@ TiledTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion
|
|||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
|
||||
{
|
||||
if (mTextureState != Valid) {
|
||||
// if the texture hasn't been initialized yet, or something important
|
||||
// changed, we need to recreate our backing surface and force the
|
||||
// client to paint everything
|
||||
aForRegion = IntRect(IntPoint(0, 0), mSize);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIntRegion newRegion;
|
||||
|
||||
// We need to query each texture with the region it will be drawing and
|
||||
// set aForRegion to be the combination of all of these regions
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
int xPos = (i % mColumns) * mTileSize;
|
||||
int yPos = (i / mColumns) * mTileSize;
|
||||
IntRect imageRect = IntRect(IntPoint(xPos,yPos),
|
||||
mImages[i]->GetSize());
|
||||
|
||||
if (aForRegion.Intersects(imageRect)) {
|
||||
// Make a copy of the region
|
||||
nsIntRegion subRegion;
|
||||
subRegion.And(aForRegion, imageRect);
|
||||
// Translate it into tile-space
|
||||
subRegion.MoveBy(-xPos, -yPos);
|
||||
// Query region
|
||||
mImages[i]->GetUpdateRegion(subRegion);
|
||||
// Translate back
|
||||
subRegion.MoveBy(xPos, yPos);
|
||||
// Add to the accumulated region
|
||||
newRegion.Or(newRegion, subRegion);
|
||||
}
|
||||
}
|
||||
|
||||
aForRegion = newRegion;
|
||||
}
|
||||
|
||||
gfx::DrawTarget*
|
||||
TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
|
||||
{
|
||||
NS_ASSERTION(!mInUpdate, "nested update");
|
||||
mInUpdate = true;
|
||||
|
||||
// Note, we don't call GetUpdateRegion here as if the updated region is
|
||||
// fully contained in a single tile, we get to avoid iterating through
|
||||
// the tiles again (and a little copying).
|
||||
if (mTextureState != Valid)
|
||||
{
|
||||
// if the texture hasn't been initialized yet, or something important
|
||||
// changed, we need to recreate our backing surface and force the
|
||||
// client to paint everything
|
||||
aRegion = IntRect(IntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
IntRect bounds = aRegion.GetBounds();
|
||||
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
int xPos = (i % mColumns) * mTileSize;
|
||||
int yPos = (i / mColumns) * mTileSize;
|
||||
nsIntRegion imageRegion =
|
||||
nsIntRegion(IntRect(IntPoint(xPos,yPos),
|
||||
mImages[i]->GetSize()));
|
||||
|
||||
// a single Image can handle this update request
|
||||
if (imageRegion.Contains(aRegion)) {
|
||||
// adjust for tile offset
|
||||
aRegion.MoveBy(-xPos, -yPos);
|
||||
// forward the actual call
|
||||
RefPtr<gfx::DrawTarget> drawTarget = mImages[i]->BeginUpdate(aRegion);
|
||||
// caller expects container space
|
||||
aRegion.MoveBy(xPos, yPos);
|
||||
// we don't have a temp surface
|
||||
mUpdateDrawTarget = nullptr;
|
||||
// remember which image to EndUpdate
|
||||
mCurrentImage = i;
|
||||
return drawTarget.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the real updated region, taking into account the capabilities of
|
||||
// each TextureImage tile
|
||||
GetUpdateRegion(aRegion);
|
||||
mUpdateRegion = aRegion;
|
||||
bounds = aRegion.GetBounds();
|
||||
|
||||
// update covers multiple Images - create a temp surface to paint in
|
||||
gfx::SurfaceFormat format =
|
||||
(GetContentType() == gfxContentType::COLOR) ?
|
||||
gfx::SurfaceFormat::B8G8R8X8: gfx::SurfaceFormat::B8G8R8A8;
|
||||
mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO,
|
||||
bounds.Size(),
|
||||
format);
|
||||
|
||||
return mUpdateDrawTarget;;
|
||||
}
|
||||
|
||||
void
|
||||
TiledTextureImage::EndUpdate()
|
||||
{
|
||||
NS_ASSERTION(mInUpdate, "EndUpdate not in update");
|
||||
if (!mUpdateDrawTarget) { // update was to a single TextureImage
|
||||
mImages[mCurrentImage]->EndUpdate();
|
||||
mInUpdate = false;
|
||||
mTextureState = Valid;
|
||||
mTextureFormat = mImages[mCurrentImage]->GetTextureFormat();
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
|
||||
RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
|
||||
|
||||
// upload tiles from temp surface
|
||||
for (unsigned i = 0; i < mImages.Length(); i++) {
|
||||
int xPos = (i % mColumns) * mTileSize;
|
||||
int yPos = (i / mColumns) * mTileSize;
|
||||
IntRect imageRect = IntRect(IntPoint(xPos,yPos), mImages[i]->GetSize());
|
||||
|
||||
nsIntRegion subregion;
|
||||
subregion.And(mUpdateRegion, imageRect);
|
||||
if (subregion.IsEmpty())
|
||||
continue;
|
||||
subregion.MoveBy(-xPos, -yPos); // Tile-local space
|
||||
// copy tile from temp target
|
||||
gfx::DrawTarget* drawTarget = mImages[i]->BeginUpdate(subregion);
|
||||
MOZ_ASSERT(drawTarget->GetBackendType() == BackendType::CAIRO,
|
||||
"updateSnapshot should not have been converted to data");
|
||||
gfxUtils::ClipToRegion(drawTarget, subregion);
|
||||
Size size(updateData->GetSize().width,
|
||||
updateData->GetSize().height);
|
||||
drawTarget->DrawSurface(updateData,
|
||||
Rect(Point(-xPos, -yPos), size),
|
||||
Rect(Point(0, 0), size),
|
||||
DrawSurfaceOptions(),
|
||||
DrawOptions(1.0, CompositionOp::OP_SOURCE,
|
||||
AntialiasMode::NONE));
|
||||
drawTarget->PopClip();
|
||||
mImages[i]->EndUpdate();
|
||||
}
|
||||
|
||||
mUpdateDrawTarget = nullptr;
|
||||
mInUpdate = false;
|
||||
mTextureFormat = mImages[0]->GetTextureFormat();
|
||||
mTextureState = Valid;
|
||||
}
|
||||
|
||||
void TiledTextureImage::BeginBigImageIteration()
|
||||
{
|
||||
mCurrentImage = 0;
|
||||
|
|
|
@ -27,20 +27,9 @@ namespace gl {
|
|||
class GLContext;
|
||||
|
||||
/**
|
||||
* A TextureImage encapsulates a surface that can be drawn to by a
|
||||
* Thebes gfxContext and (hopefully efficiently!) synchronized to a
|
||||
* texture in the server. TextureImages are associated with one and
|
||||
* only one GLContext.
|
||||
*
|
||||
* Implementation note: TextureImages attempt to unify two categories
|
||||
* of backends
|
||||
*
|
||||
* (1) proxy to server-side object that can be bound to a texture;
|
||||
* e.g. Pixmap on X11.
|
||||
*
|
||||
* (2) efficient manager of texture memory; e.g. by having clients draw
|
||||
* into a scratch buffer which is then uploaded with
|
||||
* glTexSubImage2D().
|
||||
* A TextureImage provides a mechanism to synchronize data from a
|
||||
* surface to a texture in the server. TextureImages are associated
|
||||
* with one and only one GLContext.
|
||||
*/
|
||||
class TextureImage
|
||||
{
|
||||
|
@ -70,46 +59,6 @@ public:
|
|||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags);
|
||||
|
||||
/**
|
||||
* Returns a gfxASurface for updating |aRegion| of the client's
|
||||
* image if successul, nullptr if not. |aRegion|'s bounds must fit
|
||||
* within Size(); its coordinate space (if any) is ignored. If
|
||||
* the update begins successfully, the returned gfxASurface is
|
||||
* owned by this. Otherwise, nullptr is returned.
|
||||
*
|
||||
* |aRegion| is an inout param: the returned region is what the
|
||||
* client must repaint. Category (1) regions above can
|
||||
* efficiently handle repaints to "scattered" regions, while (2)
|
||||
* can only efficiently handle repaints to rects.
|
||||
*
|
||||
* Painting the returned surface outside of |aRegion| results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* BeginUpdate() calls cannot be "nested", and each successful
|
||||
* BeginUpdate() must be followed by exactly one EndUpdate() (see
|
||||
* below). Failure to do so can leave this in a possibly
|
||||
* inconsistent state. Unsuccessful BeginUpdate()s must not be
|
||||
* followed by EndUpdate().
|
||||
*/
|
||||
virtual gfx::DrawTarget* BeginUpdate(nsIntRegion& aRegion) = 0;
|
||||
/**
|
||||
* Retrieves the region that will require updating, given a
|
||||
* region that needs to be updated. This can be used for
|
||||
* making decisions about updating before calling BeginUpdate().
|
||||
*
|
||||
* |aRegion| is an inout param.
|
||||
*/
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion) {
|
||||
}
|
||||
/**
|
||||
* Finish the active update and synchronize with the server, if
|
||||
* necessary.
|
||||
*
|
||||
* BeginUpdate() must have been called exactly once before
|
||||
* EndUpdate().
|
||||
*/
|
||||
virtual void EndUpdate() = 0;
|
||||
|
||||
/**
|
||||
* The Image may contain several textures for different regions (tiles).
|
||||
* These functions iterate over each sub texture image tile.
|
||||
|
@ -143,18 +92,10 @@ public:
|
|||
|
||||
/**
|
||||
* Set this TextureImage's size, and ensure a texture has been
|
||||
* allocated. Must not be called between BeginUpdate and EndUpdate.
|
||||
* allocated.
|
||||
* After a resize, the contents are undefined.
|
||||
*
|
||||
* If this isn't implemented by a subclass, it will just perform
|
||||
* a dummy BeginUpdate/EndUpdate pair.
|
||||
*/
|
||||
virtual void Resize(const gfx::IntSize& aSize) {
|
||||
mSize = aSize;
|
||||
nsIntRegion r(gfx::IntRect(0, 0, aSize.width, aSize.height));
|
||||
BeginUpdate(r);
|
||||
EndUpdate();
|
||||
}
|
||||
virtual void Resize(const gfx::IntSize& aSize) = 0;
|
||||
|
||||
/**
|
||||
* Mark this texture as having valid contents. Call this after modifying
|
||||
|
@ -175,8 +116,8 @@ public:
|
|||
virtual void BindTexture(GLenum aTextureUnit) = 0;
|
||||
|
||||
/**
|
||||
* Returns the image format of the texture. Only valid after a matching
|
||||
* BeginUpdate/EndUpdate pair have been called.
|
||||
* Returns the image format of the texture. Only valid after
|
||||
* DirectUpdate has been called.
|
||||
*/
|
||||
virtual gfx::SurfaceFormat GetTextureFormat() {
|
||||
return mTextureFormat;
|
||||
|
@ -194,7 +135,6 @@ public:
|
|||
|
||||
gfx::IntSize GetSize() const;
|
||||
ContentType GetContentType() const { return mContentType; }
|
||||
virtual bool InUpdate() const = 0;
|
||||
GLenum GetWrapMode() const { return mWrapMode; }
|
||||
|
||||
void SetSamplingFilter(gfx::SamplingFilter aSamplingFilter) {
|
||||
|
@ -256,37 +196,17 @@ public:
|
|||
|
||||
virtual void BindTexture(GLenum aTextureUnit);
|
||||
|
||||
virtual gfx::DrawTarget* BeginUpdate(nsIntRegion& aRegion);
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
|
||||
virtual void EndUpdate();
|
||||
virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0));
|
||||
virtual GLuint GetTextureID() { return mTexture; }
|
||||
virtual already_AddRefed<gfx::DrawTarget>
|
||||
GetDrawTargetForUpdate(const gfx::IntSize& aSize, gfx::SurfaceFormat aFmt);
|
||||
|
||||
virtual void MarkValid() { mTextureState = Valid; }
|
||||
|
||||
// Call when drawing into the update surface is complete.
|
||||
// Returns true if textures should be upload with a relative
|
||||
// offset - See UploadSurfaceToTexture.
|
||||
virtual bool FinishedSurfaceUpdate();
|
||||
|
||||
// Call after surface data has been uploaded to a texture.
|
||||
virtual void FinishedSurfaceUpload();
|
||||
|
||||
virtual bool InUpdate() const { return !!mUpdateDrawTarget; }
|
||||
|
||||
virtual void Resize(const gfx::IntSize& aSize);
|
||||
|
||||
protected:
|
||||
GLuint mTexture;
|
||||
TextureState mTextureState;
|
||||
RefPtr<GLContext> mGLContext;
|
||||
RefPtr<gfx::DrawTarget> mUpdateDrawTarget;
|
||||
nsIntRegion mUpdateRegion;
|
||||
|
||||
// The offset into the update surface at which the update rect is located.
|
||||
nsIntPoint mUpdateOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -305,9 +225,6 @@ public:
|
|||
TextureImage::ImageFormat aImageFormat = gfx::SurfaceFormat::UNKNOWN);
|
||||
~TiledTextureImage();
|
||||
void DumpDiv();
|
||||
virtual gfx::DrawTarget* BeginUpdate(nsIntRegion& aRegion);
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
|
||||
virtual void EndUpdate();
|
||||
virtual void Resize(const gfx::IntSize& aSize);
|
||||
virtual uint32_t GetTileCount();
|
||||
virtual void BeginBigImageIteration();
|
||||
|
@ -319,7 +236,6 @@ public:
|
|||
return mImages[mCurrentImage]->GetTextureID();
|
||||
}
|
||||
virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0));
|
||||
virtual bool InUpdate() const { return mInUpdate; }
|
||||
virtual void BindTexture(GLenum);
|
||||
|
||||
protected:
|
||||
|
@ -329,14 +245,9 @@ protected:
|
|||
BigImageIterationCallback mIterationCallback;
|
||||
void* mIterationCallbackData;
|
||||
nsTArray< RefPtr<TextureImage> > mImages;
|
||||
bool mInUpdate;
|
||||
unsigned int mTileSize;
|
||||
unsigned int mRows, mColumns;
|
||||
GLContext* mGL;
|
||||
// A temporary draw target to faciliate cross-tile updates.
|
||||
RefPtr<gfx::DrawTarget> mUpdateDrawTarget;
|
||||
// The region of update requested
|
||||
nsIntRegion mUpdateRegion;
|
||||
TextureState mTextureState;
|
||||
TextureImage::ImageFormat mImageFormat;
|
||||
};
|
||||
|
|
|
@ -356,43 +356,16 @@ UploadImageDataToTexture(GLContext* gl,
|
|||
int32_t aStride,
|
||||
SurfaceFormat aFormat,
|
||||
const nsIntRegion& aDstRegion,
|
||||
GLuint& aTexture,
|
||||
GLuint aTexture,
|
||||
const gfx::IntSize& aSize,
|
||||
size_t* aOutUploadSize,
|
||||
bool aNeedInit,
|
||||
bool aPixelBuffer,
|
||||
GLenum aTextureUnit,
|
||||
GLenum aTextureTarget)
|
||||
{
|
||||
bool textureInited = aNeedInit ? false : true;
|
||||
gl->MakeCurrent();
|
||||
gl->fActiveTexture(aTextureUnit);
|
||||
|
||||
if (!aTexture) {
|
||||
gl->fGenTextures(1, &aTexture);
|
||||
gl->fBindTexture(aTextureTarget, aTexture);
|
||||
gl->fTexParameteri(aTextureTarget,
|
||||
LOCAL_GL_TEXTURE_MIN_FILTER,
|
||||
LOCAL_GL_LINEAR);
|
||||
gl->fTexParameteri(aTextureTarget,
|
||||
LOCAL_GL_TEXTURE_MAG_FILTER,
|
||||
LOCAL_GL_LINEAR);
|
||||
gl->fTexParameteri(aTextureTarget,
|
||||
LOCAL_GL_TEXTURE_WRAP_S,
|
||||
LOCAL_GL_CLAMP_TO_EDGE);
|
||||
gl->fTexParameteri(aTextureTarget,
|
||||
LOCAL_GL_TEXTURE_WRAP_T,
|
||||
LOCAL_GL_CLAMP_TO_EDGE);
|
||||
textureInited = false;
|
||||
} else {
|
||||
gl->fBindTexture(aTextureTarget, aTexture);
|
||||
}
|
||||
|
||||
nsIntRegion paintRegion;
|
||||
if (!textureInited) {
|
||||
paintRegion = nsIntRegion(aDstRegion.GetBounds());
|
||||
} else {
|
||||
paintRegion = aDstRegion;
|
||||
}
|
||||
gl->fBindTexture(aTextureTarget, aTexture);
|
||||
|
||||
GLenum format = 0;
|
||||
GLenum internalFormat = 0;
|
||||
|
@ -473,26 +446,39 @@ UploadImageDataToTexture(GLContext* gl,
|
|||
NS_ASSERTION(false, "Unhandled image surface format!");
|
||||
}
|
||||
|
||||
|
||||
// Top left point of the region's bounding rectangle.
|
||||
IntPoint topLeft = paintRegion.GetBounds().TopLeft();
|
||||
|
||||
if (aOutUploadSize) {
|
||||
*aOutUploadSize = 0;
|
||||
}
|
||||
|
||||
for (auto iter = paintRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
const IntRect& rect = iter.Get();
|
||||
// The inital data pointer is at the top left point of the region's
|
||||
// bounding rectangle. We need to find the offset of this rect
|
||||
// within the region and adjust the data pointer accordingly.
|
||||
unsigned char* rectData =
|
||||
aData + DataOffset(rect.TopLeft() - topLeft, aStride, aFormat);
|
||||
if (aNeedInit || !CanUploadSubTextures(gl)) {
|
||||
// If the texture needs initialized, or we are unable to
|
||||
// upload sub textures, then initialize and upload the entire
|
||||
// texture.
|
||||
TexImage2DHelper(gl,
|
||||
aTextureTarget,
|
||||
0,
|
||||
internalFormat,
|
||||
aSize.width,
|
||||
aSize.height,
|
||||
aStride,
|
||||
pixelSize,
|
||||
0,
|
||||
format,
|
||||
type,
|
||||
aData);
|
||||
|
||||
NS_ASSERTION(textureInited || (rect.x == 0 && rect.y == 0),
|
||||
"Must be uploading to the origin when we don't have an existing texture");
|
||||
if (aOutUploadSize && aNeedInit) {
|
||||
uint32_t texelSize = GetBytesPerTexel(internalFormat, type);
|
||||
size_t numTexels = size_t(aSize.width) * size_t(aSize.height);
|
||||
*aOutUploadSize += texelSize * numTexels;
|
||||
}
|
||||
} else {
|
||||
// Upload each rect in the region to the texture
|
||||
for (auto iter = aDstRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
const IntRect& rect = iter.Get();
|
||||
const unsigned char* rectData =
|
||||
aData + DataOffset(rect.TopLeft(), aStride, aFormat);
|
||||
|
||||
if (textureInited && CanUploadSubTextures(gl)) {
|
||||
TexSubImage2DHelper(gl,
|
||||
aTextureTarget,
|
||||
0,
|
||||
|
@ -505,25 +491,6 @@ UploadImageDataToTexture(GLContext* gl,
|
|||
format,
|
||||
type,
|
||||
rectData);
|
||||
} else {
|
||||
TexImage2DHelper(gl,
|
||||
aTextureTarget,
|
||||
0,
|
||||
internalFormat,
|
||||
rect.width,
|
||||
rect.height,
|
||||
aStride,
|
||||
pixelSize,
|
||||
0,
|
||||
format,
|
||||
type,
|
||||
rectData);
|
||||
}
|
||||
|
||||
if (aOutUploadSize && !textureInited) {
|
||||
uint32_t texelSize = GetBytesPerTexel(internalFormat, type);
|
||||
size_t numTexels = size_t(rect.width) * size_t(rect.height);
|
||||
*aOutUploadSize += texelSize * numTexels;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,22 +501,24 @@ SurfaceFormat
|
|||
UploadSurfaceToTexture(GLContext* gl,
|
||||
DataSourceSurface* aSurface,
|
||||
const nsIntRegion& aDstRegion,
|
||||
GLuint& aTexture,
|
||||
GLuint aTexture,
|
||||
const gfx::IntSize& aSize,
|
||||
size_t* aOutUploadSize,
|
||||
bool aNeedInit,
|
||||
const gfx::IntPoint& aSrcPoint,
|
||||
bool aPixelBuffer,
|
||||
GLenum aTextureUnit,
|
||||
GLenum aTextureTarget)
|
||||
{
|
||||
unsigned char* data = aPixelBuffer ? nullptr : aSurface->GetData();
|
||||
|
||||
int32_t stride = aSurface->Stride();
|
||||
SurfaceFormat format = aSurface->GetFormat();
|
||||
data += DataOffset(aSrcPoint, stride, format);
|
||||
unsigned char* data = aSurface->GetData() +
|
||||
DataOffset(aSrcPoint, stride, format);
|
||||
|
||||
return UploadImageDataToTexture(gl, data, stride, format,
|
||||
aDstRegion, aTexture, aOutUploadSize,
|
||||
aNeedInit, aPixelBuffer, aTextureUnit,
|
||||
aTextureTarget);
|
||||
aDstRegion, aTexture, aSize,
|
||||
aOutUploadSize, aNeedInit,
|
||||
aTextureUnit, aTextureTarget);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -22,62 +22,56 @@ namespace gl {
|
|||
class GLContext;
|
||||
|
||||
/**
|
||||
* Creates a RGB/RGBA texture (or uses one provided) and uploads the surface
|
||||
* contents to it within aSrcRect.
|
||||
*
|
||||
* aSrcRect.x/y will be uploaded to 0/0 in the texture, and the size
|
||||
* of the texture with be aSrcRect.width/height.
|
||||
*
|
||||
* If an existing texture is passed through aTexture, it is assumed it
|
||||
* has already been initialised with glTexImage2D (or this function),
|
||||
* and that its size is equal to or greater than aSrcRect + aDstPoint.
|
||||
* You can alternatively set the overwrite flag to true and have a new
|
||||
* texture memory block allocated.
|
||||
*
|
||||
* The aDstPoint parameter is ignored if no texture was provided
|
||||
* or aOverwrite is true.
|
||||
*
|
||||
* \param aData Image data to upload.
|
||||
* \param aDstRegion Region of texture to upload to.
|
||||
* \param aTexture Texture to use, or 0 to have one created for you.
|
||||
* \param aOutUploadSize if set, the number of bytes the texture requires will be returned here
|
||||
* \param aOverwrite Over an existing texture with a new one.
|
||||
* \param aSrcPoint Offset into aSrc where the region's bound's
|
||||
* TopLeft() sits.
|
||||
* \param aPixelBuffer Pass true to upload texture data with an
|
||||
* offset from the base data (generally for pixel buffer objects),
|
||||
* otherwise textures are upload with an absolute pointer to the data.
|
||||
* \param aTextureUnit, the texture unit used temporarily to upload the
|
||||
* surface. This testure may be overridden, clients should not rely on
|
||||
* the contents of this texture after this call or even on this
|
||||
* texture unit being active.
|
||||
* \return Surface format of this texture.
|
||||
*/
|
||||
* Uploads image data to an OpenGL texture, initializing the texture
|
||||
* first if necessary.
|
||||
*
|
||||
* \param gl The GL Context to use.
|
||||
* \param aData Start of image data of surface to upload.
|
||||
* Corresponds to the first pixel of the texture.
|
||||
* \param aStride The image data's stride.
|
||||
* \param aFormat The image data's format.
|
||||
* \param aDstRegion Region of the texture to upload.
|
||||
* \param aTexture The OpenGL texture to use. Must refer to a valid texture.
|
||||
* \param aSize The full size of the texture.
|
||||
* \param aOutUploadSize If set, the number of bytes the texture requires will
|
||||
* be returned here.
|
||||
* \param aNeedInit Indicates whether a new texture must be allocated.
|
||||
* \param aTextureUnit The texture unit used temporarily to upload the surface.
|
||||
* This may be overridden, so clients should not rely on
|
||||
* the aTexture being bound to aTextureUnit after this call,
|
||||
* or even on aTextureUnit being active.
|
||||
* \param aTextureTarget The texture target to use.
|
||||
* \return Surface format of this texture.
|
||||
*/
|
||||
gfx::SurfaceFormat
|
||||
UploadImageDataToTexture(GLContext* gl,
|
||||
unsigned char* aData,
|
||||
int32_t aStride,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const nsIntRegion& aDstRegion,
|
||||
GLuint& aTexture,
|
||||
GLuint aTexture,
|
||||
const gfx::IntSize& aSize,
|
||||
size_t* aOutUploadSize = nullptr,
|
||||
bool aNeedInit = false,
|
||||
bool aPixelBuffer = false,
|
||||
GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
|
||||
GLenum aTextureTarget = LOCAL_GL_TEXTURE_2D);
|
||||
|
||||
/**
|
||||
* Convenience wrapper around UploadImageDataToTexture for gfx::DataSourceSurface's.
|
||||
*/
|
||||
* Convenience wrapper around UploadImageDataToTexture for
|
||||
* gfx::DataSourceSurface's.
|
||||
*
|
||||
* \param aSurface The surface from which to upload image data.
|
||||
* \param aSrcPoint Offset into aSurface where this texture's data begins.
|
||||
*/
|
||||
gfx::SurfaceFormat
|
||||
UploadSurfaceToTexture(GLContext* gl,
|
||||
gfx::DataSourceSurface* aSurface,
|
||||
const nsIntRegion& aDstRegion,
|
||||
GLuint& aTexture,
|
||||
GLuint aTexture,
|
||||
const gfx::IntSize& aSize,
|
||||
size_t* aOutUploadSize = nullptr,
|
||||
bool aNeedInit = false,
|
||||
const gfx::IntPoint& aSrcPoint = gfx::IntPoint(0, 0),
|
||||
bool aPixelBuffer = false,
|
||||
GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
|
||||
GLenum aTextureTarget = LOCAL_GL_TEXTURE_2D);
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TextureImageCGL_h_
|
||||
#define TextureImageCGL_h_
|
||||
|
||||
#include "GLTextureImage.h"
|
||||
#include "GLContextTypes.h"
|
||||
#include "nsSize.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
class TextureImageCGL : public BasicTextureImage
|
||||
{
|
||||
public:
|
||||
|
||||
TextureImageCGL(GLuint aTexture,
|
||||
const gfx::IntSize& aSize,
|
||||
GLenum aWrapMode,
|
||||
ContentType aContentType,
|
||||
GLContext* aContext,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags);
|
||||
|
||||
~TextureImageCGL();
|
||||
|
||||
protected:
|
||||
bool FinishedSurfaceUpdate();
|
||||
|
||||
void FinishedSurfaceUpload();
|
||||
|
||||
private:
|
||||
|
||||
GLuint mPixelBuffer;
|
||||
bool mBoundPixelBuffer;
|
||||
};
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImageCGL(GLContext* gl,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
TextureImage::ImageFormat aImageFormat);
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
TileGenFuncCGL(GLContext* gl,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
TextureImage::Flags aFlags);
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -1,109 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TextureImageCGL.h"
|
||||
#include "GLContext.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxQuartzSurface.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxFailure.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
namespace gl {
|
||||
|
||||
TextureImageCGL::TextureImageCGL(GLuint aTexture,
|
||||
const IntSize& aSize,
|
||||
GLenum aWrapMode,
|
||||
ContentType aContentType,
|
||||
GLContext* aContext,
|
||||
TextureImage::Flags aFlags)
|
||||
: BasicTextureImage(aTexture, aSize, aWrapMode, aContentType,
|
||||
aContext, aFlags)
|
||||
, mPixelBuffer(0)
|
||||
, mBoundPixelBuffer(false)
|
||||
{}
|
||||
|
||||
TextureImageCGL::~TextureImageCGL()
|
||||
{
|
||||
if (mPixelBuffer) {
|
||||
mGLContext->MakeCurrent();
|
||||
mGLContext->fDeleteBuffers(1, &mPixelBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextureImageCGL::FinishedSurfaceUpdate()
|
||||
{
|
||||
if (mBoundPixelBuffer) {
|
||||
mGLContext->MakeCurrent();
|
||||
mGLContext->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPixelBuffer);
|
||||
mGLContext->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextureImageCGL::FinishedSurfaceUpload()
|
||||
{
|
||||
if (mBoundPixelBuffer) {
|
||||
mGLContext->MakeCurrent();
|
||||
mGLContext->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
mBoundPixelBuffer = false;
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImageCGL(GLContext* gl,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
TextureImage::ImageFormat aImageFormat)
|
||||
{
|
||||
if (!gl->IsOffscreenSizeAllowed(aSize) &&
|
||||
gfxPlatform::OffMainThreadCompositingEnabled()) {
|
||||
NS_ASSERTION(aWrapMode == LOCAL_GL_CLAMP_TO_EDGE, "Can't support wrapping with tiles!");
|
||||
RefPtr<TextureImage> t = new gl::TiledTextureImage(gl, aSize, aContentType,
|
||||
aFlags, aImageFormat);
|
||||
return t.forget();
|
||||
}
|
||||
|
||||
return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode,
|
||||
aFlags);
|
||||
}
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
TileGenFuncCGL(GLContext* gl,
|
||||
const IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
TextureImage::Flags aFlags)
|
||||
{
|
||||
bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
|
||||
gl->MakeCurrent();
|
||||
|
||||
GLuint texture;
|
||||
gl->fGenTextures(1, &texture);
|
||||
|
||||
gl->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, texture);
|
||||
|
||||
GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
|
||||
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
|
||||
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
|
||||
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
||||
gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
||||
|
||||
RefPtr<TextureImageCGL> teximage
|
||||
(new TextureImageCGL(texture, aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType,
|
||||
gl, aFlags));
|
||||
return teximage.forget();
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
|
@ -95,105 +95,6 @@ TextureImageEGL::~TextureImageEGL()
|
|||
DestroyEGLSurface();
|
||||
}
|
||||
|
||||
void
|
||||
TextureImageEGL::GetUpdateRegion(nsIntRegion& aForRegion)
|
||||
{
|
||||
if (mTextureState != Valid) {
|
||||
// if the texture hasn't been initialized yet, force the
|
||||
// client to paint everything
|
||||
aForRegion = gfx::IntRect(gfx::IntPoint(0, 0), mSize);
|
||||
}
|
||||
|
||||
// We can only draw a rectangle, not subregions due to
|
||||
// the way that our texture upload functions work. If
|
||||
// needed, we /could/ do multiple texture uploads if we have
|
||||
// non-overlapping rects, but that's a tradeoff.
|
||||
aForRegion = nsIntRegion(aForRegion.GetBounds());
|
||||
}
|
||||
|
||||
gfx::DrawTarget*
|
||||
TextureImageEGL::BeginUpdate(nsIntRegion& aRegion)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?");
|
||||
|
||||
// determine the region the client will need to repaint
|
||||
GetUpdateRegion(aRegion);
|
||||
mUpdateRect = aRegion.GetBounds();
|
||||
|
||||
//printf_stderr("BeginUpdate with updateRect [%d %d %d %d]\n", mUpdateRect.x, mUpdateRect.y, mUpdateRect.width, mUpdateRect.height);
|
||||
if (!gfx::IntRect(gfx::IntPoint(0, 0), mSize).Contains(mUpdateRect)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//printf_stderr("creating image surface %dx%d format %d\n", mUpdateRect.width, mUpdateRect.height, mUpdateFormat);
|
||||
|
||||
mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO,
|
||||
gfx::IntSize(mUpdateRect.width, mUpdateRect.height),
|
||||
mUpdateFormat);
|
||||
|
||||
return mUpdateDrawTarget;
|
||||
}
|
||||
|
||||
void
|
||||
TextureImageEGL::EndUpdate()
|
||||
{
|
||||
NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?");
|
||||
|
||||
//printf_stderr("EndUpdate: slow path");
|
||||
|
||||
// This is the slower path -- we didn't have any way to set up
|
||||
// a fast mapping between our cairo target surface and the GL
|
||||
// texture, so we have to upload data.
|
||||
|
||||
RefPtr<gfx::SourceSurface> updateSurface = nullptr;
|
||||
RefPtr<gfx::DataSourceSurface> uploadImage = nullptr;
|
||||
gfx::IntSize updateSize(mUpdateRect.width, mUpdateRect.height);
|
||||
|
||||
NS_ASSERTION(mUpdateDrawTarget->GetSize() == updateSize,
|
||||
"Upload image is the wrong size!");
|
||||
|
||||
updateSurface = mUpdateDrawTarget->Snapshot();
|
||||
uploadImage = updateSurface->GetDataSurface();
|
||||
|
||||
if (!uploadImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGLContext->MakeCurrent();
|
||||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
|
||||
|
||||
if (mTextureState != Valid) {
|
||||
NS_ASSERTION(mUpdateRect.x == 0 && mUpdateRect.y == 0 &&
|
||||
mUpdateRect.Size() == mSize,
|
||||
"Bad initial update on non-created texture!");
|
||||
|
||||
mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
|
||||
0,
|
||||
GLFormatForImage(mUpdateFormat),
|
||||
mUpdateRect.width,
|
||||
mUpdateRect.height,
|
||||
0,
|
||||
GLFormatForImage(uploadImage->GetFormat()),
|
||||
GLTypeForImage(uploadImage->GetFormat()),
|
||||
uploadImage->GetData());
|
||||
} else {
|
||||
mGLContext->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
|
||||
0,
|
||||
mUpdateRect.x,
|
||||
mUpdateRect.y,
|
||||
mUpdateRect.width,
|
||||
mUpdateRect.height,
|
||||
GLFormatForImage(uploadImage->GetFormat()),
|
||||
GLTypeForImage(uploadImage->GetFormat()),
|
||||
uploadImage->GetData());
|
||||
}
|
||||
|
||||
mUpdateDrawTarget = nullptr;
|
||||
mTextureState = Valid;
|
||||
return; // mTexture is bound
|
||||
}
|
||||
|
||||
bool
|
||||
TextureImageEGL::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0,0) */)
|
||||
{
|
||||
|
@ -214,10 +115,10 @@ TextureImageEGL::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion&
|
|||
aSurf,
|
||||
region,
|
||||
mTexture,
|
||||
mSize,
|
||||
&uploadSize,
|
||||
needInit,
|
||||
bounds.TopLeft() + gfx::IntPoint(aFrom.x, aFrom.y),
|
||||
false);
|
||||
aFrom);
|
||||
if (uploadSize > 0) {
|
||||
UpdateUploadSize(uploadSize);
|
||||
}
|
||||
|
@ -242,8 +143,6 @@ TextureImageEGL::BindTexture(GLenum aTextureUnit)
|
|||
void
|
||||
TextureImageEGL::Resize(const gfx::IntSize& aSize)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?");
|
||||
|
||||
if (mSize == aSize && mTextureState != Created)
|
||||
return;
|
||||
|
||||
|
|
|
@ -26,12 +26,6 @@ public:
|
|||
|
||||
virtual ~TextureImageEGL();
|
||||
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
|
||||
|
||||
virtual gfx::DrawTarget* BeginUpdate(nsIntRegion& aRegion);
|
||||
|
||||
virtual void EndUpdate();
|
||||
|
||||
virtual bool DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom = gfx::IntPoint(0,0));
|
||||
|
||||
virtual void BindTexture(GLenum aTextureUnit);
|
||||
|
@ -45,8 +39,6 @@ public:
|
|||
return mTexture;
|
||||
};
|
||||
|
||||
virtual bool InUpdate() const { return !!mUpdateDrawTarget; }
|
||||
|
||||
virtual void Resize(const gfx::IntSize& aSize);
|
||||
|
||||
bool BindTexImage();
|
||||
|
@ -65,9 +57,7 @@ protected:
|
|||
|
||||
GLContext* mGLContext;
|
||||
|
||||
gfx::IntRect mUpdateRect;
|
||||
gfx::SurfaceFormat mUpdateFormat;
|
||||
RefPtr<gfx::DrawTarget> mUpdateDrawTarget;
|
||||
EGLImage mEGLImage;
|
||||
GLuint mTexture;
|
||||
EGLSurface mSurface;
|
||||
|
|
|
@ -94,7 +94,6 @@ if gl_provider == 'CGL':
|
|||
# These files include Mac headers that are unfriendly to unified builds
|
||||
SOURCES += [
|
||||
"GLContextProviderCGL.mm",
|
||||
"TextureImageCGL.mm"
|
||||
]
|
||||
EXPORTS += [
|
||||
'GLContextCGL.h',
|
||||
|
|
|
@ -171,16 +171,9 @@ UseTileTexture(CompositableTextureHostRef& aTexture,
|
|||
}
|
||||
|
||||
if (!aUpdateRect.IsEmpty()) {
|
||||
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
|
||||
aTexture->Updated(nullptr);
|
||||
#else
|
||||
// We possibly upload the entire texture contents here. This is a purposeful
|
||||
// decision, as sub-image upload can often be slow and/or unreliable, but
|
||||
// we may want to reevaluate this in the future.
|
||||
// For !HasIntermediateBuffer() textures, this is likely a no-op.
|
||||
nsIntRegion region = aUpdateRect;
|
||||
aTexture->Updated(®ion);
|
||||
#endif
|
||||
}
|
||||
|
||||
aTexture->PrepareTextureSource(aTextureSource);
|
||||
|
|
|
@ -42,8 +42,6 @@ GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const gfx::IntRec
|
|||
TextureImage *aDst, const gfx::IntRect& aDstRect)
|
||||
{
|
||||
GLContext *gl = mCompositor->gl();
|
||||
NS_ASSERTION(!aSrc->InUpdate(), "Source texture is in update!");
|
||||
NS_ASSERTION(!aDst->InUpdate(), "Destination texture is in update!");
|
||||
|
||||
if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty())
|
||||
return;
|
||||
|
|
|
@ -193,9 +193,6 @@ TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface,
|
|||
|
||||
mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset);
|
||||
|
||||
if (mTexImage->InUpdate()) {
|
||||
mTexImage->EndUpdate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,28 @@ struct gfxQuaternion : public mozilla::gfx::BasePoint4D<gfxFloat, gfxQuaternion>
|
|||
z = -z;
|
||||
}
|
||||
|
||||
// Convert from |direction axis, angle| pair to gfxQuaternion.
|
||||
//
|
||||
// Reference:
|
||||
// https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
|
||||
//
|
||||
// if the direction axis is (x, y, z) = xi + yj + zk,
|
||||
// and the angle is |theta|, this formula can be done using
|
||||
// an extension of Euler's formula:
|
||||
// q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))
|
||||
// = cos(theta/2) +
|
||||
// x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k
|
||||
// Note: aDirection should be an unit vector and
|
||||
// the unit of aAngle should be Radian.
|
||||
gfxQuaternion(const mozilla::gfx::Point3D &aDirection, gfxFloat aAngle) {
|
||||
MOZ_ASSERT(mozilla::gfx::FuzzyEqual(aDirection.Length(), 1.0f),
|
||||
"aDirection should be an unit vector");
|
||||
x = aDirection.x * sin(aAngle/2.0);
|
||||
y = aDirection.y * sin(aAngle/2.0);
|
||||
z = aDirection.z * sin(aAngle/2.0);
|
||||
w = cos(aAngle/2.0);
|
||||
}
|
||||
|
||||
gfxQuaternion Slerp(const gfxQuaternion &aOther, gfxFloat aCoeff) {
|
||||
gfxFloat dot = mozilla::clamped(DotProduct(aOther), -1.0, 1.0);
|
||||
if (dot == 1.0) {
|
||||
|
|
|
@ -89,3 +89,15 @@ VRControllerManager::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
|
|||
MOZ_ASSERT(vm);
|
||||
vm->NotifyGamepadChange<dom::GamepadButtonInformation>(a);
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerManager::NewAxisMove(uint32_t aIndex, uint32_t aAxis,
|
||||
double aValue)
|
||||
{
|
||||
dom::GamepadAxisInformation a(aIndex, dom::GamepadServiceType::VR,
|
||||
aAxis, aValue);
|
||||
|
||||
VRManager* vm = VRManager::Get();
|
||||
MOZ_ASSERT(vm);
|
||||
vm->NotifyGamepadChange<dom::GamepadAxisInformation>(a);
|
||||
}
|
||||
|
|
|
@ -252,6 +252,7 @@ public:
|
|||
virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult) = 0;
|
||||
virtual void ScanForDevices() = 0;
|
||||
void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
|
||||
void NewAxisMove(uint32_t aIndex, uint32_t aAxis, double aValue);
|
||||
void AddGamepad(const char* aID, dom::GamepadMappingType aMapping,
|
||||
uint32_t aNumButtons, uint32_t aNumAxes);
|
||||
|
||||
|
@ -266,6 +267,8 @@ protected:
|
|||
private:
|
||||
virtual void HandleButtonPress(uint32_t aControllerIdx,
|
||||
uint64_t aButtonPressed) = 0;
|
||||
virtual void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
|
||||
float aValue) = 0;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
|
|
@ -72,16 +72,24 @@ const uint64_t gOpenVRButtonMask[] = {
|
|||
const uint32_t gNumOpenVRButtonMask = sizeof(gOpenVRButtonMask) /
|
||||
sizeof(uint64_t);
|
||||
|
||||
const uint64_t gOpenVRAxisMask[] = {
|
||||
vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_Axis0),
|
||||
vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_Axis1),
|
||||
vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_Axis2),
|
||||
vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_Axis3),
|
||||
vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_Axis4)
|
||||
enum class VRControllerAxisType : uint16_t {
|
||||
TrackpadXAxis,
|
||||
TrackpadYAxis,
|
||||
Trigger,
|
||||
NumVRControllerAxisType
|
||||
};
|
||||
|
||||
const uint32_t gNumOpenVRAxisMask = sizeof(gOpenVRAxisMask) /
|
||||
sizeof(uint64_t);
|
||||
#define VRControllerAxis(aButtonId) (aButtonId - vr::EVRButtonId::k_EButton_Axis0)
|
||||
|
||||
const uint32_t gOpenVRAxes[] = {
|
||||
VRControllerAxis(vr::EVRButtonId::k_EButton_Axis0),
|
||||
VRControllerAxis(vr::EVRButtonId::k_EButton_Axis0),
|
||||
VRControllerAxis(vr::EVRButtonId::k_EButton_Axis1)
|
||||
};
|
||||
|
||||
const uint32_t gNumOpenVRAxis = sizeof(gOpenVRAxes) /
|
||||
sizeof(uint32_t);
|
||||
|
||||
|
||||
bool
|
||||
LoadOpenVRRuntime()
|
||||
|
@ -479,7 +487,7 @@ VRControllerOpenVR::VRControllerOpenVR()
|
|||
mControllerInfo.mControllerName.AssignLiteral("OpenVR HMD");
|
||||
mControllerInfo.mMappingType = dom::GamepadMappingType::_empty;
|
||||
mControllerInfo.mNumButtons = gNumOpenVRButtonMask;
|
||||
mControllerInfo.mNumAxes = gNumOpenVRAxisMask;
|
||||
mControllerInfo.mNumAxes = gNumOpenVRAxis;
|
||||
}
|
||||
|
||||
VRControllerOpenVR::~VRControllerOpenVR()
|
||||
|
@ -559,6 +567,7 @@ VRControllerManagerOpenVR::HandleInput()
|
|||
{
|
||||
RefPtr<impl::VRControllerOpenVR> controller;
|
||||
vr::VRControllerState_t state;
|
||||
uint32_t axis = 0;
|
||||
|
||||
if (!mOpenVRInstalled) {
|
||||
return;
|
||||
|
@ -578,7 +587,17 @@ VRControllerManagerOpenVR::HandleInput()
|
|||
HandleButtonPress(controller->GetIndex(), state.ulButtonPressed);
|
||||
}
|
||||
|
||||
// Handle Axis support in Bug 1299930
|
||||
axis = static_cast<uint32_t>(VRControllerAxisType::TrackpadXAxis);
|
||||
HandleAxisMove(controller->GetIndex(), axis,
|
||||
state.rAxis[gOpenVRAxes[axis]].x);
|
||||
|
||||
axis = static_cast<uint32_t>(VRControllerAxisType::TrackpadYAxis);
|
||||
HandleAxisMove(controller->GetIndex(), axis,
|
||||
state.rAxis[gOpenVRAxes[axis]].y);
|
||||
|
||||
axis = static_cast<uint32_t>(VRControllerAxisType::Trigger);
|
||||
HandleAxisMove(controller->GetIndex(), axis,
|
||||
state.rAxis[gOpenVRAxes[axis]].x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -595,6 +614,15 @@ VRControllerManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
|
||||
float aValue)
|
||||
{
|
||||
if (aValue != 0.0f) {
|
||||
NewAxisMove(aControllerIdx, aAxis, aValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VRControllerManagerOpenVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
|
||||
{
|
||||
|
@ -635,7 +663,7 @@ VRControllerManagerOpenVR::ScanForDevices()
|
|||
|
||||
// Not already present, add it.
|
||||
AddGamepad("OpenVR Gamepad", GamepadMappingType::_empty,
|
||||
gNumOpenVRButtonMask, gNumOpenVRAxisMask);
|
||||
gNumOpenVRButtonMask, gNumOpenVRAxis);
|
||||
++mControllerCount;
|
||||
}
|
||||
}
|
|
@ -122,6 +122,8 @@ private:
|
|||
|
||||
virtual void HandleButtonPress(uint32_t aControllerIdx,
|
||||
uint64_t aButtonPressed) override;
|
||||
virtual void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
|
||||
float aValue) override;
|
||||
|
||||
bool mOpenVRInstalled;
|
||||
nsTArray<RefPtr<impl::VRControllerOpenVR>> mOpenVRController;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -87,6 +87,8 @@ public:
|
|||
* should be calculated.
|
||||
* @param aEndValue The end of the interval for which the distance
|
||||
* should be calculated.
|
||||
* @param aStyleContext The style context to use for processing the
|
||||
* translate part of transforms.
|
||||
* @param aDistance The result of the calculation.
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
|
@ -94,6 +96,7 @@ public:
|
|||
ComputeDistance(nsCSSPropertyID aProperty,
|
||||
const StyleAnimationValue& aStartValue,
|
||||
const StyleAnimationValue& aEndValue,
|
||||
nsStyleContext* aStyleContext,
|
||||
double& aDistance);
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "nsCSSKeywords.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "gfxMatrix.h"
|
||||
#include "gfxQuaternion.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
|
@ -176,7 +177,7 @@ ProcessTranslatePart(const nsCSSValue& aValue,
|
|||
nsPresContext::AppUnitsPerCSSPixel());
|
||||
// We want to avoid calling aDimensionGetter if there's no percentage to be
|
||||
// resolved (for performance reasons - see TransformReferenceBox).
|
||||
if (percent != 0.0f && aRefBox) {
|
||||
if (percent != 0.0f && aRefBox && !aRefBox->IsEmpty()) {
|
||||
translation += percent *
|
||||
NSAppUnitsToFloatPixels((aRefBox->*aDimensionGetter)(),
|
||||
nsPresContext::AppUnitsPerCSSPixel());
|
||||
|
@ -224,7 +225,7 @@ ProcessMatrix(Matrix4x4& aMatrix,
|
|||
aMatrix = result * aMatrix;
|
||||
}
|
||||
|
||||
static void
|
||||
static void
|
||||
ProcessMatrix3D(Matrix4x4& aMatrix,
|
||||
const nsCSSValue::Array* aData,
|
||||
nsStyleContext* aContext,
|
||||
|
@ -335,7 +336,7 @@ ProcessTranslateY(Matrix4x4& aMatrix,
|
|||
aMatrix.PreTranslate(temp);
|
||||
}
|
||||
|
||||
static void
|
||||
static void
|
||||
ProcessTranslateZ(Matrix4x4& aMatrix,
|
||||
const nsCSSValue::Array* aData,
|
||||
nsStyleContext* aContext,
|
||||
|
@ -408,8 +409,8 @@ ProcessTranslate3D(Matrix4x4& aMatrix,
|
|||
/* Helper function to set up a scale matrix. */
|
||||
static void
|
||||
ProcessScaleHelper(Matrix4x4& aMatrix,
|
||||
float aXScale,
|
||||
float aYScale,
|
||||
float aXScale,
|
||||
float aYScale,
|
||||
float aZScale)
|
||||
{
|
||||
aMatrix.PreScale(aXScale, aYScale, aZScale);
|
||||
|
@ -460,7 +461,7 @@ ProcessScale(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
|
|||
const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX :
|
||||
aData->Item(2));
|
||||
|
||||
ProcessScaleHelper(aMatrix,
|
||||
ProcessScaleHelper(aMatrix,
|
||||
scaleX.GetFloatValue(),
|
||||
scaleY.GetFloatValue(),
|
||||
1.0f);
|
||||
|
@ -545,7 +546,7 @@ ProcessRotate3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
|
|||
aMatrix = temp * aMatrix;
|
||||
}
|
||||
|
||||
static void
|
||||
static void
|
||||
ProcessPerspective(Matrix4x4& aMatrix,
|
||||
const nsCSSValue::Array* aData,
|
||||
nsStyleContext *aContext,
|
||||
|
@ -666,7 +667,7 @@ MatrixForTransformFunction(Matrix4x4& aMatrix,
|
|||
break;
|
||||
case eCSSKeyword_perspective:
|
||||
*aContains3dTransform = true;
|
||||
ProcessPerspective(aMatrix, aData, aContext, aPresContext,
|
||||
ProcessPerspective(aMatrix, aData, aContext, aPresContext,
|
||||
aConditions);
|
||||
break;
|
||||
default:
|
||||
|
@ -685,6 +686,32 @@ TransformFunctionOf(const nsCSSValue::Array* aData)
|
|||
return aData->Item(0).GetKeywordValue();
|
||||
}
|
||||
|
||||
void
|
||||
SetIdentityMatrix(nsCSSValue::Array* aMatrix)
|
||||
{
|
||||
MOZ_ASSERT(aMatrix, "aMatrix should be non-null");
|
||||
|
||||
nsCSSKeyword tfunc = TransformFunctionOf(aMatrix);
|
||||
MOZ_ASSERT(tfunc == eCSSKeyword_matrix ||
|
||||
tfunc == eCSSKeyword_matrix3d,
|
||||
"Only accept matrix and matrix3d");
|
||||
|
||||
if (tfunc == eCSSKeyword_matrix) {
|
||||
MOZ_ASSERT(aMatrix->Count() == 7, "Invalid matrix");
|
||||
Matrix m;
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aMatrix->Count() == 17, "Invalid matrix3d");
|
||||
Matrix4x4 m;
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
|
||||
}
|
||||
}
|
||||
|
||||
Matrix4x4
|
||||
ReadTransforms(const nsCSSValueList* aList,
|
||||
nsStyleContext* aContext,
|
||||
|
@ -721,4 +748,318 @@ ReadTransforms(const nsCSSValueList* aList,
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* The relevant section of the transitions specification:
|
||||
* http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
|
||||
* defers all of the details to the 2-D and 3-D transforms specifications.
|
||||
* For the 2-D transforms specification (all that's relevant for us, right
|
||||
* now), the relevant section is:
|
||||
* http://dev.w3.org/csswg/css3-2d-transforms/#animation
|
||||
* This, in turn, refers to the unmatrix program in Graphics Gems,
|
||||
* available from http://tog.acm.org/resources/GraphicsGems/ , and in
|
||||
* particular as the file GraphicsGems/gemsii/unmatrix.c
|
||||
* in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
|
||||
*
|
||||
* The unmatrix reference is for general 3-D transform matrices (any of the
|
||||
* 16 components can have any value).
|
||||
*
|
||||
* For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:
|
||||
*
|
||||
* [ A C E ]
|
||||
* [ B D F ]
|
||||
* [ 0 0 1 ]
|
||||
*
|
||||
* For that case, I believe the algorithm in unmatrix reduces to:
|
||||
*
|
||||
* (1) If A * D - B * C == 0, the matrix is singular. Fail.
|
||||
*
|
||||
* (2) Set translation components (Tx and Ty) to the translation parts of
|
||||
* the matrix (E and F) and then ignore them for the rest of the time.
|
||||
* (For us, E and F each actually consist of three constants: a
|
||||
* length, a multiplier for the width, and a multiplier for the
|
||||
* height. This actually requires its own decomposition, but I'll
|
||||
* keep that separate.)
|
||||
*
|
||||
* (3) Let the X scale (Sx) be sqrt(A^2 + B^2). Then divide both A and B
|
||||
* by it.
|
||||
*
|
||||
* (4) Let the XY shear (K) be A * C + B * D. From C, subtract A times
|
||||
* the XY shear. From D, subtract B times the XY shear.
|
||||
*
|
||||
* (5) Let the Y scale (Sy) be sqrt(C^2 + D^2). Divide C, D, and the XY
|
||||
* shear (K) by it.
|
||||
*
|
||||
* (6) At this point, A * D - B * C is either 1 or -1. If it is -1,
|
||||
* negate the XY shear (K), the X scale (Sx), and A, B, C, and D.
|
||||
* (Alternatively, we could negate the XY shear (K) and the Y scale
|
||||
* (Sy).)
|
||||
*
|
||||
* (7) Let the rotation be R = atan2(B, A).
|
||||
*
|
||||
* Then the resulting decomposed transformation is:
|
||||
*
|
||||
* translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)
|
||||
*
|
||||
* An interesting result of this is that all of the simple transform
|
||||
* functions (i.e., all functions other than matrix()), in isolation,
|
||||
* decompose back to themselves except for:
|
||||
* 'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes
|
||||
* to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the
|
||||
* alternate sign possibilities that would get fixed in step 6):
|
||||
* In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
|
||||
* Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
|
||||
* In step 4, the XY shear is sin(φ).
|
||||
* Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ).
|
||||
* Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ).
|
||||
* Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
|
||||
* Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
|
||||
* In step 7, the rotation is thus φ.
|
||||
*
|
||||
* skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
|
||||
* to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
|
||||
* the alternate sign possibilities that would get fixed in step 6):
|
||||
* In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
|
||||
* Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
|
||||
* In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
|
||||
* Thus, after step 4,
|
||||
* C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
|
||||
* D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
|
||||
* Thus, in step 5, the Y scale is sqrt(C² + D²) =
|
||||
* sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -
|
||||
* 2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
|
||||
* (sin²(φ)cos²(φ) + cos⁴(φ))) =
|
||||
* sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
|
||||
* cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
|
||||
* we avoid flipping in step 6).
|
||||
* After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
|
||||
* (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
|
||||
* (dividing both numerator and denominator by cos(φ))
|
||||
* (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
|
||||
* (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
|
||||
* Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
|
||||
* In step 7, the rotation is thus φ.
|
||||
*
|
||||
* To check this result, we can multiply things back together:
|
||||
*
|
||||
* [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ) 0 ]
|
||||
* [ sin(φ) cos(φ) ] [ 0 1 ] [ 0 cos(φ) ]
|
||||
*
|
||||
* [ cos(φ) cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ) 0 ]
|
||||
* [ sin(φ) sin(φ)tan(θ + φ) + cos(φ) ] [ 0 cos(φ) ]
|
||||
*
|
||||
* but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
|
||||
* cos(φ)tan(θ + φ) - sin(φ)
|
||||
* = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
|
||||
* = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
|
||||
* = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
|
||||
* = tan(θ) (cos(φ) + sin(φ)tan(φ))
|
||||
* = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
|
||||
* = tan(θ) sec(φ)
|
||||
* and
|
||||
* sin(φ)tan(θ + φ) + cos(φ)
|
||||
* = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
|
||||
* = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
|
||||
* = sec(φ) (sin²(φ) + cos²(φ))
|
||||
* = sec(φ)
|
||||
* so the above is:
|
||||
* [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ]
|
||||
* [ sin(φ) sec(φ) ] [ 0 cos(φ) ]
|
||||
*
|
||||
* [ 1 tan(θ) ]
|
||||
* [ tan(φ) 1 ]
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decompose2DMatrix implements the above decomposition algorithm.
|
||||
*/
|
||||
|
||||
bool
|
||||
Decompose2DMatrix(const Matrix& aMatrix,
|
||||
Point3D& aScale,
|
||||
ShearArray& aShear,
|
||||
gfxQuaternion& aRotate,
|
||||
Point3D& aTranslate)
|
||||
{
|
||||
float A = aMatrix._11,
|
||||
B = aMatrix._12,
|
||||
C = aMatrix._21,
|
||||
D = aMatrix._22;
|
||||
if (A * D == B * C) {
|
||||
// singular matrix
|
||||
return false;
|
||||
}
|
||||
|
||||
float scaleX = sqrt(A * A + B * B);
|
||||
A /= scaleX;
|
||||
B /= scaleX;
|
||||
|
||||
float XYshear = A * C + B * D;
|
||||
C -= A * XYshear;
|
||||
D -= B * XYshear;
|
||||
|
||||
float scaleY = sqrt(C * C + D * D);
|
||||
C /= scaleY;
|
||||
D /= scaleY;
|
||||
XYshear /= scaleY;
|
||||
|
||||
// A*D - B*C should now be 1 or -1
|
||||
NS_ASSERTION(0.99 < Abs(A*D - B*C) && Abs(A*D - B*C) < 1.01,
|
||||
"determinant should now be 1 or -1");
|
||||
if (A * D < B * C) {
|
||||
A = -A;
|
||||
B = -B;
|
||||
C = -C;
|
||||
D = -D;
|
||||
XYshear = -XYshear;
|
||||
scaleX = -scaleX;
|
||||
}
|
||||
|
||||
float rotate = atan2f(B, A);
|
||||
aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2));
|
||||
aShear[ShearType::XYSHEAR] = XYshear;
|
||||
aScale.x = scaleX;
|
||||
aScale.y = scaleY;
|
||||
aTranslate.x = aMatrix._31;
|
||||
aTranslate.y = aMatrix._32;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the unmatrix algorithm, specified by:
|
||||
*
|
||||
* http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix
|
||||
*
|
||||
* This, in turn, refers to the unmatrix program in Graphics Gems,
|
||||
* available from http://tog.acm.org/resources/GraphicsGems/ , and in
|
||||
* particular as the file GraphicsGems/gemsii/unmatrix.c
|
||||
* in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
|
||||
*/
|
||||
bool
|
||||
Decompose3DMatrix(const Matrix4x4& aMatrix,
|
||||
Point3D& aScale,
|
||||
ShearArray& aShear,
|
||||
gfxQuaternion& aRotate,
|
||||
Point3D& aTranslate,
|
||||
Point4D& aPerspective)
|
||||
{
|
||||
Matrix4x4 local = aMatrix;
|
||||
|
||||
if (local[3][3] == 0) {
|
||||
return false;
|
||||
}
|
||||
/* Normalize the matrix */
|
||||
local.Normalize();
|
||||
|
||||
/**
|
||||
* perspective is used to solve for perspective, but it also provides
|
||||
* an easy way to test for singularity of the upper 3x3 component.
|
||||
*/
|
||||
Matrix4x4 perspective = local;
|
||||
Point4D empty(0, 0, 0, 1);
|
||||
perspective.SetTransposedVector(3, empty);
|
||||
|
||||
if (perspective.Determinant() == 0.0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* First, isolate perspective. */
|
||||
if (local[0][3] != 0 || local[1][3] != 0 ||
|
||||
local[2][3] != 0) {
|
||||
/* aPerspective is the right hand side of the equation. */
|
||||
aPerspective = local.TransposedVector(3);
|
||||
|
||||
/**
|
||||
* Solve the equation by inverting perspective and multiplying
|
||||
* aPerspective by the inverse.
|
||||
*/
|
||||
perspective.Invert();
|
||||
aPerspective = perspective.TransposeTransform4D(aPerspective);
|
||||
|
||||
/* Clear the perspective partition */
|
||||
local.SetTransposedVector(3, empty);
|
||||
} else {
|
||||
aPerspective = Point4D(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
/* Next take care of translation */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
aTranslate[i] = local[3][i];
|
||||
local[3][i] = 0;
|
||||
}
|
||||
|
||||
/* Now get scale and shear. */
|
||||
|
||||
/* Compute X scale factor and normalize first row. */
|
||||
aScale.x = local[0].Length();
|
||||
local[0] /= aScale.x;
|
||||
|
||||
/* Compute XY shear factor and make 2nd local orthogonal to 1st. */
|
||||
aShear[ShearType::XYSHEAR] = local[0].DotProduct(local[1]);
|
||||
local[1] -= local[0] * aShear[ShearType::XYSHEAR];
|
||||
|
||||
/* Now, compute Y scale and normalize 2nd local. */
|
||||
aScale.y = local[1].Length();
|
||||
local[1] /= aScale.y;
|
||||
aShear[ShearType::XYSHEAR] /= aScale.y;
|
||||
|
||||
/* Compute XZ and YZ shears, make 3rd local orthogonal */
|
||||
aShear[ShearType::XZSHEAR] = local[0].DotProduct(local[2]);
|
||||
local[2] -= local[0] * aShear[ShearType::XZSHEAR];
|
||||
aShear[ShearType::YZSHEAR] = local[1].DotProduct(local[2]);
|
||||
local[2] -= local[1] * aShear[ShearType::YZSHEAR];
|
||||
|
||||
/* Next, get Z scale and normalize 3rd local. */
|
||||
aScale.z = local[2].Length();
|
||||
local[2] /= aScale.z;
|
||||
|
||||
aShear[ShearType::XZSHEAR] /= aScale.z;
|
||||
aShear[ShearType::YZSHEAR] /= aScale.z;
|
||||
|
||||
/**
|
||||
* At this point, the matrix (in locals) is orthonormal.
|
||||
* Check for a coordinate system flip. If the determinant
|
||||
* is -1, then negate the matrix and the scaling factors.
|
||||
*/
|
||||
if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) {
|
||||
aScale *= -1;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
local[i] *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, get the rotations out */
|
||||
aRotate = gfxQuaternion(local);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Matrix
|
||||
CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray)
|
||||
{
|
||||
MOZ_ASSERT(aArray &&
|
||||
TransformFunctionOf(aArray) == eCSSKeyword_matrix &&
|
||||
aArray->Count() == 7);
|
||||
Matrix m(aArray->Item(1).GetFloatValue(),
|
||||
aArray->Item(2).GetFloatValue(),
|
||||
aArray->Item(3).GetFloatValue(),
|
||||
aArray->Item(4).GetFloatValue(),
|
||||
aArray->Item(5).GetFloatValue(),
|
||||
aArray->Item(6).GetFloatValue());
|
||||
return m;
|
||||
}
|
||||
|
||||
Matrix4x4
|
||||
CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray)
|
||||
{
|
||||
MOZ_ASSERT(aArray &&
|
||||
TransformFunctionOf(aArray) == eCSSKeyword_matrix3d &&
|
||||
aArray->Count() == 17);
|
||||
gfx::Float array[16];
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
array[i] = aArray->Item(i+1).GetFloatValue();
|
||||
}
|
||||
Matrix4x4 m(array);
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace nsStyleTransformMatrix
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
#ifndef nsStyleTransformMatrix_h_
|
||||
#define nsStyleTransformMatrix_h_
|
||||
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "nsCSSValue.h"
|
||||
|
||||
class nsIFrame;
|
||||
class nsStyleContext;
|
||||
class nsPresContext;
|
||||
struct gfxQuaternion;
|
||||
struct nsRect;
|
||||
namespace mozilla {
|
||||
class RuleNodeCacheConditions;
|
||||
|
@ -108,6 +110,10 @@ namespace nsStyleTransformMatrix {
|
|||
return mHeight;
|
||||
}
|
||||
|
||||
bool IsEmpty() {
|
||||
return !mFrame;
|
||||
}
|
||||
|
||||
private:
|
||||
// We don't really need to prevent copying, but since none of our consumers
|
||||
// currently need to copy, preventing copying may allow us to catch some
|
||||
|
@ -127,6 +133,8 @@ namespace nsStyleTransformMatrix {
|
|||
*/
|
||||
nsCSSKeyword TransformFunctionOf(const nsCSSValue::Array* aData);
|
||||
|
||||
void SetIdentityMatrix(nsCSSValue::Array* aMatrix);
|
||||
|
||||
float ProcessTranslatePart(const nsCSSValue& aValue,
|
||||
nsStyleContext* aContext,
|
||||
nsPresContext* aPresContext,
|
||||
|
@ -169,6 +177,37 @@ namespace nsStyleTransformMatrix {
|
|||
TransformReferenceBox& aBounds,
|
||||
float aAppUnitsPerMatrixUnit,
|
||||
bool* aContains3dTransform);
|
||||
|
||||
// Shear type for decomposition.
|
||||
enum class ShearType {
|
||||
XYSHEAR,
|
||||
XZSHEAR,
|
||||
YZSHEAR,
|
||||
Count
|
||||
};
|
||||
using ShearArray =
|
||||
mozilla::EnumeratedArray<ShearType, ShearType::Count, float>;
|
||||
|
||||
/*
|
||||
* Implements the 2d transform matrix decomposition algorithm.
|
||||
*/
|
||||
bool Decompose2DMatrix(const mozilla::gfx::Matrix& aMatrix,
|
||||
mozilla::gfx::Point3D& aScale,
|
||||
ShearArray& aShear,
|
||||
gfxQuaternion& aRotate,
|
||||
mozilla::gfx::Point3D& aTranslate);
|
||||
/*
|
||||
* Implements the 3d transform matrix decomposition algorithm.
|
||||
*/
|
||||
bool Decompose3DMatrix(const mozilla::gfx::Matrix4x4& aMatrix,
|
||||
mozilla::gfx::Point3D& aScale,
|
||||
ShearArray& aShear,
|
||||
gfxQuaternion& aRotate,
|
||||
mozilla::gfx::Point3D& aTranslate,
|
||||
mozilla::gfx::Point4D& aPerspective);
|
||||
|
||||
mozilla::gfx::Matrix CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray);
|
||||
mozilla::gfx::Matrix4x4 CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray);
|
||||
} // namespace nsStyleTransformMatrix
|
||||
|
||||
#endif
|
||||
|
|
|
@ -535,9 +535,6 @@ NrIceCtx::Initialize(const std::string& ufrag,
|
|||
NR_ICE_CTX_FLAGS_ANSWERER;
|
||||
flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
|
||||
switch (policy_) {
|
||||
case ICE_POLICY_NONE:
|
||||
MOZ_CRASH();
|
||||
break;
|
||||
case ICE_POLICY_RELAY:
|
||||
flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY;
|
||||
break;
|
||||
|
@ -837,9 +834,6 @@ abort:
|
|||
|
||||
nsresult NrIceCtx::StartGathering(bool default_route_only, bool proxy_only) {
|
||||
ASSERT_ON_THREAD(sts_target_);
|
||||
if (policy_ == ICE_POLICY_NONE) {
|
||||
return NS_OK;
|
||||
}
|
||||
SetGatheringState(ICE_CTX_GATHER_STARTED);
|
||||
|
||||
if (default_route_only) {
|
||||
|
@ -927,11 +921,6 @@ nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) {
|
|||
nsresult NrIceCtx::StartChecks() {
|
||||
int r;
|
||||
|
||||
if (policy_ == ICE_POLICY_NONE) {
|
||||
MOZ_MTLOG(ML_ERROR, "Couldn't start peer checks because policy == none");
|
||||
SetConnectionState(ICE_CTX_FAILED);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
r=nr_ice_peer_ctx_pair_candidates(peer_);
|
||||
if (r) {
|
||||
MOZ_MTLOG(ML_ERROR, "Couldn't pair candidates on "
|
||||
|
|
|
@ -209,8 +209,7 @@ class NrIceCtx {
|
|||
ICE_CONTROLLED
|
||||
};
|
||||
|
||||
enum Policy { ICE_POLICY_NONE,
|
||||
ICE_POLICY_RELAY,
|
||||
enum Policy { ICE_POLICY_RELAY,
|
||||
ICE_POLICY_NO_HOST,
|
||||
ICE_POLICY_ALL
|
||||
};
|
||||
|
|
|
@ -479,9 +479,6 @@ PeerConnectionConfiguration::Init(const RTCConfiguration& aSrc)
|
|||
}
|
||||
|
||||
switch (aSrc.mIceTransportPolicy) {
|
||||
case dom::RTCIceTransportPolicy::None:
|
||||
setIceTransportPolicy(NrIceCtx::ICE_POLICY_NONE);
|
||||
break;
|
||||
case dom::RTCIceTransportPolicy::Relay:
|
||||
setIceTransportPolicy(NrIceCtx::ICE_POLICY_RELAY);
|
||||
break;
|
||||
|
|
|
@ -49,7 +49,6 @@ import org.mozilla.gecko.GeckoAppShell.AppStateListener;
|
|||
public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateListener {
|
||||
private final static String TAG = "WEBRTC-JC";
|
||||
|
||||
private static SurfaceHolder localPreview;
|
||||
// Only non-null while capturing, accessed exclusively from synchronized methods.
|
||||
Camera camera;
|
||||
private Camera.CameraInfo info;
|
||||
|
@ -81,23 +80,13 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateL
|
|||
private int frameCount;
|
||||
private int frameDropRatio;
|
||||
|
||||
// Requests future capturers to send their frames to |localPreview| directly.
|
||||
public static void setLocalPreview(SurfaceHolder localPreview) {
|
||||
// It is a gross hack that this is a class-static. Doing it right would
|
||||
// mean plumbing this through the C++ API and using it from
|
||||
// webrtc/examples/android/media_demo's MediaEngine class.
|
||||
VideoCaptureAndroid.localPreview = localPreview;
|
||||
}
|
||||
|
||||
@WebRTCJNITarget
|
||||
public VideoCaptureAndroid(int id, long native_capturer) {
|
||||
this.id = id;
|
||||
this.native_capturer = native_capturer;
|
||||
this.context = GetContext();
|
||||
if(android.os.Build.VERSION.SDK_INT>8) {
|
||||
this.info = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(id, info);
|
||||
}
|
||||
this.info = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(id, info);
|
||||
mCaptureRotation = GetRotateAmount();
|
||||
}
|
||||
|
||||
|
@ -133,21 +122,13 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateL
|
|||
case Surface.ROTATION_180: degrees = 180; break;
|
||||
case Surface.ROTATION_270: degrees = 270; break;
|
||||
}
|
||||
if(android.os.Build.VERSION.SDK_INT>8) {
|
||||
int result;
|
||||
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
result = (info.orientation + degrees) % 360;
|
||||
} else { // back-facing
|
||||
result = (info.orientation - degrees + 360) % 360;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// Assume 90deg orientation for Froyo devices.
|
||||
// Only back-facing cameras are supported in Froyo.
|
||||
int orientation = 90;
|
||||
int result = (orientation - degrees + 360) % 360;
|
||||
return result;
|
||||
int result;
|
||||
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
result = (info.orientation + degrees) % 360;
|
||||
} else { // back-facing
|
||||
result = (info.orientation - degrees + 360) % 360;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return the global application context.
|
||||
|
@ -203,51 +184,34 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateL
|
|||
}
|
||||
Throwable error = null;
|
||||
try {
|
||||
if(android.os.Build.VERSION.SDK_INT>8) {
|
||||
camera = Camera.open(id);
|
||||
} else {
|
||||
camera = Camera.open();
|
||||
}
|
||||
camera = Camera.open(id);
|
||||
|
||||
localPreview = ViERenderer.GetLocalRenderer();
|
||||
if (localPreview != null) {
|
||||
localPreview.addCallback(this);
|
||||
if (localPreview.getSurface() != null &&
|
||||
localPreview.getSurface().isValid()) {
|
||||
camera.setPreviewDisplay(localPreview);
|
||||
}
|
||||
} else {
|
||||
if(android.os.Build.VERSION.SDK_INT>10) {
|
||||
// No local renderer (we only care about onPreviewFrame() buffers, not a
|
||||
// directly-displayed UI element). Camera won't capture without
|
||||
// setPreview{Texture,Display}, so we create a SurfaceTexture and hand
|
||||
// it over to Camera, but never listen for frame-ready callbacks,
|
||||
// and never call updateTexImage on it.
|
||||
try {
|
||||
cameraGlTextures = new int[1];
|
||||
// No local renderer (we only care about onPreviewFrame() buffers, not a
|
||||
// directly-displayed UI element). Camera won't capture without
|
||||
// setPreview{Texture,Display}, so we create a SurfaceTexture and hand
|
||||
// it over to Camera, but never listen for frame-ready callbacks,
|
||||
// and never call updateTexImage on it.
|
||||
try {
|
||||
cameraGlTextures = new int[1];
|
||||
|
||||
// Generate one texture pointer and bind it as an external texture.
|
||||
GLES20.glGenTextures(1, cameraGlTextures, 0);
|
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
cameraGlTextures[0]);
|
||||
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
// Generate one texture pointer and bind it as an external texture.
|
||||
GLES20.glGenTextures(1, cameraGlTextures, 0);
|
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
cameraGlTextures[0]);
|
||||
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
|
||||
cameraSurfaceTexture = new SurfaceTexture(cameraGlTextures[0]);
|
||||
cameraSurfaceTexture.setOnFrameAvailableListener(null);
|
||||
camera.setPreviewTexture(cameraSurfaceTexture);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("No preview surface for Camera.");
|
||||
}
|
||||
cameraSurfaceTexture = new SurfaceTexture(cameraGlTextures[0]);
|
||||
cameraSurfaceTexture.setOnFrameAvailableListener(null);
|
||||
camera.setPreviewTexture(cameraSurfaceTexture);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Camera orientation: " + info.orientation +
|
||||
|
@ -300,11 +264,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateL
|
|||
min_mfps *= frameDropRatio;
|
||||
max_mfps *= frameDropRatio;
|
||||
Log.d(TAG, "Camera preview mfps range: " + min_mfps + " - " + max_mfps);
|
||||
if (android.os.Build.VERSION.SDK_INT>8) {
|
||||
parameters.setPreviewFpsRange(min_mfps, max_mfps);
|
||||
} else {
|
||||
parameters.setPreviewFrameRate(max_mfps / 1000);
|
||||
}
|
||||
parameters.setPreviewFpsRange(min_mfps, max_mfps);
|
||||
|
||||
int format = ImageFormat.NV21;
|
||||
parameters.setPreviewFormat(format);
|
||||
|
@ -336,8 +296,6 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateL
|
|||
}
|
||||
exchange(result, true);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
error = e;
|
||||
} catch (RuntimeException e) {
|
||||
error = e;
|
||||
}
|
||||
|
@ -401,18 +359,11 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback, AppStateL
|
|||
try {
|
||||
camera.setPreviewCallbackWithBuffer(null);
|
||||
camera.stopPreview();
|
||||
if (localPreview != null) {
|
||||
localPreview.removeCallback(this);
|
||||
camera.setPreviewDisplay(null);
|
||||
} else {
|
||||
if(android.os.Build.VERSION.SDK_INT>10) {
|
||||
camera.setPreviewTexture(null);
|
||||
cameraSurfaceTexture = null;
|
||||
if (cameraGlTextures != null) {
|
||||
GLES20.glDeleteTextures(1, cameraGlTextures, 0);
|
||||
cameraGlTextures = null;
|
||||
}
|
||||
}
|
||||
camera.setPreviewTexture(null);
|
||||
cameraSurfaceTexture = null;
|
||||
if (cameraGlTextures != null) {
|
||||
GLES20.glDeleteTextures(1, cameraGlTextures, 0);
|
||||
cameraGlTextures = null;
|
||||
}
|
||||
camera.release();
|
||||
camera = null;
|
||||
|
|
|
@ -24,25 +24,6 @@ import org.mozilla.gecko.util.ThreadUtils;
|
|||
public class ViERenderer {
|
||||
private final static String TAG = "WEBRTC-ViEREnderer";
|
||||
|
||||
// View used for local rendering that Cameras can use for Video Overlay.
|
||||
private static SurfaceHolder g_localRenderer;
|
||||
|
||||
public static SurfaceView CreateRenderer(Context context) {
|
||||
return CreateRenderer(context, false);
|
||||
}
|
||||
|
||||
public static SurfaceView CreateRenderer(Context context,
|
||||
boolean useOpenGLES2) {
|
||||
if(useOpenGLES2 == true && ViEAndroidGLES20.IsSupported(context))
|
||||
return new ViEAndroidGLES20(context);
|
||||
else
|
||||
return new SurfaceView(context);
|
||||
}
|
||||
|
||||
// Creates a SurfaceView to be used by Android Camera
|
||||
// service to display a local preview.
|
||||
// This needs to be used on Android prior to version 2.1
|
||||
// in order to run the camera.
|
||||
// Call this function before ViECapture::StartCapture.
|
||||
// The created view needs to be added to a visible layout
|
||||
// after a camera has been allocated
|
||||
|
@ -53,19 +34,13 @@ public class ViERenderer {
|
|||
// LinearLayout.addview
|
||||
// ViECapture::StartCapture
|
||||
public static void CreateLocalRenderer() {
|
||||
View cameraView = GeckoAppShell.getGeckoInterface().getCameraView();
|
||||
if (cameraView != null && (cameraView instanceof SurfaceView)) {
|
||||
SurfaceView localRender = (SurfaceView)cameraView;
|
||||
g_localRenderer = localRender.getHolder();
|
||||
}
|
||||
|
||||
ThreadUtils.getUiHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
GeckoAppShell.getGeckoInterface().enableCameraView();
|
||||
GeckoAppShell.getGeckoInterface().enableOrientationListener();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "CreateLocalRenderer enableCameraView exception: "
|
||||
Log.e(TAG, "enableOrientationListener exception: "
|
||||
+ e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
@ -73,26 +48,17 @@ public class ViERenderer {
|
|||
}
|
||||
|
||||
public static void DestroyLocalRenderer() {
|
||||
if (g_localRenderer != null) {
|
||||
g_localRenderer = null;
|
||||
|
||||
ThreadUtils.getUiHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
GeckoAppShell.getGeckoInterface().disableCameraView();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG,
|
||||
"DestroyLocalRenderer disableCameraView exception: " +
|
||||
e.getLocalizedMessage());
|
||||
}
|
||||
ThreadUtils.getUiHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
GeckoAppShell.getGeckoInterface().disableOrientationListener();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG,
|
||||
"disableOrientationListener exception: " +
|
||||
e.getLocalizedMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static SurfaceHolder GetLocalRenderer() {
|
||||
return g_localRenderer;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -171,7 +171,6 @@ public abstract class GeckoApp
|
|||
protected RelativeLayout mMainLayout;
|
||||
|
||||
protected RelativeLayout mGeckoLayout;
|
||||
private View mCameraView;
|
||||
private OrientationEventListener mCameraOrientationEventListener;
|
||||
public List<GeckoAppShell.AppStateListener> mAppStateListeners = new LinkedList<GeckoAppShell.AppStateListener>();
|
||||
protected MenuPanel mMenuPanel;
|
||||
|
@ -333,11 +332,6 @@ public abstract class GeckoApp
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCameraView() {
|
||||
return mCameraView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAppStateListener(GeckoAppShell.AppStateListener listener) {
|
||||
mAppStateListeners.add(listener);
|
||||
|
@ -1847,7 +1841,7 @@ public abstract class GeckoApp
|
|||
}
|
||||
|
||||
@Override
|
||||
public void enableCameraView() {
|
||||
public void enableOrientationListener() {
|
||||
// Start listening for orientation events
|
||||
mCameraOrientationEventListener = new OrientationEventListener(this) {
|
||||
@Override
|
||||
|
@ -1860,27 +1854,14 @@ public abstract class GeckoApp
|
|||
}
|
||||
};
|
||||
mCameraOrientationEventListener.enable();
|
||||
|
||||
// Try to make it fully transparent.
|
||||
if (mCameraView instanceof SurfaceView) {
|
||||
mCameraView.setAlpha(0.0f);
|
||||
ViewGroup mCameraLayout = (ViewGroup) findViewById(R.id.camera_layout);
|
||||
// Some phones (eg. nexus S) need at least a 8x16 preview size
|
||||
mCameraLayout.addView(mCameraView,
|
||||
new AbsoluteLayout.LayoutParams(8, 16, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCameraView() {
|
||||
public void disableOrientationListener() {
|
||||
if (mCameraOrientationEventListener != null) {
|
||||
mCameraOrientationEventListener.disable();
|
||||
mCameraOrientationEventListener = null;
|
||||
}
|
||||
if (mCameraView != null) {
|
||||
ViewGroup mCameraLayout = (ViewGroup) findViewById(R.id.camera_layout);
|
||||
mCameraLayout.removeView(mCameraView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -89,15 +89,6 @@ public abstract class AbstractTransactionalProvider extends ContentProvider {
|
|||
*/
|
||||
final ThreadLocal<Boolean> isInBatchOperation = new ThreadLocal<Boolean>();
|
||||
|
||||
/**
|
||||
* Return true if OS version and database parallelism support indicates
|
||||
* that this provider should bundle writes into transactions.
|
||||
*/
|
||||
@SuppressWarnings("static-method")
|
||||
protected boolean shouldUseTransactions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isInBatch() {
|
||||
final Boolean isInBatch = isInBatchOperation.get();
|
||||
if (isInBatch == null) {
|
||||
|
@ -116,7 +107,7 @@ public abstract class AbstractTransactionalProvider extends ContentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
if (shouldUseTransactions() && !db.inTransaction()) {
|
||||
if (!db.inTransaction()) {
|
||||
trace("beginWrite: beginning transaction.");
|
||||
db.beginTransaction();
|
||||
}
|
||||
|
@ -132,7 +123,7 @@ public abstract class AbstractTransactionalProvider extends ContentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
if (shouldUseTransactions() && db.inTransaction()) {
|
||||
if (db.inTransaction()) {
|
||||
trace("Marking write transaction successful.");
|
||||
db.setTransactionSuccessful();
|
||||
}
|
||||
|
@ -150,7 +141,7 @@ public abstract class AbstractTransactionalProvider extends ContentProvider {
|
|||
return;
|
||||
}
|
||||
|
||||
if (shouldUseTransactions() && db.inTransaction()) {
|
||||
if (db.inTransaction()) {
|
||||
trace("endWrite: ending transaction.");
|
||||
db.endTransaction();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.nio.ByteBuffer;
|
|||
import java.util.LinkedList;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/* package */ final class Codec extends ICodec.Stub implements IBinder.DeathRecipient {
|
||||
private static final String LOGTAG = "GeckoRemoteCodec";
|
||||
|
@ -188,12 +189,13 @@ import java.util.Queue;
|
|||
mAvailableInputBuffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private volatile ICodecCallbacks mCallbacks;
|
||||
private AsyncCodec mCodec;
|
||||
private InputProcessor mInputProcessor;
|
||||
private volatile boolean mFlushing = false;
|
||||
private SamplePool mSamplePool;
|
||||
private Queue<Sample> mSentOutputs = new LinkedList<>();
|
||||
private Queue<Sample> mSentOutputs = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public synchronized void setCallbacks(ICodecCallbacks callbacks) throws RemoteException {
|
||||
mCallbacks = callbacks;
|
||||
|
@ -345,8 +347,9 @@ import java.util.Queue;
|
|||
public synchronized void releaseOutput(Sample sample) {
|
||||
try {
|
||||
mSamplePool.recycleOutput(mSentOutputs.remove());
|
||||
} catch (NoSuchElementException e) {
|
||||
Log.e(LOGTAG, "releaseOutput not found: " + sample + "sent: " + mSentOutputs);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "failed to release output:" + sample);
|
||||
e.printStackTrace();
|
||||
}
|
||||
sample.dispose();
|
||||
}
|
||||
|
|
|
@ -87,7 +87,15 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
|
|||
|
||||
Message msg = obtainMessage(MSG_INPUT_BUFFER_AVAILABLE);
|
||||
msg.arg1 = index;
|
||||
sendMessage(msg);
|
||||
processMessage(msg);
|
||||
}
|
||||
|
||||
private void processMessage(Message msg) {
|
||||
if (Looper.myLooper() == getLooper()) {
|
||||
handleMessage(msg);
|
||||
} else {
|
||||
sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyOutputBuffer(int index, MediaCodec.BufferInfo info) {
|
||||
|
@ -97,20 +105,19 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
|
|||
|
||||
Message msg = obtainMessage(MSG_OUTPUT_BUFFER_AVAILABLE, info);
|
||||
msg.arg1 = index;
|
||||
sendMessage(msg);
|
||||
processMessage(msg);
|
||||
}
|
||||
|
||||
public void notifyOutputFormat(MediaFormat format) {
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage(obtainMessage(MSG_OUTPUT_FORMAT_CHANGE, format));
|
||||
processMessage(obtainMessage(MSG_OUTPUT_FORMAT_CHANGE, format));
|
||||
}
|
||||
|
||||
public void notifyError(int result) {
|
||||
Log.e(LOGTAG, "codec error:" + result);
|
||||
sendMessage(obtainMessage(MSG_ERROR, result, 0));
|
||||
processMessage(obtainMessage(MSG_ERROR, result, 0));
|
||||
}
|
||||
|
||||
protected boolean handleMessageLocked(Message msg) {
|
||||
|
|
|
@ -42,13 +42,6 @@
|
|||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<FrameLayout android:id="@+id/camera_layout"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true">
|
||||
</FrameLayout>
|
||||
|
||||
<view class="org.mozilla.gecko.media.VideoPlayer" android:id="@+id/video_player"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
var ZoomHelper = {
|
||||
zoomOut: function() {
|
||||
Messaging.sendRequest({ type: "Browser:ZoomToPageWidth" });
|
||||
},
|
||||
|
||||
isRectZoomedIn: function(aRect, aViewport) {
|
||||
// This function checks to see if the area of the rect visible in the
|
||||
// viewport (i.e. the "overlapArea" variable below) is approximately
|
||||
// the max area of the rect we can show. It also checks that the rect
|
||||
// is actually on-screen by testing the left and right edges of the rect.
|
||||
// In effect, this tells us whether or not zooming in to this rect
|
||||
// will significantly change what the user is seeing.
|
||||
const minDifference = -20;
|
||||
const maxDifference = 20;
|
||||
const maxZoomAllowed = 4; // keep this in sync with mobile/android/base/ui/PanZoomController.MAX_ZOOM
|
||||
|
||||
let vRect = new Rect(aViewport.cssX, aViewport.cssY, aViewport.cssWidth, aViewport.cssHeight);
|
||||
let overlap = vRect.intersect(aRect);
|
||||
let overlapArea = overlap.width * overlap.height;
|
||||
let availHeight = Math.min(aRect.width * vRect.height / vRect.width, aRect.height);
|
||||
let showing = overlapArea / (aRect.width * availHeight);
|
||||
let dw = (aRect.width - vRect.width);
|
||||
let dx = (aRect.x - vRect.x);
|
||||
|
||||
if (fuzzyEquals(aViewport.zoom, maxZoomAllowed) && overlap.width / aRect.width > 0.9) {
|
||||
// we're already at the max zoom and the block is not spilling off the side of the screen so that even
|
||||
// if the block isn't taking up most of the viewport we can't pan/zoom in any more. return true so that we zoom out
|
||||
return true;
|
||||
}
|
||||
|
||||
return (showing > 0.9 &&
|
||||
dx > minDifference && dx < maxDifference &&
|
||||
dw > minDifference && dw < maxDifference);
|
||||
},
|
||||
|
||||
/* Zoom to an element, optionally keeping a particular part of it
|
||||
* in view if it is really tall.
|
||||
*/
|
||||
zoomToElement: function(aElement, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true) {
|
||||
let rect = ElementTouchHelper.getBoundingContentRect(aElement);
|
||||
|
||||
const margin = 15;
|
||||
|
||||
|
||||
let viewport = BrowserApp.selectedTab.getViewport();
|
||||
rect = new Rect(aCanScrollHorizontally ? Math.max(viewport.cssPageLeft, rect.x - margin) : viewport.cssX,
|
||||
rect.y,
|
||||
aCanScrollHorizontally ? rect.w + 2 * margin : viewport.cssWidth,
|
||||
rect.h);
|
||||
// constrict the rect to the screen's right edge
|
||||
rect.width = Math.min(rect.width, viewport.cssPageRight - rect.x);
|
||||
|
||||
// if the rect is already taking up most of the visible area and is stretching the
|
||||
// width of the page, then we want to zoom out instead.
|
||||
if (aElement) {
|
||||
if (ZoomHelper.isRectZoomedIn(rect, viewport)) {
|
||||
if (aCanZoomOut) {
|
||||
ZoomHelper.zoomOut();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ZoomHelper.zoomToRect(rect, aClickY);
|
||||
}
|
||||
},
|
||||
|
||||
/* Zoom to a specific part of the screen defined by a rect,
|
||||
* optionally keeping a particular part of it in view
|
||||
* if it is really tall.
|
||||
*/
|
||||
zoomToRect: function(aRect, aClickY = -1) {
|
||||
let viewport = BrowserApp.selectedTab.getViewport();
|
||||
|
||||
let rect = {
|
||||
x: aRect.x,
|
||||
y: aRect.y,
|
||||
w: aRect.width,
|
||||
h: Math.min(aRect.width * viewport.cssHeight / viewport.cssWidth, aRect.height)
|
||||
};
|
||||
|
||||
rect.type = "Browser:ZoomToRect";
|
||||
|
||||
if (aClickY >= 0) {
|
||||
// if the block we're zooming to is really tall, and we want to keep a particular
|
||||
// part of it in view, then adjust the y-coordinate of the target rect accordingly.
|
||||
// the 1.2 multiplier is just a little fuzz to compensate for aRect including horizontal
|
||||
// margins but not vertical ones.
|
||||
let cssTapY = viewport.cssY + aClickY;
|
||||
if ((aRect.height > rect.h) && (cssTapY > rect.y + (rect.h * 1.2))) {
|
||||
rect.y = cssTapY - (rect.h / 2);
|
||||
}
|
||||
}
|
||||
|
||||
Messaging.sendRequest(rect);
|
||||
},
|
||||
};
|
|
@ -129,7 +129,6 @@ var lazilyLoadedBrowserScripts = [
|
|||
["PluginHelper", "chrome://browser/content/PluginHelper.js"],
|
||||
["OfflineApps", "chrome://browser/content/OfflineApps.js"],
|
||||
["Linkifier", "chrome://browser/content/Linkify.js"],
|
||||
["ZoomHelper", "chrome://browser/content/ZoomHelper.js"],
|
||||
["CastingApps", "chrome://browser/content/CastingApps.js"],
|
||||
["RemoteDebugger", "chrome://browser/content/RemoteDebugger.js"],
|
||||
];
|
||||
|
@ -4481,32 +4480,6 @@ var BrowserEventHandler = {
|
|||
}
|
||||
},
|
||||
|
||||
_handleTouchStart: function(aEvent) {
|
||||
if (!BrowserApp.isBrowserContentDocumentDisplayed() || aEvent.touches.length > 1 || aEvent.defaultPrevented)
|
||||
return;
|
||||
|
||||
let target = aEvent.target;
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've pressed a scrollable element, let Java know that we may
|
||||
// want to override the scroll behaviour (for document sub-frames)
|
||||
this._scrollableElement = this._findScrollableElement(target, true);
|
||||
this._firstScrollEvent = true;
|
||||
|
||||
if (this._scrollableElement != null) {
|
||||
// Discard if it's the top-level scrollable, we let Java handle this
|
||||
// The top-level scrollable is the body in quirks mode and the html element
|
||||
// in standards mode
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
let rootScrollable = (doc.compatMode === "BackCompat" ? doc.body : doc.documentElement);
|
||||
if (this._scrollableElement != rootScrollable) {
|
||||
Messaging.sendRequest({ type: "Panning:Override" });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleRetargetedTouchStart: function(aEvent) {
|
||||
// we should only get this called just after a new touchstart with a single
|
||||
// touch point.
|
||||
|
@ -4610,45 +4583,6 @@ var BrowserEventHandler = {
|
|||
});
|
||||
},
|
||||
|
||||
onDoubleTap: function(aData) {
|
||||
let metadata = ViewportHandler.getMetadataForDocument(BrowserApp.selectedBrowser.contentDocument);
|
||||
if (metadata && !metadata.allowDoubleTapZoom) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = JSON.parse(aData);
|
||||
let element = ElementTouchHelper.anyElementFromPoint(data.x, data.y);
|
||||
|
||||
if (!element) {
|
||||
ZoomHelper.zoomOut();
|
||||
return;
|
||||
}
|
||||
|
||||
while (element && !this._shouldZoomToElement(element))
|
||||
element = element.parentNode;
|
||||
|
||||
if (!element) {
|
||||
ZoomHelper.zoomOut();
|
||||
} else {
|
||||
ZoomHelper.zoomToElement(element, data.y);
|
||||
}
|
||||
},
|
||||
|
||||
_shouldZoomToElement: function(aElement) {
|
||||
let win = aElement.ownerDocument.defaultView;
|
||||
if (win.getComputedStyle(aElement, null).display == "inline")
|
||||
return false;
|
||||
if (aElement instanceof Ci.nsIDOMHTMLLIElement)
|
||||
return false;
|
||||
if (aElement instanceof Ci.nsIDOMHTMLQuoteElement)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
|
||||
_firstScrollEvent: false,
|
||||
|
||||
_scrollableElement: null,
|
||||
|
||||
_highlightElement: null,
|
||||
|
||||
_doTapHighlight: function _doTapHighlight(aElement) {
|
||||
|
@ -4660,137 +4594,10 @@ var BrowserEventHandler = {
|
|||
return;
|
||||
|
||||
this._highlightElement = null;
|
||||
},
|
||||
|
||||
_updateLastPosition: function(x, y, dx, dy) {
|
||||
this.lastX = x;
|
||||
this.lastY = y;
|
||||
this.lastTime = Date.now();
|
||||
|
||||
this.motionBuffer.push({ dx: dx, dy: dy, time: this.lastTime });
|
||||
},
|
||||
|
||||
_sendMouseEvent: function _sendMouseEvent(aName, aX, aY) {
|
||||
let win = BrowserApp.selectedBrowser.contentWindow;
|
||||
try {
|
||||
let cwu = win.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
cwu.sendMouseEventToWindow(aName, aX, aY, 0, 1, 0, true, 0, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH, false);
|
||||
} catch(e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
},
|
||||
|
||||
_hasScrollableOverflow: function(elem) {
|
||||
var win = elem.ownerDocument.defaultView;
|
||||
if (!win)
|
||||
return false;
|
||||
var computedStyle = win.getComputedStyle(elem);
|
||||
if (!computedStyle)
|
||||
return false;
|
||||
// We check for overflow:hidden only because all the other cases are scrollable
|
||||
// under various conditions. See https://bugzilla.mozilla.org/show_bug.cgi?id=911574#c24
|
||||
// for some more details.
|
||||
return !(computedStyle.overflowX == 'hidden' && computedStyle.overflowY == 'hidden');
|
||||
},
|
||||
|
||||
_findScrollableElement: function(elem, checkElem) {
|
||||
// Walk the DOM tree until we find a scrollable element
|
||||
let scrollable = false;
|
||||
while (elem) {
|
||||
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
||||
* - It has overflow other than 'hidden', or
|
||||
* - It's a textarea node, or
|
||||
* - It's a text input, or
|
||||
* - It's a select element showing multiple rows
|
||||
*/
|
||||
if (checkElem) {
|
||||
if ((elem.scrollTopMin != elem.scrollTopMax ||
|
||||
elem.scrollLeftMin != elem.scrollLeftMax) &&
|
||||
(this._hasScrollableOverflow(elem) ||
|
||||
elem.matches("textarea")) ||
|
||||
(elem instanceof HTMLInputElement && elem.mozIsTextField(false)) ||
|
||||
(elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
|
||||
scrollable = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
checkElem = true;
|
||||
}
|
||||
|
||||
// Propagate up iFrames
|
||||
if (!elem.parentNode && elem.documentElement && elem.documentElement.ownerDocument)
|
||||
elem = elem.documentElement.ownerDocument.defaultView.frameElement;
|
||||
else
|
||||
elem = elem.parentNode;
|
||||
}
|
||||
|
||||
if (!scrollable)
|
||||
return null;
|
||||
|
||||
return elem;
|
||||
},
|
||||
|
||||
_scrollElementBy: function(elem, x, y) {
|
||||
elem.scrollTop = elem.scrollTop + y;
|
||||
elem.scrollLeft = elem.scrollLeft + x;
|
||||
},
|
||||
|
||||
_elementCanScroll: function(elem, x, y) {
|
||||
let scrollX = (x < 0 && elem.scrollLeft > 0)
|
||||
|| (x > 0 && elem.scrollLeft < elem.scrollLeftMax);
|
||||
|
||||
let scrollY = (y < 0 && elem.scrollTop > 0)
|
||||
|| (y > 0 && elem.scrollTop < elem.scrollTopMax);
|
||||
|
||||
return scrollX || scrollY;
|
||||
}
|
||||
};
|
||||
|
||||
const ElementTouchHelper = {
|
||||
/* Return the element at the given coordinates, starting from the given window and
|
||||
drilling down through frames. If no window is provided, the top-level window of
|
||||
the currently selected tab is used. The coordinates provided should be CSS pixels
|
||||
relative to the window's scroll position. */
|
||||
anyElementFromPoint: function(aX, aY, aWindow) {
|
||||
let win = (aWindow ? aWindow : BrowserApp.selectedBrowser.contentWindow);
|
||||
let cwu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
let elem = cwu.elementFromPoint(aX, aY, true, true);
|
||||
|
||||
while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) {
|
||||
let rect = elem.getBoundingClientRect();
|
||||
aX -= rect.left;
|
||||
aY -= rect.top;
|
||||
cwu = elem.contentDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
elem = cwu.elementFromPoint(aX, aY, true, true);
|
||||
}
|
||||
|
||||
return elem;
|
||||
},
|
||||
|
||||
/* Returns the touch radius with zoom factored in. */
|
||||
getTouchRadius: function getTouchRadius() {
|
||||
let zoom = BrowserApp.selectedTab._zoom;
|
||||
return {
|
||||
top: this.radius.top / zoom,
|
||||
right: this.radius.right / zoom,
|
||||
bottom: this.radius.bottom / zoom,
|
||||
left: this.radius.left / zoom
|
||||
};
|
||||
},
|
||||
|
||||
/* Returns the touch radius in device pixels. */
|
||||
get radius() {
|
||||
let mmToIn = 1 / 25.4;
|
||||
let mmToPx = mmToIn * ViewportHandler.displayDPI;
|
||||
let prefs = Services.prefs;
|
||||
delete this.radius;
|
||||
return this.radius = { "top": prefs.getIntPref("ui.touch.radius.topmm") * mmToPx,
|
||||
"right": prefs.getIntPref("ui.touch.radius.rightmm") * mmToPx,
|
||||
"bottom": prefs.getIntPref("ui.touch.radius.bottommm") * mmToPx,
|
||||
"left": prefs.getIntPref("ui.touch.radius.leftmm") * mmToPx
|
||||
};
|
||||
},
|
||||
|
||||
getBoundingContentRect: function(aElement) {
|
||||
if (!aElement)
|
||||
return {x: 0, y: 0, w: 0, h: 0};
|
||||
|
@ -5545,11 +5352,6 @@ var XPInstallObserver = {
|
|||
};
|
||||
|
||||
var ViewportHandler = {
|
||||
// The cached viewport metadata for each document. We tie viewport metadata to each document
|
||||
// instead of to each tab so that we don't have to update it when the document changes. Using an
|
||||
// ES6 weak map lets us avoid leaks.
|
||||
_metadata: new WeakMap(),
|
||||
|
||||
init: function init() {
|
||||
Services.obs.addObserver(this, "Window:Resize", false);
|
||||
},
|
||||
|
@ -5560,51 +5362,6 @@ var ViewportHandler = {
|
|||
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.setNextPaintSyncId(scrollChange.id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a viewport tag was specified
|
||||
*/
|
||||
isViewportSpecified: function isViewportSpecified(aWindow) {
|
||||
let tab = BrowserApp.getTabForWindow(aWindow);
|
||||
let readerMode = false;
|
||||
try {
|
||||
readerMode = tab.browser.contentDocument.documentURI.startsWith("about:reader");
|
||||
} catch (e) {
|
||||
}
|
||||
if (tab.desktopMode && !readerMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
return !isNaN(parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")))
|
||||
|| !isNaN(parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale")))
|
||||
|| !isNaN(parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale")))
|
||||
|| ("" != windowUtils.getDocumentMetadata("viewport-user-scalable"))
|
||||
|| ("" != windowUtils.getDocumentMetadata("viewport-width"))
|
||||
|| ("" != windowUtils.getDocumentMetadata("viewport-height"));
|
||||
},
|
||||
|
||||
get displayDPI() {
|
||||
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
delete this.displayDPI;
|
||||
return this.displayDPI = utils.displayDPI;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the viewport metadata for the given document, or undefined if there
|
||||
* isn't one.
|
||||
*/
|
||||
getMetadataForDocument: function getMetadataForDocument(aDocument) {
|
||||
return this._metadata.get(aDocument);
|
||||
},
|
||||
|
||||
/** Updates the saved viewport metadata for the given content document. */
|
||||
setMetadataForDocument: function setMetadataForDocument(aDocument, aMetadata) {
|
||||
if (!aMetadata)
|
||||
this._metadata.delete(aDocument);
|
||||
else
|
||||
this._metadata.set(aDocument, aMetadata);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ chrome.jar:
|
|||
content/FeedHandler.js (content/FeedHandler.js)
|
||||
content/Feedback.js (content/Feedback.js)
|
||||
content/Linkify.js (content/Linkify.js)
|
||||
content/ZoomHelper.js (content/ZoomHelper.js)
|
||||
content/CastingApps.js (content/CastingApps.js)
|
||||
content/RemoteDebugger.js (content/RemoteDebugger.js)
|
||||
#ifdef MOZ_SERVICES_HEALTHREPORT
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче