merge mozilla-inbound to mozilla-central. r=merge a=merge

MozReview-Commit-ID: F5aZ4FC75r7
This commit is contained in:
Sebastian Hengst 2017-04-07 22:03:41 +02:00
commit 0e1faab582
254 changed files with 5877 additions and 3945 deletions

View File

@ -797,6 +797,7 @@ Pete Collins <petejc@collab.net>
Peter Annema <disttsc@bart.nl>
Peter Bajusz <hyp-x@inf.bme.hu>
Peter Lubczynski <peterl@netscape.com>
Peter Moore <petemoore@gmx.net>
Peter Naulls
Peter Parente <parente@cs.unc.edu>
Peter Seliger

View File

@ -4,7 +4,7 @@
# 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/.
if CONFIG['COMPILE_ENVIRONMENT']:
if CONFIG['COMPILE_ENVIRONMENT'] and CONFIG['ACCESSIBILITY']:
DIRS += [
'handler',
'typelib',

View File

@ -24,12 +24,8 @@ const MAX_EXPIRY = Math.pow(2, 62);
* The external API implemented by the SessionCookies module.
*/
this.SessionCookies = Object.freeze({
update(windows) {
SessionCookiesInternal.update(windows);
},
getHostsForWindow(window) {
return SessionCookiesInternal.getHostsForWindow(window);
collect() {
return SessionCookiesInternal.collect();
},
restore(cookies) {
@ -47,69 +43,17 @@ var SessionCookiesInternal = {
_initialized: false,
/**
* Retrieve the list of all hosts contained in the given windows' session
* history entries (per window) and collect the associated cookies for those
* hosts, if any. The given state object is being modified.
*
* @param windows
* Array of window state objects.
* [{ tabs: [...], cookies: [...] }, ...]
* Retrieve an array of all stored session cookies.
*/
update(windows) {
collect() {
this._ensureInitialized();
// Check whether we're allowed to store cookies.
let storeAnyCookies = PrivacyLevel.canSave(false);
let storeSecureCookies = PrivacyLevel.canSave(true);
for (let window of windows) {
let cookies = [];
if (storeAnyCookies) {
// Collect all cookies for the current window.
for (let host of this.getHostsForWindow(window)) {
for (let cookie of CookieStore.getCookiesForHost(host)) {
if (!cookie.secure || storeSecureCookies) {
cookies.push(cookie);
}
}
}
}
// Don't include/keep empty cookie sections.
if (cookies.length) {
window.cookies = cookies;
} else if ("cookies" in window) {
delete window.cookies;
}
}
},
/**
* Returns a map of all hosts for a given window that we might want to
* collect cookies for.
*
* @param window
* A window state object containing tabs with history entries.
* @return {set} A set of hosts for a given window state object.
*/
getHostsForWindow(window) {
let hosts = new Set();
for (let tab of window.tabs) {
for (let entry of tab.entries) {
this._extractHostsFromEntry(entry, hosts);
}
}
return hosts;
return CookieStore.toArray();
},
/**
* Restores a given list of session cookies.
*/
restore(cookies) {
for (let cookie of cookies) {
let expiry = "expiry" in cookie ? cookie.expiry : MAX_EXPIRY;
let cookieObj = {
@ -117,10 +61,12 @@ var SessionCookiesInternal = {
path: cookie.path || "",
name: cookie.name || ""
};
if (!Services.cookies.cookieExists(cookieObj, cookie.originAttributes || {})) {
let originAttributes = cookie.originAttributes || {};
if (!Services.cookies.cookieExists(cookieObj, originAttributes)) {
Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
cookie.value, !!cookie.secure, !!cookie.httponly,
/* isSession = */ true, expiry, cookie.originAttributes || {});
/* isSession = */ true, expiry, originAttributes);
}
}
},
@ -146,10 +92,6 @@ var SessionCookiesInternal = {
case "batch-deleted":
this._removeCookies(subject);
break;
case "reload":
CookieStore.clear();
this._reloadCookies();
break;
default:
throw new Error("Unhandled cookie-changed notification.");
}
@ -164,31 +106,11 @@ var SessionCookiesInternal = {
this._reloadCookies();
this._initialized = true;
Services.obs.addObserver(this, "cookie-changed", false);
}
},
/**
* Fill a given map with hosts found in the given entry's session history and
* any child entries.
*
* @param entry
* the history entry, serialized
* @param hosts
* the set that will be used to store hosts
*/
_extractHostsFromEntry(entry, hosts) {
try {
// It's alright if this throws for about: URIs.
let {host, scheme} = Utils.makeURI(entry.url);
if (/^(file|https?)$/.test(scheme)) {
hosts.add(host);
}
} catch (ex) { }
if (entry.children) {
for (let child of entry.children) {
this._extractHostsFromEntry(child, hosts);
}
// Listen for privacy level changes to reload cookies when needed.
Services.prefs.addObserver("browser.sessionstore.privacy_level", () => {
this._reloadCookies();
}, false);
}
},
@ -198,8 +120,9 @@ var SessionCookiesInternal = {
_addCookie(cookie) {
cookie.QueryInterface(Ci.nsICookie2);
if (cookie.isSession) {
CookieStore.set(cookie);
// Store only session cookies, obey the privacy level.
if (cookie.isSession && PrivacyLevel.canSave(cookie.isSecure)) {
CookieStore.add(cookie);
}
},
@ -209,8 +132,9 @@ var SessionCookiesInternal = {
_updateCookie(cookie) {
cookie.QueryInterface(Ci.nsICookie2);
if (cookie.isSession) {
CookieStore.set(cookie);
// Store only session cookies, obey the privacy level.
if (cookie.isSession && PrivacyLevel.canSave(cookie.isSecure)) {
CookieStore.add(cookie);
} else {
CookieStore.delete(cookie);
}
@ -238,9 +162,16 @@ var SessionCookiesInternal = {
/**
* Iterates all cookies in the cookies service and puts them into the store
* if they're session cookies.
* if they're session cookies. Obeys the user's chosen privacy level.
*/
_reloadCookies() {
CookieStore.clear();
// Bail out if we're not supposed to store cookies at all.
if (!PrivacyLevel.canSave(false)) {
return;
}
let iter = Services.cookies.enumerator;
while (iter.hasMoreElements()) {
this._addCookie(iter.getNext());
@ -249,108 +180,13 @@ var SessionCookiesInternal = {
};
/**
* Generates all possible subdomains for a given host and prepends a leading
* dot to all variants.
*
* See http://tools.ietf.org/html/rfc6265#section-5.1.3
* http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path
*
* All cookies belonging to a web page will be internally represented by a
* nsICookie object. nsICookie.host will be the request host if no domain
* parameter was given when setting the cookie. If a specific domain was given
* then nsICookie.host will contain that specific domain and prepend a leading
* dot to it.
*
* We thus generate all possible subdomains for a given domain and prepend a
* leading dot to them as that is the value that was used as the map key when
* the cookie was set.
*/
function* getPossibleSubdomainVariants(host) {
// Try given domain with a leading dot (.www.example.com).
yield "." + host;
// Stop if there are only two parts left (e.g. example.com was given).
let parts = host.split(".");
if (parts.length < 3) {
return;
}
// Remove the first subdomain (www.example.com -> example.com).
let rest = parts.slice(1).join(".");
// Try possible parent subdomains.
yield* getPossibleSubdomainVariants(rest);
}
/**
* The internal cookie storage that keeps track of every active session cookie.
* These are stored using maps per host, path, and cookie name.
* The internal storage that keeps track of session cookies.
*/
var CookieStore = {
/**
* The internal structure holding all known cookies.
*
* Host =>
* Path =>
* Name => {path: "/", name: "sessionid", secure: true}
*
* Maps are used for storage but the data structure is equivalent to this:
*
* this._hosts = {
* "www.mozilla.org": {
* "/": {
* "username": {name: "username", value: "my_name_is", etc...},
* "sessionid": {name: "sessionid", value: "1fdb3a", etc...}
* }
* },
* "tbpl.mozilla.org": {
* "/path": {
* "cookiename": {name: "cookiename", value: "value", etc...}
* }
* },
* ".example.com": {
* "/path": {
* "cookiename": {name: "cookiename", value: "value", etc...}
* }
* }
* };
* The internal map holding all known session cookies.
*/
_hosts: new Map(),
/**
* Returns the list of stored session cookies for a given host.
*
* @param mainHost
* A string containing the host name we want to get cookies for.
*/
getCookiesForHost(mainHost) {
let cookies = [];
let appendCookiesForHost = host => {
if (!this._hosts.has(host)) {
return;
}
for (let pathToNamesMap of this._hosts.get(host).values()) {
for (let nameToCookiesMap of pathToNamesMap.values()) {
cookies.push(...nameToCookiesMap.values());
}
}
}
// Try to find cookies for the given host, e.g. <www.example.com>.
// The full hostname will be in the map if the Set-Cookie header did not
// have a domain= attribute, i.e. the cookie will only be stored for the
// request domain. Also, try to find cookies for subdomains, e.g.
// <.example.com>. We will find those variants with a leading dot in the
// map if the Set-Cookie header had a domain= attribute, i.e. the cookie
// will be stored for a parent domain and we send it for any subdomain.
for (let variant of [mainHost, ...getPossibleSubdomainVariants(mainHost)]) {
appendCookiesForHost(variant);
}
return cookies;
},
_entries: new Map(),
/**
* Stores a given cookie.
@ -358,7 +194,7 @@ var CookieStore = {
* @param cookie
* The nsICookie2 object to add to the storage.
*/
set(cookie) {
add(cookie) {
let jscookie = {host: cookie.host, value: cookie.value};
// Only add properties with non-default values to save a few bytes.
@ -386,7 +222,7 @@ var CookieStore = {
jscookie.originAttributes = cookie.originAttributes;
}
this._ensureMap(cookie).set(cookie.name, jscookie);
this._entries.set(this._getKeyForCookie(cookie), jscookie);
},
/**
@ -396,60 +232,38 @@ var CookieStore = {
* The nsICookie2 object to be removed from storage.
*/
delete(cookie) {
// Deletion shouldn't create new maps.
// Bail out whenever we find that an entry or a map doesn't exist.
let map = this._hosts.get(cookie.host);
if (!map) {
return;
}
map = map.get(ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
if (!map) {
return;
}
map = map.get(cookie.path);
if (!map) {
return;
}
map.delete(cookie.name);
this._entries.delete(this._getKeyForCookie(cookie));
},
/**
* Removes all cookies.
*/
clear() {
this._hosts.clear();
this._entries.clear();
},
/**
* Creates all maps necessary to store a given cookie.
* Return all cookies as an array.
*/
toArray() {
return [...this._entries.values()];
},
/**
* Returns the key needed to properly store and identify a given cookie.
* A cookie is uniquely identified by the combination of its host, name,
* path, and originAttributes properties.
*
* @param cookie
* The nsICookie2 object to create maps for.
*
* @return The newly created Map instance mapping cookie names to
* internal jscookies, in the given path of the given host.
* The nsICookie2 object to compute a key for.
* @return string
*/
_ensureMap(cookie) {
if (!this._hosts.has(cookie.host)) {
this._hosts.set(cookie.host, new Map());
}
let originAttributesMap = this._hosts.get(cookie.host);
// If cookie.originAttributes is null, originAttributes will be an empty string.
let originAttributes = ChromeUtils.originAttributesToSuffix(cookie.originAttributes);
if (!originAttributesMap.has(originAttributes)) {
originAttributesMap.set(originAttributes, new Map());
}
let pathToNamesMap = originAttributesMap.get(originAttributes);
if (!pathToNamesMap.has(cookie.path)) {
pathToNamesMap.set(cookie.path, new Map());
}
return pathToNamesMap.get(cookie.path);
_getKeyForCookie(cookie) {
return JSON.stringify({
host: cookie.host,
name: cookie.name,
path: cookie.path,
attr: ChromeUtils.originAttributesToSuffix(cookie.originAttributes)
});
}
};

View File

@ -634,10 +634,15 @@ var SessionStoreInternal = {
let [iniState, remainingState] = this._prepDataForDeferredRestore(state);
// If we have a iniState with windows, that means that we have windows
// with app tabs to restore.
if (iniState.windows.length)
if (iniState.windows.length) {
// Move cookies over from the remaining state so that they're
// restored right away, and pinned tabs will load correctly.
iniState.cookies = remainingState.cookies;
delete remainingState.cookies;
state = iniState;
else
} else {
state = null;
}
if (remainingState.windows.length) {
LastSession.setState(remainingState);
@ -1153,6 +1158,9 @@ var SessionStoreInternal = {
// it happens before observers are notified
this._globalState.setFromState(aInitialState);
// Restore session cookies before loading any tabs.
SessionCookies.restore(aInitialState.cookies || []);
let overwrite = this._isCmdLineEmpty(aWindow, aInitialState);
let options = {firstWindow: true, overwriteTabs: overwrite};
this.restoreWindows(aWindow, aInitialState, options);
@ -1381,7 +1389,6 @@ var SessionStoreInternal = {
winData.title = tabbrowser.selectedBrowser.contentTitle || tabbrowser.selectedTab.label;
winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
tabbrowser.selectedTab);
SessionCookies.update([winData]);
}
if (AppConstants.platform != "macosx") {
@ -2221,6 +2228,9 @@ var SessionStoreInternal = {
// it happens before observers are notified
this._globalState.setFromState(state);
// Restore session cookies.
SessionCookies.restore(state.cookies || []);
// restore to the given state
this.restoreWindows(window, state, {overwriteTabs: true});
@ -2686,6 +2696,9 @@ var SessionStoreInternal = {
// it happens before observers are notified
this._globalState.setFromState(lastSessionState);
// Restore session cookies.
SessionCookies.restore(lastSessionState.cookies || []);
// Restore into windows or open new ones as needed.
for (let i = 0; i < lastSessionState.windows.length; i++) {
let winState = lastSessionState.windows[i];
@ -3046,10 +3059,6 @@ var SessionStoreInternal = {
nonPopupCount++;
}
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
SessionCookies.update(total);
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
// collect the data for all windows yet to be restored
for (ix in this._statesToRestore) {
for (let winData of this._statesToRestore[ix].windows) {
@ -3102,6 +3111,11 @@ var SessionStoreInternal = {
global: this._globalState.getState()
};
// Collect and store session cookies.
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
state.cookies = SessionCookies.collect();
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_COOKIES_MS");
if (Cu.isModuleLoaded("resource://devtools/client/scratchpad/scratchpad-manager.jsm")) {
// get open Scratchpad window states too
let scratchpads = ScratchpadManager.getSessionState();
@ -3139,10 +3153,7 @@ var SessionStoreInternal = {
this._collectWindowData(aWindow);
}
let windows = [this._windows[aWindow.__SSi]];
SessionCookies.update(windows);
return { windows };
return { windows: [this._windows[aWindow.__SSi]] };
},
/**
@ -3386,9 +3397,10 @@ var SessionStoreInternal = {
this.restoreWindowFeatures(aWindow, winData);
delete this._windows[aWindow.__SSi].extData;
}
if (winData.cookies) {
SessionCookies.restore(winData.cookies);
}
// Restore cookies from legacy sessions, i.e. before bug 912717.
SessionCookies.restore(winData.cookies || []);
if (winData.extData) {
if (!this._windows[aWindow.__SSi].extData) {
this._windows[aWindow.__SSi].extData = {};
@ -4312,8 +4324,7 @@ var SessionStoreInternal = {
* (defaultState) will be a state that should still be restored at startup,
* while the second part (state) is a state that should be saved for later.
* defaultState will be comprised of windows with only pinned tabs, extracted
* from state. It will contain the cookies that go along with the history
* entries in those tabs. It will also contain window position information.
* from state. It will also contain window position information.
*
* defaultState will be restored at startup. state will be passed into
* LastSession and will be kept in case the user explicitly wants
@ -4338,7 +4349,7 @@ var SessionStoreInternal = {
let window = state.windows[wIndex];
window.selected = window.selected || 1;
// We're going to put the state of the window into this object
let pinnedWindowState = { tabs: [], cookies: []};
let pinnedWindowState = { tabs: [] };
for (let tIndex = 0; tIndex < window.tabs.length;) {
if (window.tabs[tIndex].pinned) {
// Adjust window.selected
@ -4379,9 +4390,6 @@ var SessionStoreInternal = {
window.__lastSessionWindowID = pinnedWindowState.__lastSessionWindowID
= "" + Date.now() + Math.random();
// Extract the cookies that belong with each pinned tab
this._splitCookiesFromWindow(window, pinnedWindowState);
// Actually add this window to our defaultState
defaultState.windows.push(pinnedWindowState);
// Remove the window from the state if it doesn't have any tabs
@ -4404,36 +4412,6 @@ var SessionStoreInternal = {
return [defaultState, state];
},
/**
* Splits out the cookies from aWinState into aTargetWinState based on the
* tabs that are in aTargetWinState.
* This alters the state of aWinState and aTargetWinState.
*/
_splitCookiesFromWindow:
function ssi_splitCookiesFromWindow(aWinState, aTargetWinState) {
if (!aWinState.cookies || !aWinState.cookies.length)
return;
// Get the hosts for history entries in aTargetWinState
let cookieHosts = SessionCookies.getHostsForWindow(aTargetWinState);
// By creating a regex we reduce overhead and there is only one loop pass
// through either array (cookieHosts and aWinState.cookies).
let hosts = [...cookieHosts].join("|").replace(/\./g, "\\.");
// If we don't actually have any hosts, then we don't want to do anything.
if (!hosts.length)
return;
let cookieRegex = new RegExp(".*(" + hosts + ")");
for (let cIndex = 0; cIndex < aWinState.cookies.length;) {
if (cookieRegex.test(aWinState.cookies[cIndex].host)) {
aTargetWinState.cookies =
aTargetWinState.cookies.concat(aWinState.cookies.splice(cIndex, 1));
continue;
}
cIndex++;
}
},
_sendRestoreCompletedNotifications: function ssi_sendRestoreCompletedNotifications() {
// not all windows restored, yet
if (this._restoreCount > 1) {

View File

@ -12,7 +12,6 @@ support-files =
head.js
content.js
content-forms.js
browser_cookies.sjs
browser_formdata_sample.html
browser_formdata_xpath_sample.html
browser_frametree_sample.html
@ -80,7 +79,6 @@ skip-if = debug # bug 1211084
[browser_broadcast.js]
[browser_capabilities.js]
[browser_cleaner.js]
[browser_cookies.js]
[browser_crashedTabs.js]
skip-if = !e10s || !crashreporter
[browser_unrestored_crashedTabs.js]
@ -251,3 +249,7 @@ skip-if = !e10s # GroupedSHistory is e10s-only
[browser_duplicate_history.js]
[browser_tabicon_after_bg_tab_crash.js]
skip-if = !e10s # Tabs can't crash without e10s
[browser_cookies.js]
[browser_cookies_legacy.js]
[browser_cookies_privacy.js]

View File

@ -15,15 +15,12 @@ add_task(function*() {
set: [["browser.sessionstore.interval", 0]]
});
let win = yield BrowserTestUtils.openNewBrowserWindow();
let browser = win.gBrowser.selectedBrowser;
browser.loadURI(testURL);
yield BrowserTestUtils.browserLoaded(browser);
yield TabStateFlusher.flush(browser);
let tab = gBrowser.addTab(testURL);
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
yield TabStateFlusher.flush(tab.linkedBrowser);
// get the sessionstore state for the window
let state = ss.getWindowState(win);
let state = ss.getBrowserState();
// verify our cookie got set during pageload
let enumerator = Services.cookies.enumerator;
@ -39,7 +36,7 @@ add_task(function*() {
Services.cookies.removeAll();
// restore the window state
ss.setWindowState(win, state, true);
ss.setBrowserState(state);
// at this point, the cookie should be restored...
enumerator = Services.cookies.enumerator;
@ -55,5 +52,5 @@ add_task(function*() {
// clean up
Services.cookies.removeAll();
yield BrowserTestUtils.closeWindow(win);
yield promiseRemoveTab(tab);
});

View File

@ -1,275 +1,73 @@
"use strict";
const PATH = "/browser/browser/components/sessionstore/test/";
/**
* Remove all cookies to start off a clean slate.
*/
add_task(function* test_setup() {
requestLongerTimeout(3);
Services.cookies.removeAll();
});
/**
* Test multiple scenarios with different Set-Cookie header domain= params.
*/
add_task(function* test_run() {
// Set-Cookie: foobar=random()
// The domain of the cookie should be the request domain (www.example.com).
// We should collect data only for the request domain, no parent or subdomains.
yield testCookieCollection({
host: "http://www.example.com",
cookieHost: "www.example.com",
cookieURIs: ["http://www.example.com" + PATH],
noCookieURIs: ["http://example.com/" + PATH]
});
// Set-Cookie: foobar=random()
// The domain of the cookie should be the request domain (example.com).
// We should collect data only for the request domain, no parent or subdomains.
yield testCookieCollection({
host: "http://example.com",
cookieHost: "example.com",
cookieURIs: ["http://example.com" + PATH],
noCookieURIs: ["http://www.example.com/" + PATH]
});
// Set-Cookie: foobar=random(); Domain=example.com
// The domain of the cookie should be the given one (.example.com).
// We should collect data for the given domain and its subdomains.
yield testCookieCollection({
host: "http://example.com",
domain: "example.com",
cookieHost: ".example.com",
cookieURIs: ["http://example.com" + PATH, "http://www.example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// Set-Cookie: foobar=random(); Domain=.example.com
// The domain of the cookie should be the given one (.example.com).
// We should collect data for the given domain and its subdomains.
yield testCookieCollection({
host: "http://example.com",
domain: ".example.com",
cookieHost: ".example.com",
cookieURIs: ["http://example.com" + PATH, "http://www.example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// Set-Cookie: foobar=random(); Domain=www.example.com
// The domain of the cookie should be the given one (.www.example.com).
// We should collect data for the given domain and its subdomains.
yield testCookieCollection({
host: "http://www.example.com",
domain: "www.example.com",
cookieHost: ".www.example.com",
cookieURIs: ["http://www.example.com/" + PATH],
noCookieURIs: ["http://example.com"]
});
// Set-Cookie: foobar=random(); Domain=.www.example.com
// The domain of the cookie should be the given one (.www.example.com).
// We should collect data for the given domain and its subdomains.
yield testCookieCollection({
host: "http://www.example.com",
domain: ".www.example.com",
cookieHost: ".www.example.com",
cookieURIs: ["http://www.example.com/" + PATH],
noCookieURIs: ["http://example.com"]
});
});
/**
* Test multiple scenarios with different privacy levels.
*/
add_task(function* test_run_privacy_level() {
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
});
// With the default privacy level we collect all cookies.
yield testCookieCollection({
host: "http://example.com",
domain: ".example.com",
cookieHost: ".example.com",
cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// With the default privacy level we collect all cookies.
yield testCookieCollection({
host: "https://example.com",
domain: ".example.com",
cookieHost: ".example.com",
cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// With the default privacy level we collect all cookies.
yield testCookieCollection({
isSecure: true,
host: "https://example.com",
domain: ".example.com",
cookieHost: ".example.com",
cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// Set level=encrypted, don't store any secure cookies.
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1);
// With level=encrypted, non-secure cookies will be stored.
yield testCookieCollection({
host: "http://example.com",
domain: ".example.com",
cookieHost: ".example.com",
cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// With level=encrypted, non-secure cookies will be stored,
// even if sent by an HTTPS site.
yield testCookieCollection({
host: "https://example.com",
domain: ".example.com",
cookieHost: ".example.com",
cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
noCookieURIs: ["about:robots"]
});
// With level=encrypted, secure cookies will NOT be stored.
yield testCookieCollection({
isSecure: true,
host: "https://example.com",
domain: ".example.com",
cookieHost: ".example.com",
noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
});
// Set level=full, don't store any cookies.
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
// With level=full we must not store any cookies.
yield testCookieCollection({
host: "http://example.com",
domain: ".example.com",
cookieHost: ".example.com",
noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
});
// With level=full we must not store any cookies.
yield testCookieCollection({
host: "https://example.com",
domain: ".example.com",
cookieHost: ".example.com",
noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
});
// With level=full we must not store any cookies.
yield testCookieCollection({
isSecure: true,
host: "https://example.com",
domain: ".example.com",
cookieHost: ".example.com",
noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
});
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
});
/**
* Generic test function to check sessionstore's cookie collection module with
* different cookie domains given in the Set-Cookie header. See above for some
* usage examples.
*/
var testCookieCollection = async function(params) {
let tab = gBrowser.addTab("about:blank");
let browser = tab.linkedBrowser;
let urlParams = new URLSearchParams();
let value = Math.random();
urlParams.append("value", value);
if (params.domain) {
urlParams.append("domain", params.domain);
}
if (params.isSecure) {
urlParams.append("secure", "1");
}
// Construct request URI.
let requestUri = `${params.host}${PATH}browser_cookies.sjs?${urlParams}`;
// Wait for the browser to load and the cookie to be set.
// These two events can probably happen in no particular order,
// so let's wait for them in parallel.
await Promise.all([
waitForNewCookie(),
replaceCurrentURI(browser, requestUri)
function promiseSetCookie(cookie) {
info(`Set-Cookie: ${cookie}`);
return Promise.all([
waitForCookieChanged(),
ContentTask.spawn(gBrowser.selectedBrowser, cookie,
passedCookie => content.document.cookie = passedCookie)
]);
}
// Check all URIs for which the cookie should be collected.
for (let uri of params.cookieURIs || []) {
await replaceCurrentURI(browser, uri);
// Check the cookie.
let cookie = getCookie();
is(cookie.host, params.cookieHost, "cookie host is correct");
is(cookie.path, PATH, "cookie path is correct");
is(cookie.name, "foobar", "cookie name is correct");
is(cookie.value, value, "cookie value is correct");
}
// Check all URIs for which the cookie should NOT be collected.
for (let uri of params.noCookieURIs || []) {
await replaceCurrentURI(browser, uri);
// Cookie should be ignored.
ok(!getCookie(), "no cookie collected");
}
// Clean up.
gBrowser.removeTab(tab);
Services.cookies.removeAll();
};
/**
* Replace the current URI of the given browser by loading a new URI. The
* browser's session history will be completely replaced. This function ensures
* that the parent process has the lastest shistory data before resolving.
*/
var replaceCurrentURI = async function(browser, uri) {
// Replace the tab's current URI with the parent domain.
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
browser.loadURIWithFlags(uri, flags);
await promiseBrowserLoaded(browser);
// Ensure the tab's session history is up-to-date.
await TabStateFlusher.flush(browser);
};
/**
* Waits for a new "*example.com" cookie to be added.
*/
function waitForNewCookie() {
function waitForCookieChanged() {
return new Promise(resolve => {
Services.obs.addObserver(function observer(subj, topic, data) {
let cookie = subj.QueryInterface(Ci.nsICookie2);
if (data == "added" && cookie.host.endsWith("example.com")) {
Services.obs.removeObserver(observer, topic);
resolve();
}
Services.obs.removeObserver(observer, topic);
resolve();
}, "cookie-changed", false);
});
}
/**
* Retrieves the first cookie in the first window from the current sessionstore
* state.
*/
function getCookie() {
let state = JSON.parse(ss.getWindowState(window));
let cookies = state.windows[0].cookies || [];
return cookies[0] || null;
function cookieExists(host, name, value) {
let {cookies: [c]} = JSON.parse(ss.getBrowserState());
return c && c.host == host && c.name == name && c.value == value;
}
// Setup and cleanup.
add_task(function* test_setup() {
registerCleanupFunction(() => {
Services.cookies.removeAll();
});
});
// Test session cookie storage.
add_task(function* test_run() {
Services.cookies.removeAll();
// Add a new tab for testing.
gBrowser.selectedTab = gBrowser.addTab("http://example.com/");
yield promiseBrowserLoaded(gBrowser.selectedBrowser);
// Add a session cookie.
yield promiseSetCookie("foo=bar");
ok(cookieExists("example.com", "foo", "bar"), "cookie was added");
// Modify a session cookie.
yield promiseSetCookie("foo=baz");
ok(cookieExists("example.com", "foo", "baz"), "cookie was modified");
// Turn the session cookie into a normal one.
let expiry = new Date();
expiry.setDate(expiry.getDate() + 2);
yield promiseSetCookie(`foo=baz; Expires=${expiry.toUTCString()}`);
ok(!cookieExists("example.com", "foo", "baz"), "no longer a session cookie");
// Turn it back into a session cookie.
yield promiseSetCookie("foo=bar");
ok(cookieExists("example.com", "foo", "bar"), "again a session cookie");
// Remove the session cookie.
yield promiseSetCookie("foo=; Expires=Thu, 01 Jan 1970 00:00:00 GMT");
ok(!cookieExists("example.com", "foo", ""), "cookie was removed");
// Add a session cookie.
yield promiseSetCookie("foo=bar");
ok(cookieExists("example.com", "foo", "bar"), "cookie was added");
// Clear all session cookies.
Services.cookies.removeAll();
ok(!cookieExists("example.com", "foo", "bar"), "cookies were cleared");
// Cleanup.
yield promiseRemoveTab(gBrowser.selectedTab);
});

View File

@ -1,26 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.importGlobalProperties(["URLSearchParams"]);
function handleRequest(req, resp) {
resp.setStatusLine(req.httpVersion, 200);
let params = new URLSearchParams(req.queryString);
let value = params.get("value");
let domain = "";
if (params.has("domain")) {
domain = `; Domain=${params.get("domain")}`;
}
let secure = "";
if (params.has("secure")) {
secure = "; Secure";
}
resp.setHeader("Set-Cookie", `foobar=${value}${domain}${secure}`);
resp.write("<meta charset=utf-8>hi");
}

View File

@ -0,0 +1,71 @@
"use strict";
function createTestState() {
let r = Math.round(Math.random() * 100000);
let cookie = {
host: "http://example.com",
path: "/",
name: `name${r}`,
value: `value${r}`
};
let state = {
windows: [{
tabs: [{entries: [{url: "about:robots", triggeringPrincipal_base64}]}],
cookies: [cookie]
}]
};
return [state, cookie];
}
function waitForNewCookie({host, name, value}) {
info(`waiting for cookie ${name}=${value} from ${host}...`);
return new Promise(resolve => {
Services.obs.addObserver(function observer(subj, topic, data) {
if (data != "added") {
return;
}
let cookie = subj.QueryInterface(Ci.nsICookie2);
if (cookie.host == host && cookie.name == name && cookie.value == value) {
ok(true, "cookie added by the cookie service");
Services.obs.removeObserver(observer, topic);
resolve();
}
}, "cookie-changed", false);
});
}
// Setup and cleanup.
add_task(function* test_setup() {
Services.cookies.removeAll();
registerCleanupFunction(() => {
Services.cookies.removeAll();
});
});
// Test that calling ss.setWindowState() with a legacy state object that
// contains cookies in the window state restores session cookies properly.
add_task(function* test_window() {
let [state, cookie] = createTestState();
let win = yield promiseNewWindowLoaded();
let promiseCookie = waitForNewCookie(cookie);
ss.setWindowState(win, JSON.stringify(state), true);
yield promiseCookie;
yield BrowserTestUtils.closeWindow(win);
});
// Test that calling ss.setBrowserState() with a legacy state object that
// contains cookies in the window state restores session cookies properly.
add_task(function* test_browser() {
let backupState = ss.getBrowserState();
let [state, cookie] = createTestState();
yield Promise.all([waitForNewCookie(cookie), promiseBrowserState(state)]);
yield promiseBrowserState(backupState);
});

View File

@ -0,0 +1,110 @@
"use strict";
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision.
const MAX_EXPIRY = Math.pow(2, 62);
function addCookie(scheme, secure = false) {
let cookie = createTestCookie("http", secure);
Services.cookies.add(cookie.host, cookie.path, cookie.name, cookie.value,
cookie.secure, /* isHttpOnly = */ false,
/* isSession = */ true, MAX_EXPIRY,
/* originAttributes = */ {});
return cookie;
}
function createTestCookie(scheme, secure = false) {
let r = Math.round(Math.random() * 100000);
let cookie = {
host: `${scheme}://example.com`,
path: "/",
name: `name${r}`,
value: `value${r}`,
secure
};
return cookie;
}
function getCookie() {
let state = JSON.parse(ss.getBrowserState());
let cookies = state.cookies || [];
return cookies[0];
}
function compareCookies(a) {
let b = getCookie();
return a.host == b.host && a.name == b.name && a.value == b.value;
}
// Setup and cleanup.
add_task(function* test_setup() {
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
Services.cookies.removeAll();
});
});
// Test privacy_level=none (default). We store all session cookies.
add_task(function* test_level_none() {
Services.cookies.removeAll();
// Set level=none, store all cookies.
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 0);
// With the default privacy level we collect all cookies.
ok(compareCookies(addCookie("http")), "non-secure http cookie stored");
Services.cookies.removeAll();
// With the default privacy level we collect all cookies.
ok(compareCookies(addCookie("https")), "non-secure https cookie stored");
Services.cookies.removeAll();
// With the default privacy level we collect all cookies.
ok(compareCookies(addCookie("https", true)), "secure https cookie stored");
Services.cookies.removeAll();
});
// Test privacy_level=encrypted. We store all non-secure session cookies.
add_task(function* test_level_encrypted() {
Services.cookies.removeAll();
// Set level=encrypted, don't store any secure cookies.
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1);
// With level=encrypted, non-secure cookies will be stored.
ok(compareCookies(addCookie("http")), "non-secure http cookie stored");
Services.cookies.removeAll();
// With level=encrypted, non-secure cookies will be stored,
// even if sent by an HTTPS site.
ok(compareCookies(addCookie("https")), "non-secure https cookie stored");
Services.cookies.removeAll();
// With level=encrypted, non-secure cookies will be stored,
// even if sent by an HTTPS site.
ok(addCookie("https", true) && !getCookie(), "secure https cookie not stored");
Services.cookies.removeAll();
});
// Test privacy_level=full. We store no session cookies.
add_task(function* test_level_full() {
Services.cookies.removeAll();
// Set level=full, don't store any cookies.
Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
// With level=full we must not store any cookies.
ok(addCookie("http") && !getCookie(), "non-secure http cookie not stored");
Services.cookies.removeAll();
// With level=full we must not store any cookies.
ok(addCookie("https") && !getCookie(), "non-secure https cookie not stored");
Services.cookies.removeAll();
// With level=full we must not store any cookies.
ok(addCookie("https", true) && !getCookie(), "secure https cookie not stored");
Services.cookies.removeAll();
});

View File

@ -110,7 +110,8 @@ add_task(function* test() {
"set": [ [ "privacy.userContext.enabled", true ] ]
});
let lastSessionRestore;
Services.cookies.removeAll();
for (let userContextId of Object.keys(USER_CONTEXTS)) {
// Load the page in 3 different contexts and set a cookie
// which should only be visible in that context.
@ -128,13 +129,11 @@ add_task(function* test() {
// Ensure the tab's session history is up-to-date.
yield TabStateFlusher.flush(browser);
lastSessionRestore = ss.getWindowState(window);
// Remove the tab.
gBrowser.removeTab(tab);
}
let state = JSON.parse(lastSessionRestore);
is(state.windows[0].cookies.length, USER_CONTEXTS.length,
let state = JSON.parse(ss.getBrowserState());
is(state.cookies.length, USER_CONTEXTS.length,
"session restore should have each container's cookie");
});

View File

@ -170,6 +170,7 @@
#ifdef ACCESSIBILITY
#ifdef XP_WIN32
@BINPATH@/Accessible.tlb
@BINPATH@/AccessibleHandler.dll
@BINPATH@/AccessibleMarshal.dll
@BINPATH@/IA2Marshal.dll
#endif

View File

@ -55,6 +55,24 @@
!define UpdateChannel "@MOZ_UPDATE_CHANNEL@"
!endif
# AccessibleHandler.dll uses a different CLSID depending on release channel.
# These defines must match HANDLER_CLSID defined in
# accessible/ipc/win/handler/HandlerData.idl
#ifndef MOZ_OFFICIAL_BRANDING
#ifdef DEBUG
!define AccessibleHandlerCLSID "{398FFD8D-5382-48F7-9E3B-19012762D39A}"
#else
!define AccessibleHandlerCLSID "{CE573FAF-7815-4FC2-A031-B092268ACE9E}"
#endif
#elifdef NIGHTLY_BUILD
!define AccessibleHandlerCLSID "{4629216B-8753-41BF-9527-5BFF51401671}"
#elifdef RELEASE_OR_BETA
!define AccessibleHandlerCLSID "{1BAA303D-B4B9-45E5-9CCB-E3FCA3E274B6}"
#else
!define AccessibleHandlerCLSID "{4A195748-DCA2-45FB-9295-0A139E76A9E7}"
#endif
# Due to official and beta using the same branding this is needed to
# differentiante between the url used by the stub for downloading.
!if "@MOZ_UPDATE_CHANNEL@" == "beta"

View File

@ -307,6 +307,16 @@ Section "-Application" APP_IDX
ClearErrors
${RegisterDLL} "$INSTDIR\AccessibleHandler.dll"
${If} ${Errors}
${LogMsg} "** ERROR Registering: $INSTDIR\AccessibleHandler.dll **"
${Else}
${LogUninstall} "DLLReg: \AccessibleHandler.dll"
${LogMsg} "Registered: $INSTDIR\AccessibleHandler.dll"
${EndIf}
ClearErrors
; Default for creating Start Menu shortcut
; (1 = create, 0 = don't create)
${If} $AddStartMenuSC == ""

View File

@ -1160,6 +1160,7 @@
; should be ${FileMainEXE} so if it is in use the CheckForFilesInUse macro
; returns after the first check.
Push "end"
Push "AccessibleHandler.dll"
Push "AccessibleMarshal.dll"
Push "IA2Marshal.dll"
Push "freebl3.dll"

View File

@ -383,6 +383,12 @@ Section "Uninstall"
${UnregisterDLL} "$INSTDIR\AccessibleMarshal.dll"
${EndIf}
; Only unregister the dll if the registration points to this installation
ReadRegStr $R1 HKCR "CLSID\${AccessibleHandlerCLSID}\InprocHandler32" ""
${If} "$INSTDIR\AccessibleHandler.dll" == "$R1"
${UnregisterDLL} "$INSTDIR\AccessibleHandler.dll"
${EndIf}
${un.RemovePrecompleteEntries} "false"
${If} ${FileExists} "$INSTDIR\defaults\pref\channel-prefs.js"

View File

@ -49,7 +49,10 @@ if test -z "$bucket" -a -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PG
# prevent rerun if az is set, or wget is not available
if test -z "$availability_zone" -a -x "$(command -v wget)"; then
if test -n "${TASKCLUSTER_WORKER_GROUP}"; then
availability_zone="${TASKCLUSTER_WORKER_GROUP}"
# TASKCLUSTER_WORKER_GROUP is just the region now, so
# stick an extra character on to make the already-convoluted logic
# here simpler.
availability_zone="${TASKCLUSTER_WORKER_GROUP}x"
else
# timeout after 1 second, and don't retry (failure indicates instance is not in ec2 or network issue)
# availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a

Binary file not shown.

View File

@ -942,6 +942,7 @@ CARGO_BUILD = env $(environment_cleaner) $(rustflags_override) \
CLANG_PATH=$(MOZ_CLANG_PATH) \
PKG_CONFIG_ALLOW_CROSS=1 \
RUST_BACKTRACE=1 \
MOZ_TOPOBJDIR=$(topobjdir) \
$(CARGO) build $(cargo_build_flags)
ifdef RUST_LIBRARY_FILE

View File

@ -653,7 +653,7 @@ skip-if = (toolkit == 'android') # Android: Bug 775227
[test_innersize_scrollport.html]
[test_integer_attr_with_leading_zero.html]
[test_intersectionobservers.html]
skip-if = (os == "android") # Timing issues
skip-if = true # Track Bug 1320704
[test_link_prefetch.html]
skip-if = !e10s # Track Bug 1281415
[test_link_stylesheet.html]

View File

@ -3592,7 +3592,7 @@ HTMLInputElement::Select()
nsTextEditorState* tes = GetEditorState();
if (tes) {
nsFrameSelection* fs = tes->GetConstFrameSelection();
RefPtr<nsFrameSelection> fs = tes->GetConstFrameSelection();
if (fs && fs->MouseDownRecorded()) {
// This means that we're being called while the frame selection has a mouse
// down event recorded to adjust the caret during the mouse up event.

View File

@ -3076,23 +3076,31 @@ TabChild::ReinitRendering()
Unused << cb->SendGetCompositorOptions(mLayersId, &options);
mCompositorOptions = Some(options);
bool success;
nsTArray<LayersBackend> ignored;
PLayerTransactionChild* shadowManager =
cb->SendPLayerTransactionConstructor(ignored, LayersId(), &mTextureFactoryIdentifier, &success);
if (!success) {
NS_WARNING("failed to re-allocate layer transaction");
return;
}
if (gfxVars::UseWebRender()) {
RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(nullptr);
MOZ_ASSERT(lm->AsWebRenderLayerManager());
lm->AsWebRenderLayerManager()->Initialize(cb,
wr::AsPipelineId(mLayersId),
&mTextureFactoryIdentifier);
} else {
bool success;
nsTArray<LayersBackend> ignored;
PLayerTransactionChild* shadowManager =
cb->SendPLayerTransactionConstructor(ignored, LayersId(), &mTextureFactoryIdentifier, &success);
if (!success) {
NS_WARNING("failed to re-allocate layer transaction");
return;
}
if (!shadowManager) {
NS_WARNING("failed to re-construct LayersChild");
return;
}
if (!shadowManager) {
NS_WARNING("failed to re-construct LayersChild");
return;
}
RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(shadowManager);
ShadowLayerForwarder* lf = lm->AsShadowForwarder();
lf->IdentifyTextureHost(mTextureFactoryIdentifier);
RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(shadowManager);
ShadowLayerForwarder* lf = lm->AsShadowForwarder();
lf->IdentifyTextureHost(mTextureFactoryIdentifier);
}
InitAPZState();

View File

@ -4,3 +4,5 @@ support-files =
file_domainPolicy_base.html
[browser_domainPolicy.js]
[browser_memory_distribution_telemetry.js]
skip-if = !e10 # This is an e10s only probe.

View File

@ -0,0 +1,71 @@
"use strict";
var session = Cu.import("resource://gre/modules/TelemetrySession.jsm", {});
const DUMMY_PAGE_DATA_URI = `data:text/html,
<html>
<head>
<meta charset="utf-8"/>
<title>Dummy</title>
</head>
<body>
<h1 id='header'>Just a regular everyday normal page.</h1>
</body>
</html>`;
/**
* Tests the MEMORY_DISTRIBUTION_AMONG_CONTENT probe by opening a few tabs, then triggering
* the memory probes and waiting for the "gather-memory-telemetry-finished" notification.
*/
add_task(function* test_memory_distribution() {
waitForExplicitFinish();
if (SpecialPowers.getIntPref("dom.ipc.processCount", 1) < 2) {
ok(true, "Skip this test if e10s-multi is disabled.");
finish();
return;
}
yield SpecialPowers.pushPrefEnv({set: [["toolkit.telemetry.enabled", true]]});
Services.telemetry.canRecordExtended = true;
let histogram = Services.telemetry.getKeyedHistogramById("MEMORY_DISTRIBUTION_AMONG_CONTENT");
histogram.clear();
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE_DATA_URI);
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE_DATA_URI);
let tab3 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE_DATA_URI);
let finishedGathering = new Promise(resolve => {
let obs = function () {
Services.obs.removeObserver(obs, "gather-memory-telemetry-finished");
resolve();
}
Services.obs.addObserver(obs, "gather-memory-telemetry-finished");
});
session.TelemetrySession.getPayload();
yield finishedGathering;
let s = histogram.snapshot();
ok("0 - 10 tabs" in s, "We should have some samples by now in this bucket.")
for (var key in s) {
is(key, "0 - 10 tabs");
let fewTabsSnapshot = s[key];
ok(fewTabsSnapshot.sum > 0, "Zero difference between all the content processes is unlikely, what happened?");
ok(fewTabsSnapshot.sum < 80, "20 percentage difference on average is unlikely, what happened?");
let c = fewTabsSnapshot.counts;
for (let i = 10; i < c.length; i++) {
// If this check fails it means that one of the content processes uses at least 20% more or 20% less than the mean.
is(c[i], 0, "All the buckets above 10 should be empty");
}
}
histogram.clear();
yield BrowserTestUtils.removeTab(tab3);
yield BrowserTestUtils.removeTab(tab2);
yield BrowserTestUtils.removeTab(tab1);
finish();
});

View File

@ -64,6 +64,14 @@ VP8TrackEncoder::VP8TrackEncoder(TrackRate aTrackRate)
VP8TrackEncoder::~VP8TrackEncoder()
{
Destroy();
MOZ_COUNT_DTOR(VP8TrackEncoder);
}
void
VP8TrackEncoder::Destroy()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (mInitialized) {
vpx_codec_destroy(mVPXContext);
}
@ -71,7 +79,7 @@ VP8TrackEncoder::~VP8TrackEncoder()
if (mVPXImageWrapper) {
vpx_img_free(mVPXImageWrapper);
}
MOZ_COUNT_DTOR(VP8TrackEncoder);
mInitialized = false;
}
nsresult
@ -83,18 +91,15 @@ VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
}
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mFrameWidth = aWidth;
mFrameHeight = aHeight;
mDisplayWidth = aDisplayWidth;
mDisplayHeight = aDisplayHeight;
if (mInitialized) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
// Encoder configuration structure.
vpx_codec_enc_cfg_t config;
memset(&config, 0, sizeof(vpx_codec_enc_cfg_t));
if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config, 0)) {
return NS_ERROR_FAILURE;
}
nsresult rv = SetConfigurationValues(aWidth, aHeight, aDisplayWidth, aDisplayHeight, config);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Creating a wrapper to the image - setting image data to NULL. Actual
// pointer will be set in encode. Setting align to 1, as it is meaningless
@ -102,6 +107,71 @@ VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
vpx_img_wrap(mVPXImageWrapper, VPX_IMG_FMT_I420,
mFrameWidth, mFrameHeight, 1, nullptr);
vpx_codec_flags_t flags = 0;
flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
if (vpx_codec_enc_init(mVPXContext, vpx_codec_vp8_cx(), &config, flags)) {
return NS_ERROR_FAILURE;
}
vpx_codec_control(mVPXContext, VP8E_SET_STATIC_THRESHOLD, 1);
vpx_codec_control(mVPXContext, VP8E_SET_CPUUSED, -6);
vpx_codec_control(mVPXContext, VP8E_SET_TOKEN_PARTITIONS,
VP8_ONE_TOKENPARTITION);
mInitialized = true;
mon.NotifyAll();
return NS_OK;
}
nsresult
VP8TrackEncoder::Reconfigure(int32_t aWidth, int32_t aHeight,
int32_t aDisplayWidth, int32_t aDisplayHeight)
{
if(aWidth <= 0 || aHeight <= 0 || aDisplayWidth <= 0 || aDisplayHeight <= 0) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
if (!mInitialized) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
mInitialized = false;
// Recreate image wrapper
vpx_img_free(mVPXImageWrapper);
vpx_img_wrap(mVPXImageWrapper, VPX_IMG_FMT_I420, aWidth, aHeight, 1, nullptr);
// Encoder configuration structure.
vpx_codec_enc_cfg_t config;
nsresult rv = SetConfigurationValues(aWidth, aHeight, aDisplayWidth, aDisplayHeight, config);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Set new configuration
if (vpx_codec_enc_config_set(mVPXContext.get(), &config) != VPX_CODEC_OK) {
VP8LOG(LogLevel::Error, "Failed to set new configuration");
return NS_ERROR_FAILURE;
}
mInitialized = true;
return NS_OK;
}
nsresult
VP8TrackEncoder::SetConfigurationValues(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
int32_t aDisplayHeight, vpx_codec_enc_cfg_t& config)
{
mFrameWidth = aWidth;
mFrameHeight = aHeight;
mDisplayWidth = aDisplayWidth;
mDisplayHeight = aDisplayHeight;
// Encoder configuration structure.
memset(&config, 0, sizeof(vpx_codec_enc_cfg_t));
if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config, 0)) {
VP8LOG(LogLevel::Error, "Failed to get default configuration");
return NS_ERROR_FAILURE;
}
config.g_w = mFrameWidth;
config.g_h = mFrameHeight;
// TODO: Maybe we should have various aFrameRate bitrate pair for each devices?
@ -144,20 +214,6 @@ VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
// Ensure that we can output one I-frame per second.
config.kf_max_dist = 60;
vpx_codec_flags_t flags = 0;
flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
if (vpx_codec_enc_init(mVPXContext, vpx_codec_vp8_cx(), &config, flags)) {
return NS_ERROR_FAILURE;
}
vpx_codec_control(mVPXContext, VP8E_SET_STATIC_THRESHOLD, 1);
vpx_codec_control(mVPXContext, VP8E_SET_CPUUSED, -6);
vpx_codec_control(mVPXContext, VP8E_SET_TOKEN_PARTITIONS,
VP8_ONE_TOKENPARTITION);
mInitialized = true;
mon.NotifyAll();
return NS_OK;
}
@ -299,10 +355,31 @@ nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
}
if (img->GetSize() != IntSize(mFrameWidth, mFrameHeight)) {
VP8LOG(LogLevel::Error,
"Dynamic resolution changes (was %dx%d, now %dx%d) are unsupported",
VP8LOG(LogLevel::Info,
"Dynamic resolution change (was %dx%d, now %dx%d).",
mFrameWidth, mFrameHeight, img->GetSize().width, img->GetSize().height);
return NS_ERROR_FAILURE;
gfx::IntSize intrinsicSize = aChunk.mFrame.GetIntrinsicSize();
gfx::IntSize imgSize = aChunk.mFrame.GetImage()->GetSize();
if (imgSize <= IntSize(mFrameWidth, mFrameHeight) && // check buffer size instead
// If the new size is less than or equal to old,
// the existing encoder instance can continue.
NS_SUCCEEDED(Reconfigure(imgSize.width,
imgSize.height,
intrinsicSize.width,
intrinsicSize.height))) {
VP8LOG(LogLevel::Info, "Reconfigured VP8 encoder.");
} else {
// New frame size is larger; re-create the encoder.
Destroy();
nsresult rv = Init(imgSize.width,
imgSize.height,
intrinsicSize.width,
intrinsicSize.height);
VP8LOG(LogLevel::Info, "Recreated VP8 encoder.");
NS_ENSURE_SUCCESS(rv, rv);
}
}
ImageFormat format = img->GetFormat();
@ -335,7 +412,7 @@ nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
uint32_t halfHeight = (mFrameHeight + 1) / 2;
uint32_t uvPlaneSize = halfWidth * halfHeight;
if (mI420Frame.IsEmpty()) {
if (mI420Frame.Length() != yPlaneSize + uvPlaneSize * 2) {
mI420Frame.SetLength(yPlaneSize + uvPlaneSize * 2);
}
@ -567,6 +644,7 @@ VP8TrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
if (vpx_codec_encode(mVPXContext, mVPXImageWrapper, mEncodedTimestamp,
(unsigned long)chunk.GetDuration(), flags,
VPX_DL_REALTIME)) {
VP8LOG(LogLevel::Error, "vpx_codec_encode failed to encode the frame.");
return NS_ERROR_FAILURE;
}
// Get the encoded data from VP8 encoder.

View File

@ -52,6 +52,17 @@ private:
// Prepare the input data to the mVPXImageWrapper for encoding.
nsresult PrepareRawFrame(VideoChunk &aChunk);
// Re-configures an existing encoder with a new frame size.
nsresult Reconfigure(int32_t aWidth, int32_t aHeight,
int32_t aDisplayWidth, int32_t aDisplayHeight);
// Destroys the context and image wrapper. Does not de-allocate the structs.
void Destroy();
// Helper method to set the values on a VPX configuration.
nsresult SetConfigurationValues(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
int32_t aDisplayHeight, vpx_codec_enc_cfg_t& config);
// Encoded timestamp.
StreamTime mEncodedTimestamp;

View File

@ -5,7 +5,7 @@
#ifndef JavaCallbacksSupport_h_
#define JavaCallbacksSupport_h_
#include "FennecJNINatives.h"
#include "GeneratedJNINatives.h"
#include "MediaResult.h"
#include "MediaCodec.h"

View File

@ -821,6 +821,12 @@ tags=msg
[test_mediarecorder_record_changing_video_resolution.html]
skip-if = android_version == '17' # android(bug 1232305)
tags=msg
[test_mediarecorder_record_upsize_resolution.html]
skip-if = android_version == '17' # android(bug 1232305)
tags=msg
[test_mediarecorder_record_downsize_resolution.html]
skip-if = android_version == '17' # android(bug 1232305)
tags=msg
[test_mediarecorder_record_gum_video_timeslice.html]
skip-if = android_version == '17' # bug 1297298, android(bug 1232305)
tags=msg

View File

@ -15,53 +15,148 @@ function startTest() {
var content = document.getElementById("content");
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
const resolution_change = [
{width: 100, height: 100, color: "red"},
{width: 150, height: 150, color: "blue"},
{width: 100, height: 100, color: "red"},
];
canvas.width = resolution_change[0].width;
canvas.height = resolution_change[0].height;
var ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillStyle = resolution_change[0].color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
var stream = canvas.captureStream(0);
// The recorded stream coming from canvas.
var stream = canvas.captureStream();
var numErrorRaised = 0;
// Check values for events
var numDataAvailabledRaised = 0;
var numResizeRaised = 0;
// Recorded data that will be playback.
var blob;
mediaRecorder = new MediaRecorder(stream);
// Media recorder for VP8 and canvas stream.
var mediaRecorder = new MediaRecorder(stream);
is(mediaRecorder.stream, stream,
"Media recorder stream = canvas stream at the start of recording");
mediaRecorder.onwarning = () => ok(false, "onwarning unexpectedly fired");
// Not expected events.
mediaRecorder.onwarning = () => ok(false, "MediaRecorder: onwarning unexpectedly fired");
mediaRecorder.onerror = err => {
info("Got 'error' event, " + err.name + " (" + err.message + ")");
++numErrorRaised;
ok(false, "MediaRecorder: onerror unexpectedly fired. Code " + err.name);
SimpleTest.finish();
};
// When recorder is stopped get recorded data.
mediaRecorder.ondataavailable = ev => {
info("Got 'dataavailable' event");
++numDataAvailabledRaised;
is(blob, undefined, "Should only get one dataavailable event");
// Save recorded data for playback
blob = ev.data;
};
mediaRecorder.onstart = () => {
canvas.width = canvas.height = canvas.width * 1.1;
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
stream.requestFrame();
info('onstart fired successfully');
};
mediaRecorder.onstop = () => {
info("Got 'stop' event");
is(numErrorRaised, 1, "Should have gotten 1 error event");
is(numDataAvailabledRaised, 1, "Should have gotten 1 dataavailable event");
SimpleTest.finish();
// Playback stream and verify resolution changes.
ok(blob, "Should have gotten a data blob");
var video = document.createElement("video");
video.id = "recorded-video";
video.src = URL.createObjectURL(blob);
video.preload = "metadata";
video.onerror = err => {
ok(false, "Should be able to play the recording. Got error. code=" + video.error.code);
SimpleTest.finish();
};
video.onloadedmetadata = function(){
info("onloadedmetadata");
video.seekToNextFrame()
.then(()=>video.seekToNextFrame())
.catch((reason)=>{
info("seekToNextFrame rejected: " + reason)
callSeekToNextFrame();
});
}
// Check that resize is correct for playback stream.
video.onresize = function() {
if (numResizeRaised < resolution_change.length) {
is(video.videoWidth, resolution_change[numResizeRaised].width,
"onresize width should be as expected");
is(video.videoHeight, resolution_change[numResizeRaised].height,
"onresize height should be as expected");
if (numResizeRaised > 0) {
callSeekToNextFrame();
}
} else {
ok(false, "Got more resize events than expected");
}
++numResizeRaised;
};
video.onended = function() {
is(numResizeRaised, resolution_change.length, "Expected number of resize events");
SimpleTest.finish();
};
document.getElementById("content").appendChild(video);
function callSeekToNextFrame() {
video.seekToNextFrame()
.then()
.catch((reason)=>{
info("seekToNextFrame rejected: " + reason)
callSeekToNextFrame();
});
};
};
// Start here by stream recorder.
mediaRecorder.start();
is(mediaRecorder.state, "recording", "Media recorder should be recording");
requestAnimationFrame(draw);
// Change resolution in every frame
// Stop recorder on last frame
var countFrames=0;
var previous_time = performance.now();
function draw(timestamp) {
if (timestamp - previous_time < 100) {
requestAnimationFrame(draw);
return;
}
previous_time = timestamp;
if (countFrames < resolution_change.length) {
canvas.width = resolution_change[countFrames].width;
canvas.height = resolution_change[countFrames].height;
ctx.fillStyle = resolution_change[countFrames].color;
// Resize and draw canvas
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Register draw to be called on next rendering
requestAnimationFrame(draw);
countFrames++;
} else {
mediaRecorder.stop();
}
}
}
SimpleTest.waitForExplicitFinish();
startTest();
SpecialPowers.pushPrefEnv(
{
"set": [["media.seekToNextFrame.enabled", true ]],
"set": [["media.video-queue.send-to-compositor-size", 1]]
}, startTest);
</script>
</pre>

View File

@ -0,0 +1,146 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Recording canvas dynamically changes to greater resolution</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/dom/canvas/test/captureStream_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<div id="content">
</div>
<script class="testbody" type="text/javascript">
function startTest() {
var canvas = document.createElement("canvas");
var canvas_size = 100;
var new_canvas_size = 50;
canvas.width = canvas.height = canvas_size;
var helper = new CaptureStreamTestHelper2D(canvas_size, canvas_size);
helper.drawColor(canvas, helper.red);
// The recorded stream coming from canvas.
var stream = canvas.captureStream();
// Check values for events
var numDataAvailabledRaised = 0;
var numResizeRaised = 0;
// Recorded data that will be playback.
var blob;
// Media recorder for VP8 and canvas stream.
var mediaRecorder = new MediaRecorder(stream);
is(mediaRecorder.stream, stream,
"Media recorder stream = canvas stream at the beginning of recording");
// Not expected events.
mediaRecorder.onwarning = () => ok(false, "MediaRecorder: onwarning unexpectedly fired");
mediaRecorder.onerror = err => {
ok(false, "MediaRecorder: onerror unexpectedly fired. Code " + err.name);
SimpleTest.finish();
}
// When recorder is stopped get recorded data.
mediaRecorder.ondataavailable = ev => {
info("Got 'dataavailable' event");
++numDataAvailabledRaised;
is(blob, undefined, "On dataavailable event blob is undefined");
// Save recorded data for playback
blob = ev.data;
};
mediaRecorder.onstart = () => {
info('onstart fired successfully');
};
mediaRecorder.onstop = () => {
info("Got 'stop' event");
is(numDataAvailabledRaised, 1, "Expected 1 dataavailable event");
// Playback stream and verify resolution changes.
ok(blob, "Should have gotten a data blob");
var video = document.createElement("video");
video.id = "recorded-video";
video.src = URL.createObjectURL(blob);
video.onerror = err => {
ok(false, "Should be able to play the recording. Got error. code=" + video.error.code);
SimpleTest.finish();
};
// Check here that resize is correct in the playback stream.
video.onresize = function() {
++numResizeRaised;
if (numResizeRaised == 1) {
is(this.videoWidth, canvas_size, "1st resize event original width");
is(this.videoHeight, canvas_size, "1st resize event original height ");
} else if (numResizeRaised == 2) {
is(this.videoWidth, new_canvas_size, "2nd resize event new width");
is(this.videoHeight, new_canvas_size, "2nd resize event new height");
} else {
ok(false, "Only 2 numResizeRaised events are expected");
}
};
video.onended = () => {
is(numResizeRaised, 2, "Expected 2 resize event");
};
document.getElementById("content").appendChild(video);
video.play();
// Check last color
helper.waitForPixelColor(video, helper.red, 128, "Should become red")
.then(() => {
video.onresize = {};
video.onended = {};
SimpleTest.finish();
});
};
// Start here by stream recorder.
mediaRecorder.start();
is(mediaRecorder.state, "recording", "Media recorder started");
requestAnimationFrame(draw);
// Change resolution every 100ms
var countFrames=0;
var previous_time = performance.now();
function draw(timestamp) {
if (timestamp - previous_time < 100) {
requestAnimationFrame(draw);
return;
}
previous_time = timestamp;
var size = 0;
var color = "";
if (countFrames < 1) {
// Initial size
size = canvas_size;
color = helper.blue;
} else if (countFrames < 2) {
// upsize
size = new_canvas_size;
color = helper.red;
} else {
// Stop recoredr on last frame
mediaRecorder.stop();
return;
}
// Resize and draw canvas
canvas.width = canvas.height = size;
helper.drawColor(canvas, color);
// Register next draw on every call
requestAnimationFrame(draw);
countFrames++;
}
}
SimpleTest.waitForExplicitFinish();
startTest();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,146 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Recording canvas dynamically changes to greater resolution</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/dom/canvas/test/captureStream_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<div id="content">
</div>
<script class="testbody" type="text/javascript">
function startTest() {
var canvas = document.createElement("canvas");
var canvas_size = 100;
var new_canvas_size = 150;
canvas.width = canvas.height = canvas_size;
var helper = new CaptureStreamTestHelper2D(canvas_size, canvas_size);
helper.drawColor(canvas, helper.red);
// The recorded stream coming from canvas.
var stream = canvas.captureStream();
// Check values for events
var numDataAvailabledRaised = 0;
var numResizeRaised = 0;
// Recorded data that will be playback.
var blob;
// Media recorder for VP8 and canvas stream.
var mediaRecorder = new MediaRecorder(stream);
is(mediaRecorder.stream, stream,
"Media recorder stream = canvas stream at the beginning of recording");
// Not expected events.
mediaRecorder.onwarning = () => ok(false, "MediaRecorder: onwarning unexpectedly fired");
mediaRecorder.onerror = err => {
ok(false, "MediaRecorder: onerror unexpectedly fired. Code " + err.name);
SimpleTest.finish();
}
// When recorder is stopped get recorded data.
mediaRecorder.ondataavailable = ev => {
info("Got 'dataavailable' event");
++numDataAvailabledRaised;
is(blob, undefined, "On dataavailable event blob is undefined");
// Save recorded data for playback
blob = ev.data;
};
mediaRecorder.onstart = () => {
info('onstart fired successfully');
};
mediaRecorder.onstop = () => {
info("Got 'stop' event");
is(numDataAvailabledRaised, 1, "Expected 1 dataavailable event");
// Playback stream and verify resolution changes.
ok(blob, "Should have gotten a data blob");
var video = document.createElement("video");
video.id = "recorded-video";
video.src = URL.createObjectURL(blob);
video.onerror = err => {
ok(false, "Should be able to play the recording. Got error. code=" + video.error.code);
SimpleTest.finish();
};
// Check here that resize is correct in the playback stream.
video.onresize = function() {
++numResizeRaised;
if (numResizeRaised == 1) {
is(this.videoWidth, canvas_size, "1st resize event original width");
is(this.videoHeight, canvas_size, "1st resize event original height ");
} else if (numResizeRaised == 2) {
is(this.videoWidth, new_canvas_size, "2nd resize event new width");
is(this.videoHeight, new_canvas_size, "2nd resize event new height");
} else {
ok(false, "Only 2 numResizeRaised events are expected");
}
};
video.onended = () => {
is(numResizeRaised, 2, "Expected 2 resize event");
};
document.getElementById("content").appendChild(video);
video.play();
// Check last color
helper.waitForPixelColor(video, helper.red, 128, "Should become red")
.then(() => {
video.onresize = {};
video.onended = {};
SimpleTest.finish();
});
};
// Start here by stream recorder.
mediaRecorder.start();
is(mediaRecorder.state, "recording", "Media recorder started");
requestAnimationFrame(draw);
// Change resolution every 100 ms
var countFrames=0;
var previous_time = performance.now();
function draw(timestamp) {
if (timestamp - previous_time < 100) {
requestAnimationFrame(draw);
return;
}
previous_time = timestamp;
var size = 0;
var color = "";
if (countFrames < 1) {
// Initial size
size = canvas_size;
color = helper.blue;
} else if (countFrames < 2) {
// upsize
size = new_canvas_size;
color = helper.red;
}else {
// Stop recoredr on last frame
mediaRecorder.stop();
return;
}
// Resize and draw canvas
canvas.width = canvas.height = size;
helper.drawColor(canvas, color);
// Register next draw on every call
requestAnimationFrame(draw);
countFrames++;
}
}
SimpleTest.waitForExplicitFinish();
startTest();
</script>
</pre>
</body>
</html>

View File

@ -85,7 +85,7 @@ public:
RefPtr<TransferBuffer> transfer =
new TransferBuffer(aStream, aInput.AsAudioChunk());
NS_DispatchToMainThread(transfer);
mAbstractMainThread->Dispatch(transfer.forget());
}
virtual bool IsActive() const override

View File

@ -827,7 +827,7 @@ AudioBufferSourceNode::NotifyMainThreadStreamFinished()
RefPtr<AudioBufferSourceNode> mNode;
};
NS_DispatchToMainThread(new EndedEventDispatcher(this));
Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this)));
// Drop the playing reference
// Warning: The below line might delete this.

View File

@ -10,6 +10,7 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/AnalyserNode.h"
#include "mozilla/dom/AnalyserNodeBinding.h"
@ -762,6 +763,24 @@ private:
RefPtr<AudioContext> mAudioContext;
};
void
AudioContext::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIGlobalObject> parentObject =
do_QueryInterface(GetParentObject());
// It can happen that this runnable took a long time to reach the main thread,
// and the global is not valid anymore.
if (parentObject) {
parentObject->AbstractMainThreadFor(TaskCategory::Other)
->Dispatch(std::move(aRunnable));
} else {
RefPtr<nsIRunnable> runnable(aRunnable);
runnable = nullptr;
}
}
void
AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
{
@ -826,9 +845,8 @@ AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
}
if (mAudioContextState != aNewState) {
RefPtr<OnStateChangeTask> onStateChangeTask =
new OnStateChangeTask(this);
NS_DispatchToMainThread(onStateChangeTask);
RefPtr<OnStateChangeTask> task = new OnStateChangeTask(this);
Dispatch(task.forget());
}
mAudioContextState = aNewState;

View File

@ -328,6 +328,8 @@ public:
bool CheckClosed(ErrorResult& aRv);
void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable);
private:
void DisconnectFromWindow();
void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);

View File

@ -167,9 +167,8 @@ public:
aNode->ResolvePromise(renderedBuffer);
RefPtr<OnCompleteTask> onCompleteTask =
new OnCompleteTask(context, renderedBuffer);
NS_DispatchToMainThread(onCompleteTask);
mAbstractMainThread->Dispatch(do_AddRef(new OnCompleteTask(context,
renderedBuffer)));
context->OnStateChanged(nullptr, AudioContextState::Closed);
}

View File

@ -275,7 +275,7 @@ ConstantSourceNode::NotifyMainThreadStreamFinished()
RefPtr<ConstantSourceNode> mNode;
};
NS_DispatchToMainThread(new EndedEventDispatcher(this));
Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this)));
// Drop the playing reference
// Warning: The below line might delete this.

View File

@ -169,7 +169,7 @@ private:
float mReduction;
};
NS_DispatchToMainThread(new Command(aStream, aReduction));
mAbstractMainThread->Dispatch(do_AddRef(new Command(aStream, aReduction)));
}
private:

View File

@ -104,11 +104,12 @@ private:
mDecodeJob.OnFailure(aErrorCode);
} else {
// Take extra care to cleanup on the main thread
NS_DispatchToMainThread(NewRunnableMethod(this, &MediaDecodeTask::Cleanup));
mMainThread->Dispatch(NewRunnableMethod(this, &MediaDecodeTask::Cleanup));
nsCOMPtr<nsIRunnable> event =
new ReportResultTask(mDecodeJob, &WebAudioDecodeJob::OnFailure, aErrorCode);
NS_DispatchToMainThread(event);
mMainThread->Dispatch(event.forget());
}
}
@ -142,6 +143,7 @@ private:
RefPtr<MediaDecoderReader> mDecoderReader;
MediaInfo mMediaInfo;
MediaQueue<AudioData> mAudioQueue;
RefPtr<AbstractThread> mMainThread;
bool mFirstFrameDecoded;
};
@ -201,9 +203,9 @@ MediaDecodeTask::CreateReader()
mLength, principal, mContainerType);
MOZ_ASSERT(!mBufferDecoder);
RefPtr<AbstractThread> mainThread =
mMainThread =
mDecodeJob.mContext->GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other);
mBufferDecoder = new BufferDecoder(resource, mainThread,
mBufferDecoder = new BufferDecoder(resource, mMainThread,
new BufferDecoderGMPCrashHelper(parent));
// If you change this list to add support for new decoders, please consider
@ -441,7 +443,7 @@ MediaDecodeTask::FinishDecode()
}
mPhase = PhaseEnum::AllocateBuffer;
NS_DispatchToMainThread(this);
mMainThread->Dispatch(do_AddRef(this));
}
void
@ -499,7 +501,7 @@ AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
&WebAudioDecodeJob::OnFailure,
WebAudioDecodeJob::UnknownContent);
JS_free(nullptr, aBuffer);
NS_DispatchToMainThread(event);
aDecodeJob.mContext->Dispatch(event.forget());
return;
}
@ -510,7 +512,7 @@ AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
new ReportResultTask(aDecodeJob,
&WebAudioDecodeJob::OnFailure,
WebAudioDecodeJob::UnknownError);
NS_DispatchToMainThread(event);
aDecodeJob.mContext->Dispatch(event.forget());
} else {
// If we did this without a temporary:
// task->Reader()->OwnerThread()->Dispatch(task.forget())

View File

@ -598,7 +598,7 @@ OscillatorNode::NotifyMainThreadStreamFinished()
RefPtr<OscillatorNode> mNode;
};
NS_DispatchToMainThread(new EndedEventDispatcher(this));
Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this)));
// Drop the playing reference
// Warning: The below line might delete this.

View File

@ -468,8 +468,9 @@ private:
double mPlaybackTime;
};
NS_DispatchToMainThread(new Command(aStream, mInputBuffer.forget(),
playbackTime));
RefPtr<Command> command = new Command(aStream, mInputBuffer.forget(),
playbackTime);
mAbstractMainThread->Dispatch(command.forget());
}
friend class ScriptProcessorNode;

View File

@ -232,6 +232,12 @@ SVGFEConvolveMatrixElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstan
Size kernelUnitLength =
GetKernelUnitLength(aInstance, &mNumberPairAttributes[KERNEL_UNIT_LENGTH]);
if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) {
// According to spec, A negative or zero value is an error. See link below for details.
// https://www.w3.org/TR/SVG/filters.html#feConvolveMatrixElementKernelUnitLengthAttribute
return failureDescription;
}
FilterPrimitiveDescription descr(PrimitiveType::ConvolveMatrix);
AttributeMap& atts = descr.Attributes();
atts.Set(eConvolveMatrixKernelSize, IntSize(orderX, orderY));

View File

@ -596,9 +596,9 @@ var interfaceNamesInGlobalScope =
// IMPORTANT: Do not change this list without review from a DOM peer!
"InstallTrigger",
// IMPORTANT: Do not change this list without review from a DOM peer!
"IntersectionObserver",
{name: "IntersectionObserver", disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IntersectionObserverEntry",
{name: "IntersectionObserverEntry", disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"KeyEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -25,6 +25,7 @@
// outparams using the &-operator. But it will have to do as there's no easy
// solution.
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/DebugOnly.h"
@ -696,6 +697,27 @@ struct GlyphMetrics
Float mHeight;
};
class UnscaledFont
: public external::AtomicRefCounted<UnscaledFont>
, public SupportsWeakPtr<UnscaledFont>
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFont)
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(UnscaledFont)
virtual ~UnscaledFont();
virtual FontType GetType() const = 0;
static uint32_t DeletionCounter() { return sDeletionCounter; }
protected:
UnscaledFont() {}
private:
static uint32_t sDeletionCounter;
};
/** This class is an abstraction of a backend/platform specific font object
* at a particular size. It is passed into text drawing calls to describe
* the font used for the drawing call.
@ -718,6 +740,7 @@ public:
typedef void (*FontDescriptorOutput)(const uint8_t* aData, uint32_t aLength, Float aFontSize, void* aBaton);
virtual FontType GetType() const = 0;
virtual Float GetSize() const = 0;
virtual AntialiasMode GetDefaultAAMode();
/** This allows getting a path that describes the outline of a set of glyphs.
@ -753,10 +776,15 @@ public:
return mUserData.Get(key);
}
const RefPtr<UnscaledFont>& GetUnscaledFont() const { return mUnscaledFont; }
protected:
ScaledFont() {}
explicit ScaledFont(const RefPtr<UnscaledFont>& aUnscaledFont)
: mUnscaledFont(aUnscaledFont)
{}
UserData mUserData;
RefPtr<UnscaledFont> mUnscaledFont;
};
/**
@ -1426,11 +1454,14 @@ public:
CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
static already_AddRefed<ScaledFont>
CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
CreateScaledFontForNativeFont(const NativeFont &aNativeFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize);
#ifdef MOZ_WIDGET_GTK
static already_AddRefed<ScaledFont>
CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
#endif
/**
@ -1462,7 +1493,10 @@ public:
* cairo_scaled_font_t* parameters must correspond to the same font.
*/
static already_AddRefed<ScaledFont>
CreateScaledFontWithCairo(const NativeFont &aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont);
CreateScaledFontWithCairo(const NativeFont &aNativeFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize,
cairo_scaled_font_t* aScaledFont);
/**
* This creates a simple data source surface for a certain size. It allocates
@ -1588,6 +1622,7 @@ public:
static already_AddRefed<ScaledFont>
CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
const gfxFontStyle* aStyle,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize,
bool aUseEmbeddedBitmap,
bool aForceGDIMode);

View File

@ -469,31 +469,33 @@ Factory::GetMaxSurfaceSize(BackendType aType)
}
already_AddRefed<ScaledFont>
Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
{
switch (aNativeFont.mType) {
#ifdef WIN32
case NativeFontType::DWRITE_FONT_FACE:
{
return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aUnscaledFont, aSize);
}
#if defined(USE_CAIRO) || defined(USE_SKIA)
case NativeFontType::GDI_FONT_FACE:
{
return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aUnscaledFont, aSize);
}
#endif
#endif
#ifdef XP_DARWIN
case NativeFontType::MAC_FONT_FACE:
{
return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aUnscaledFont, aSize);
}
#endif
#if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
case NativeFontType::CAIRO_FONT_FACE:
{
return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize);
return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aUnscaledFont, aSize);
}
#endif
default:
@ -564,14 +566,17 @@ Factory::CreateScaledFontFromFontDescriptor(FontType aType, const uint8_t* aData
}
already_AddRefed<ScaledFont>
Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont)
Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize,
cairo_scaled_font_t* aScaledFont)
{
#ifdef USE_CAIRO
// In theory, we could pull the NativeFont out of the cairo_scaled_font_t*,
// but that would require a lot of code that would be otherwise repeated in
// various backends.
// Therefore, we just reuse CreateScaledFontForNativeFont's implementation.
RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize);
RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aUnscaledFont, aSize);
static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont);
return font.forget();
#else
@ -581,9 +586,10 @@ Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, c
#ifdef MOZ_WIDGET_GTK
already_AddRefed<ScaledFont>
Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize)
{
return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aSize);
return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aUnscaledFont, aSize);
}
#endif
@ -745,11 +751,12 @@ Factory::D2DCleanup()
already_AddRefed<ScaledFont>
Factory::CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
const gfxFontStyle* aStyle,
const RefPtr<UnscaledFont>& aUnscaledFont,
float aSize,
bool aUseEmbeddedBitmap,
bool aForceGDIMode)
{
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aSize,
return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
aUseEmbeddedBitmap, aForceGDIMode,
aStyle);
}

View File

@ -275,7 +275,7 @@ NativeFontResourceDWrite::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
return nullptr;
}
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, aGlyphSize);
RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, nullptr, aGlyphSize);
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
gfxWarning() << "Unable to create cairo scaled font DWrite font.";
return nullptr;

View File

@ -50,7 +50,7 @@ NativeFontResourceGDI::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
// Constructor for ScaledFontWin dereferences and copies the LOGFONT, so we
// are safe to pass this reference.
RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, aGlyphSize);
RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, nullptr, aGlyphSize);
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
gfxWarning() << "Unable to create cairo scaled font GDI font.";
return nullptr;

View File

@ -209,7 +209,7 @@ already_AddRefed<ScaledFont>
NativeFontResourceMac::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
{
RefPtr<ScaledFontBase> scaledFont = new ScaledFontMac(mFontRef, aGlyphSize);
RefPtr<ScaledFontBase> scaledFont = new ScaledFontMac(mFontRef, nullptr, aGlyphSize);
if (!scaledFont->PopulateCairoScaledFont()) {
gfxWarning() << "Unable to create cairo scaled Mac font.";

View File

@ -26,6 +26,13 @@ using namespace std;
namespace mozilla {
namespace gfx {
uint32_t UnscaledFont::sDeletionCounter = 0;
UnscaledFont::~UnscaledFont()
{
sDeletionCounter++;
}
AntialiasMode
ScaledFont::GetDefaultAAMode()
{
@ -46,8 +53,10 @@ ScaledFontBase::~ScaledFontBase()
#endif
}
ScaledFontBase::ScaledFontBase(Float aSize)
: mSize(aSize)
ScaledFontBase::ScaledFontBase(const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
: ScaledFont(aUnscaledFont)
, mSize(aSize)
{
#ifdef USE_SKIA
mTypeface = nullptr;

View File

@ -28,7 +28,7 @@ class ScaledFontBase : public ScaledFont
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontBase)
explicit ScaledFontBase(Float aSize);
ScaledFontBase(const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
virtual ~ScaledFontBase();
virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
@ -37,7 +37,7 @@ public:
virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
float GetSize() { return mSize; }
virtual Float GetSize() const { return mSize; }
#ifdef USE_SKIA
virtual SkTypeface* GetSkTypeface() { return mTypeface; }

View File

@ -17,9 +17,11 @@ namespace gfx {
// an SkFontHost implementation that allows Skia to render using this.
// This is mainly because FT_Face is not good for sharing between libraries, which
// is a requirement when we consider runtime switchable backends and so on
ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize)
: ScaledFontBase(aSize)
{
ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
: ScaledFontBase(aUnscaledFont, aSize)
{
SetCairoScaledFont(aScaledFont);
}

View File

@ -18,7 +18,9 @@ class ScaledFontCairo : public ScaledFontBase
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontCairo)
ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize);
ScaledFontCairo(cairo_scaled_font_t* aScaledFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize);
#if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
virtual SkTypeface* GetSkTypeface();

View File

@ -102,10 +102,13 @@ DWriteFontStretchFromStretch(int16_t aStretch)
}
}
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize,
bool aUseEmbeddedBitmap, bool aForceGDIMode,
ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize,
bool aUseEmbeddedBitmap,
bool aForceGDIMode,
const gfxFontStyle* aStyle)
: ScaledFontBase(aSize)
: ScaledFontBase(aUnscaledFont, aSize)
, mFontFace(aFontFace)
, mUseEmbeddedBitmap(aUseEmbeddedBitmap)
, mForceGDIMode(aForceGDIMode)

View File

@ -19,15 +19,21 @@ class ScaledFontDWrite final : public ScaledFontBase
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
: ScaledFontBase(aSize)
ScaledFontDWrite(IDWriteFontFace *aFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
: ScaledFontBase(aUnscaledFont, aSize)
, mFontFace(aFont)
, mUseEmbeddedBitmap(false)
, mForceGDIMode(false)
{}
ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize, bool aUseEmbeddedBitmap,
bool aForceGDIMode, const gfxFontStyle* aStyle);
ScaledFontDWrite(IDWriteFontFace *aFontFace,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize,
bool aUseEmbeddedBitmap,
bool aForceGDIMode,
const gfxFontStyle* aStyle);
virtual FontType GetType() const { return FontType::DWRITE; }

View File

@ -23,8 +23,9 @@ namespace gfx {
// is a requirement when we consider runtime switchable backends and so on
ScaledFontFontconfig::ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont,
FcPattern* aPattern,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
: ScaledFontBase(aSize),
: ScaledFontBase(aUnscaledFont, aSize),
mPattern(aPattern)
{
SetCairoScaledFont(aScaledFont);
@ -331,7 +332,7 @@ ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
}
RefPtr<ScaledFontFontconfig> scaledFont =
new ScaledFontFontconfig(cairoScaledFont, pattern, aSize);
new ScaledFontFontconfig(cairoScaledFont, pattern, nullptr, aSize);
FcPatternDestroy(pattern);

View File

@ -19,7 +19,8 @@ class ScaledFontFontconfig : public ScaledFontBase
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontFontconfig, override)
ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
~ScaledFontFontconfig();
FontType GetType() const override { return FontType::FONTCONFIG; }
@ -28,6 +29,8 @@ public:
SkTypeface* GetSkTypeface() override;
#endif
bool CanSerialize() override { return true; }
bool GetFontFileData(FontFileDataOutput aDataCallback, void* aBaton) override;
bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;

View File

@ -67,8 +67,10 @@ CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize)
return ctFont;
}
ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize)
: ScaledFontBase(aSize)
ScaledFontMac::ScaledFontMac(CGFontRef aFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
: ScaledFontBase(aUnscaledFont, aSize)
{
if (!sSymbolLookupDone) {
CTFontDrawGlyphsPtr =

View File

@ -41,7 +41,7 @@ class ScaledFontMac : public ScaledFontBase
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontMac)
ScaledFontMac(CGFontRef aFont, Float aSize);
ScaledFontMac(CGFontRef aFont, const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
virtual ~ScaledFontMac();
virtual FontType GetType() const { return FontType::MAC; }

View File

@ -22,8 +22,10 @@
namespace mozilla {
namespace gfx {
ScaledFontWin::ScaledFontWin(const LOGFONT* aFont, Float aSize)
: ScaledFontBase(aSize)
ScaledFontWin::ScaledFontWin(const LOGFONT* aFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize)
: ScaledFontBase(aUnscaledFont, aSize)
, mLogFont(*aFont)
{
}
@ -80,7 +82,7 @@ ScaledFontWin::CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLeng
nativeFont.mFont = (void*)aData;
RefPtr<ScaledFont> font =
Factory::CreateScaledFontForNativeFont(nativeFont, aSize);
Factory::CreateScaledFontForNativeFont(nativeFont, nullptr, aSize);
#ifdef USE_CAIRO_SCALED_FONT
static_cast<ScaledFontBase*>(font.get())->PopulateCairoScaledFont();

View File

@ -16,7 +16,9 @@ class ScaledFontWin : public ScaledFontBase
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
ScaledFontWin(const LOGFONT* aFont, Float aSize);
ScaledFontWin(const LOGFONT* aFont,
const RefPtr<UnscaledFont>& aUnscaledFont,
Float aSize);
virtual FontType GetType() const { return FontType::GDI; }

View File

@ -150,7 +150,8 @@ enum class FontType : int8_t {
SKIA,
CAIRO,
COREGRAPHICS,
FONTCONFIG
FONTCONFIG,
FREETYPE
};
enum class NativeSurfaceType : int8_t {

View File

@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_UNSCALEDFONTDWRITE_H_
#define MOZILLA_GFX_UNSCALEDFONTDWRITE_H_
#include <dwrite.h>
#include "2D.h"
namespace mozilla {
namespace gfx {
class UnscaledFontDWrite final : public UnscaledFont
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontDWrite, override)
explicit UnscaledFontDWrite(const RefPtr<IDWriteFontFace>& aFontFace,
DWRITE_FONT_SIMULATIONS aSimulations =
DWRITE_FONT_SIMULATIONS_NONE)
: mFontFace(aFontFace),
mSimulations(aSimulations)
{}
FontType GetType() const override { return FontType::DWRITE; }
const RefPtr<IDWriteFontFace> GetFontFace() const { return mFontFace; }
DWRITE_FONT_SIMULATIONS GetSimulations() const { return mSimulations; }
private:
RefPtr<IDWriteFontFace> mFontFace;
DWRITE_FONT_SIMULATIONS mSimulations;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_UNSCALEDFONTDWRITE_H_ */

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_
#define MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_
#include <cairo-ft.h>
#include "2D.h"
namespace mozilla {
namespace gfx {
class UnscaledFontFreeType : public UnscaledFont
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontFreeType, override)
explicit UnscaledFontFreeType(FT_Face aFace)
: mFace(aFace)
, mIndex(0)
{}
explicit UnscaledFontFreeType(const char* aFile,
uint32_t aIndex = 0)
: mFace(nullptr)
, mFile(aFile)
, mIndex(aIndex)
{}
FontType GetType() const override { return FontType::FREETYPE; }
FT_Face GetFace() const { return mFace; }
const char* GetFile() const { return mFile.c_str(); }
uint32_t GetIndex() const { return mIndex; }
private:
FT_Face mFace;
std::string mFile;
uint32_t mIndex;
};
#ifdef MOZ_WIDGET_GTK
class UnscaledFontFontconfig : public UnscaledFontFreeType
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontFontconfig, override)
explicit UnscaledFontFontconfig(FT_Face aFace)
: UnscaledFontFreeType(aFace)
{}
explicit UnscaledFontFontconfig(const char* aFile,
uint32_t aIndex = 0)
: UnscaledFontFreeType(aFile, aIndex)
{}
FontType GetType() const override { return FontType::FONTCONFIG; }
};
#endif
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_ */

35
gfx/2d/UnscaledFontGDI.h Normal file
View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_UNSCALEDFONTGDI_H_
#define MOZILLA_GFX_UNSCALEDFONTGDI_H_
#include "2D.h"
#include <windows.h>
namespace mozilla {
namespace gfx {
class UnscaledFontGDI final : public UnscaledFont
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontGDI, override)
explicit UnscaledFontGDI(const LOGFONT& aLogFont)
: mLogFont(aLogFont)
{}
FontType GetType() const override { return FontType::GDI; }
const LOGFONT& GetLogFont() const { return mLogFont; }
private:
LOGFONT mLogFont;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_UNSCALEDFONTGDI_H_ */

47
gfx/2d/UnscaledFontMac.h Normal file
View File

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_GFX_UNSCALEDFONTMAC_H_
#define MOZILLA_GFX_UNSCALEDFONTMAC_H_
#ifdef MOZ_WIDGET_COCOA
#include <ApplicationServices/ApplicationServices.h>
#else
#include <CoreGraphics/CoreGraphics.h>
#include <CoreText/CoreText.h>
#endif
#include "2D.h"
namespace mozilla {
namespace gfx {
class UnscaledFontMac final : public UnscaledFont
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontMac, override)
explicit UnscaledFontMac(CGFontRef aFont)
: mFont(aFont)
{
CFRetain(mFont);
}
~UnscaledFontMac()
{
CFRelease(mFont);
}
FontType GetType() const override { return FontType::MAC; }
CGFontRef GetFont() const { return mFont; }
private:
CGFontRef mFont;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_UNSCALEDFONTMAC_H_ */

View File

@ -65,12 +65,17 @@ EXPORTS.mozilla.gfx += ['ssse3-scaler.h']
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
EXPORTS.mozilla.gfx += [
'MacIOSurface.h',
'UnscaledFontMac.h',
]
UNIFIED_SOURCES += [
'NativeFontResourceMac.cpp',
'ScaledFontMac.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
EXPORTS.mozilla.gfx += [
'UnscaledFontDWrite.h',
'UnscaledFontGDI.h',
]
SOURCES += [
'DrawTargetD2D1.cpp',
'ExtendInputEffectD2D1.cpp',
@ -91,6 +96,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
'JobScheduler_posix.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3'):
EXPORTS.mozilla.gfx += [
'UnscaledFontFreeType.h',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
SOURCES += [
'NativeFontResourceFontconfig.cpp',

View File

@ -1368,14 +1368,6 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
MOZ_ASSERT(mFrameMetrics.IsRootContent());
MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
float prevSpan = aEvent.mPreviousSpan;
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
// We're still handling it; we've just decided to throw this event away.
return nsEventStatus_eConsumeNoDefault;
}
float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
{
ReentrantMonitorAutoEnter lock(mMonitor);
@ -1384,12 +1376,29 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom();
ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
mLastZoomFocus = focusPoint;
// If displacing by the change in focus point will take us off page bounds,
// then reduce the displacement such that it doesn't.
focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
ScrollBy(focusChange / userZoom);
// If the span is zero or close to it, we don't want to process this zoom
// change because we're going to get wonky numbers for the spanRatio. So
// let's bail out here. Note that we do this after the focus-change-scroll
// above, so that if we have a pinch with zero span but changing focus,
// such as generated by some Synaptics touchpads on Windows, we still
// scroll properly.
float prevSpan = aEvent.mPreviousSpan;
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
// We might have done a nonzero ScrollBy above, so update metrics and
// repaint/recomposite
ScheduleCompositeAndMaybeRepaint();
UpdateSharedCompositorFrameMetrics();
return nsEventStatus_eConsumeNoDefault;
}
float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
// When we zoom in with focus, we can zoom too much towards the boundaries
// that we actually go over them. These are the needed displacements along
// either axis such that we don't overscroll the boundaries when zooming.
@ -1448,8 +1457,6 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
// We did a ScrollBy call above even if we didn't do a scale, so we
// should composite for that.
ScheduleComposite();
mLastZoomFocus = focusPoint;
}
return nsEventStatus_eConsumeNoDefault;

View File

@ -237,3 +237,58 @@ TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
apzc->AssertStateIsReset();
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) {
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
// When APZAllowZooming is false, the ZoomConstraintsClient produces
// ZoomConstraints with mAllowZoom set to false.
MakeApzcUnzoomable();
// With APZAllowZooming false, we expect the NotifyPinchGesture function to
// get called as the pinch progresses, but the metrics shouldn't change.
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
int inputId = 0;
ScreenIntPoint focus(250, 300);
// Do a pinch holding a zero span and moving the focus by y=100
MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
apzc->ReceiveInputEvent(mtiStart, nullptr);
focus.y -= 35 + 1; // this is to get over the PINCH_START_THRESHOLD in GestureEventListener.cpp
MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
apzc->ReceiveInputEvent(mtiMove1, nullptr);
focus.y -= 100; // do a two-finger scroll of 100 screen pixels
MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
apzc->ReceiveInputEvent(mtiMove2, nullptr);
MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
apzc->ReceiveInputEvent(mtiEnd, nullptr);
// Done, check the metrics to make sure we scrolled by 100 screen pixels,
// which is 50 CSS pixels for the pinchable frame metrics.
FrameMetrics fm = apzc->GetFrameMetrics();
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
EXPECT_EQ(originalMetrics.GetScrollOffset().y + 50, fm.GetScrollOffset().y);
apzc->AssertStateIsReset();
}

View File

@ -180,8 +180,10 @@ TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& a
if (!mPooledClients.empty()) {
textureHolder = mPooledClients.top();
mPooledClients.pop();
// If a pooled TextureClient is not compatible, release it.
if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
// If the texture's allocator is not open or a pooled TextureClient is
// not compatible, release it.
if (!textureHolder->GetTextureClient()->GetAllocator()->IPCOpen() ||
!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
// Release TextureClient.
RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
textureHolder->ClearTextureClient();

View File

@ -46,6 +46,7 @@ parent:
SurfaceFormat aFormat, ByteBuffer aBytes);
sync DeleteImage(ImageKey aImageKey);
async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex);
async DeleteFont(FontKey aFontKey);
async DPBegin(IntSize aSize);
async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc, ByteBuffer aAux, WrAuxiliaryListsDescriptor aAuxDesc);

View File

@ -24,6 +24,7 @@ WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId)
, mPipelineId(aPipelineId)
, mIPCOpen(false)
, mDestroyed(false)
, mFontKeysDeleted(0)
{
}
@ -151,6 +152,112 @@ WebRenderBridgeChild::DeallocExternalImageId(uint64_t aImageId)
SendRemoveExternalImageId(aImageId);
}
struct FontFileData
{
wr::ByteBuffer mFontBuffer;
uint32_t mFontIndex;
float mGlyphSize;
};
static void
WriteFontFileData(const uint8_t* aData, uint32_t aLength, uint32_t aIndex,
float aGlyphSize, uint32_t aVariationCount,
const ScaledFont::VariationSetting* aVariations, void* aBaton)
{
FontFileData* data = static_cast<FontFileData*>(aBaton);
if (!data->mFontBuffer.Allocate(aLength)) {
return;
}
memcpy(data->mFontBuffer.mData, aData, aLength);
data->mFontIndex = aIndex;
data->mGlyphSize = aGlyphSize;
}
void
WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
gfx::ScaledFont* aFont, const gfx::Point& aOffset, const gfx::Rect& aBounds,
const gfx::Rect& aClip)
{
MOZ_ASSERT(aFont);
MOZ_ASSERT(!aGlyphs.IsEmpty());
WrFontKey key = GetFontKeyForScaledFont(aFont);
MOZ_ASSERT(key.mNamespace && key.mHandle);
WrClipRegion clipRegion = aBuilder.BuildClipRegion(wr::ToWrRect(aClip));
for (size_t i = 0; i < aGlyphs.Length(); i++) {
GlyphArray glyph_array = aGlyphs[i];
nsTArray<gfx::Glyph>& glyphs = glyph_array.glyphs();
nsTArray<WrGlyphInstance> wr_glyph_instances;
wr_glyph_instances.SetLength(glyphs.Length());
for (size_t j = 0; j < glyphs.Length(); j++) {
wr_glyph_instances[j].index = glyphs[j].mIndex;
wr_glyph_instances[j].x = glyphs[j].mPosition.x - aOffset.x;
wr_glyph_instances[j].y = glyphs[j].mPosition.y - aOffset.y;
}
aBuilder.PushText(wr::ToWrRect(aBounds),
clipRegion,
glyph_array.color().value(),
key,
Range<const WrGlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()),
aFont->GetSize());
}
}
wr::FontKey
WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
{
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(aScaledFont);
MOZ_ASSERT((aScaledFont->GetType() == gfx::FontType::DWRITE) ||
(aScaledFont->GetType() == gfx::FontType::MAC) ||
(aScaledFont->GetType() == gfx::FontType::FONTCONFIG));
RefPtr<UnscaledFont> unscaled = aScaledFont->GetUnscaledFont();
MOZ_ASSERT(unscaled);
wr::FontKey key = {0, 0};
if (mFontKeys.Get(unscaled, &key)) {
return key;
}
FontFileData data;
if (!aScaledFont->GetFontFileData(WriteFontFileData, &data) ||
!data.mFontBuffer.mData) {
return key;
}
key.mNamespace = GetNamespace();
key.mHandle = GetNextResourceId();
SendAddRawFont(key, data.mFontBuffer, data.mFontIndex);
mFontKeys.Put(unscaled, key);
return key;
}
void
WebRenderBridgeChild::RemoveExpiredFontKeys()
{
uint32_t counter = UnscaledFont::DeletionCounter();
if (mFontKeysDeleted != counter) {
mFontKeysDeleted = counter;
for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) {
if (!iter.Key()) {
SendDeleteFont(iter.Data());
iter.Remove();
}
}
}
}
CompositorBridgeChild*
WebRenderBridgeChild::GetCompositorBridgeChild()
{

View File

@ -26,6 +26,28 @@ class CompositableClient;
class CompositorBridgeChild;
class TextureForwarder;
class UnscaledFontHashKey : public PLDHashEntryHdr
{
public:
typedef gfx::UnscaledFont* KeyType;
typedef const gfx::UnscaledFont* KeyTypePointer;
explicit UnscaledFontHashKey(KeyTypePointer aKey) : mKey(const_cast<KeyType>(aKey)) {}
KeyType GetKey() const { return mKey; }
bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{
return NS_PTR_TO_UINT32(aKey) >> 2;
}
enum { ALLOW_MEMMOVE = true };
private:
WeakPtr<gfx::UnscaledFont> mKey;
};
class WebRenderBridgeChild final : public PWebRenderBridgeChild
, public CompositableForwarder
{
@ -67,6 +89,14 @@ public:
mIdNamespace = aIdNamespace;
}
void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<GlyphArray>& aGlyphs,
gfx::ScaledFont* aFont, const gfx::Point& aOffset, const gfx::Rect& aBounds,
const gfx::Rect& aClip);
wr::FontKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
void RemoveExpiredFontKeys();
private:
friend class CompositorBridgeChild;
@ -123,6 +153,9 @@ private:
bool mIPCOpen;
bool mDestroyed;
uint32_t mFontKeysDeleted;
nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mFontKeys;
};
} // namespace layers

View File

@ -226,7 +226,16 @@ WebRenderBridgeParent::RecvAddRawFont(const wr::FontKey& aFontKey,
return IPC_OK();
}
mozilla::ipc::IPCResult
WebRenderBridgeParent::RecvDeleteFont(const wr::FontKey& aFontKey)
{
if (mDestroyed) {
return IPC_OK();
}
MOZ_ASSERT(mApi);
mApi->DeleteFont(aFontKey);
return IPC_OK();
}
mozilla::ipc::IPCResult
WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey,

View File

@ -73,14 +73,15 @@ public:
const uint32_t& aStride,
const gfx::SurfaceFormat& aFormat,
const ByteBuffer& aBuffer) override;
mozilla::ipc::IPCResult RecvAddRawFont(const wr::FontKey& aFontKey,
const ByteBuffer& aBuffer,
const uint32_t& aFontIndex) override;
mozilla::ipc::IPCResult RecvUpdateImage(const wr::ImageKey& aImageKey,
const gfx::IntSize& aSize,
const gfx::SurfaceFormat& aFormat,
const ByteBuffer& aBuffer) override;
mozilla::ipc::IPCResult RecvDeleteImage(const wr::ImageKey& a1) override;
mozilla::ipc::IPCResult RecvAddRawFont(const wr::FontKey& aFontKey,
const ByteBuffer& aBuffer,
const uint32_t& aFontIndex) override;
mozilla::ipc::IPCResult RecvDeleteFont(const wr::FontKey& aFontKey) override;
mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize) override;
mozilla::ipc::IPCResult RecvDPEnd(const gfx::IntSize& aSize,
InfallibleTArray<WebRenderParentCommand>&& aCommands,

View File

@ -288,6 +288,7 @@ WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
EndTransactionFlags aFlags)
{
DiscardImages();
WrBridge()->RemoveExpiredFontKeys();
mPaintedLayerCallback = aCallback;
mPaintedLayerCallbackData = aCallbackData;

View File

@ -39,8 +39,7 @@ WebRenderTextLayer::RenderLayer(wr::DisplayListBuilder& aBuilder)
Stringify(clip).c_str());
}
mGlyphHelper.BuildWebRenderCommands(WrBridge(), aBuilder, mGlyphs, mFont,
GetOffsetToParent(), rect, clip);
WrBridge()->PushGlyphs(aBuilder, mGlyphs, mFont, GetOffsetToParent(), rect, clip);
}
} // namespace layers

View File

@ -33,8 +33,6 @@ public:
Layer* GetLayer() override { return this; }
void RenderLayer(wr::DisplayListBuilder& aBuilder) override;
protected:
gfx::WebRenderGlyphHelper mGlyphHelper;
};
} // namespace layers

View File

@ -6,7 +6,6 @@
#ifndef GFX_PLATFORM_ANDROID_H
#define GFX_PLATFORM_ANDROID_H
#include "gfxFT2Fonts.h"
#include "gfxPlatform.h"
#include "gfxUserFontSet.h"
#include "nsCOMPtr.h"

View File

@ -27,6 +27,7 @@
#include "harfbuzz/hb.h"
using namespace mozilla;
using namespace mozilla::gfx;
using mozilla::intl::OSPreferences;
#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
@ -579,7 +580,26 @@ gfxFont *
gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
bool aNeedsBold)
{
return new gfxDWriteFont(this, aFontStyle, aNeedsBold);
WeakPtr<UnscaledFont>& unscaledFontPtr =
aNeedsBold ? mUnscaledFontBold : mUnscaledFont;
RefPtr<UnscaledFontDWrite> unscaledFont =
static_cast<UnscaledFontDWrite*>(unscaledFontPtr.get());
if (!unscaledFont) {
DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
if (aNeedsBold) {
sims |= DWRITE_FONT_SIMULATIONS_BOLD;
}
RefPtr<IDWriteFontFace> fontFace;
nsresult rv = CreateFontFace(getter_AddRefs(fontFace), sims);
if (NS_FAILED(rv)) {
return nullptr;
}
unscaledFont = new UnscaledFontDWrite(fontFace, sims);
unscaledFontPtr = unscaledFont;
}
return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
}
nsresult

View File

@ -17,6 +17,8 @@
#include "gfxPlatform.h"
#include <algorithm>
#include "mozilla/gfx/UnscaledFontDWrite.h"
/**
* gfxDWriteFontFamily is a class that describes one of the fonts on the
@ -201,6 +203,9 @@ protected:
int8_t mIsCJK;
bool mForceGDIClassic;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFontBold;
};
// custom text renderer used to determine the fallback font for a given char

View File

@ -71,11 +71,12 @@ UsingClearType()
////////////////////////////////////////////////////////////////////////////////
// gfxDWriteFont
gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold,
AntialiasOption anAAOption)
: gfxFont(aFontEntry, aFontStyle, anAAOption)
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption)
, mCairoFontFace(nullptr)
, mMetrics(nullptr)
, mSpaceGlyph(0)
@ -84,27 +85,15 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
, mUseSubpixelPositions(false)
, mAllowManualShowGlyphs(true)
{
gfxDWriteFontEntry *fe =
static_cast<gfxDWriteFontEntry*>(aFontEntry);
nsresult rv;
DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
fe->IsUpright() &&
aFontEntry->IsUpright() &&
GetStyle()->allowSyntheticStyle) {
// For this we always use the font_matrix for uniformity. Not the
// DWrite simulation.
mNeedsOblique = true;
}
if (aNeedsBold) {
sims |= DWRITE_FONT_SIMULATIONS_BOLD;
}
rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
if (NS_FAILED(rv)) {
mIsValid = false;
return;
}
mFontFace = aUnscaledFont->GetFontFace();
ComputeMetrics(anAAOption);
}
@ -131,7 +120,8 @@ UniquePtr<gfxFont>
gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
{
auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
return MakeUnique<gfxDWriteFont>(entry, &mStyle, mNeedsBold, anAAOption);
RefPtr<UnscaledFontDWrite> unscaledFont = static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
return MakeUnique<gfxDWriteFont>(unscaledFont, entry, &mStyle, mNeedsBold, anAAOption);
}
const gfxFont::Metrics&
@ -694,6 +684,7 @@ gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
if (wantCairo) {
mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
GetUnscaledFont(),
GetAdjustedSize(),
GetCairoScaledFont());
} else if (aTarget->GetBackendType() == BackendType::SKIA) {
@ -704,11 +695,13 @@ gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
const gfxFontStyle* fontStyle = GetStyle();
mAzureScaledFont =
Factory::CreateScaledFontForDWriteFont(mFontFace, fontStyle,
GetUnscaledFont(),
GetAdjustedSize(),
useEmbeddedBitmap,
GetForceGDIClassic());
} else {
mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
GetUnscaledFont(),
GetAdjustedSize());
}

View File

@ -17,13 +17,16 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "mozilla/gfx/UnscaledFontDWrite.h"
/**
* \brief Class representing a font face for a font entry.
*/
class gfxDWriteFont : public gfxFont
{
public:
gfxDWriteFont(gfxFontEntry *aFontEntry,
gfxDWriteFont(const RefPtr<mozilla::gfx::UnscaledFontDWrite>& aUnscaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold = false,
AntialiasOption = kAntialiasDefault);

View File

@ -12,10 +12,11 @@
using namespace mozilla::gfx;
gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
gfxFT2FontBase::gfxFT2FontBase(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle)
: gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
mSpaceGlyph(0),
mHasMetrics(false)
{

View File

@ -10,10 +10,12 @@
#include "gfxContext.h"
#include "gfxFont.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/UnscaledFontFreeType.h"
class gfxFT2FontBase : public gfxFont {
public:
gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
gfxFT2FontBase(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle);
virtual ~gfxFT2FontBase();

View File

@ -50,6 +50,7 @@
#include <sys/stat.h>
using namespace mozilla;
using namespace mozilla::gfx;
static LazyLogModule sFontInfoLog("fontInfoLog");
@ -237,7 +238,20 @@ FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold
if (!scaledFont) {
return nullptr;
}
gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
RefPtr<UnscaledFontFreeType> unscaledFont =
static_cast<UnscaledFontFreeType*>(mUnscaledFont.get());
if (!unscaledFont) {
unscaledFont =
mFilename.IsEmpty() ?
new UnscaledFontFreeType(mFTFace) :
new UnscaledFontFreeType(mFilename.BeginReading(),
mFTFontIndex);
mUnscaledFont = unscaledFont;
}
gfxFont *font = new gfxFT2Font(unscaledFont, scaledFont, this,
aFontStyle, aNeedsBold);
cairo_scaled_font_destroy(scaledFont);
return font;
}

View File

@ -8,6 +8,7 @@
#include "mozilla/MemoryReporting.h"
#include "gfxPlatformFontList.h"
#include "mozilla/gfx/UnscaledFontFreeType.h"
namespace mozilla {
namespace dom {
@ -94,6 +95,8 @@ public:
nsCString mFilename;
uint8_t mFTFontIndex;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
};
class FT2FontFamily : public gfxFontFamily

View File

@ -156,11 +156,12 @@ gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
}
}
gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
gfxFT2Font::gfxFT2Font(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
cairo_scaled_font_t *aCairoFont,
FT2FontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold)
: gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle)
: gfxFT2FontBase(aUnscaledFont, aCairoFont, aFontEntry, aFontStyle)
, mCharGlyphCache(32)
{
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");

View File

@ -19,7 +19,8 @@ class FT2FontEntry;
class gfxFT2Font : public gfxFT2FontBase {
public: // new functions
gfxFT2Font(cairo_scaled_font_t *aCairoFont,
gfxFT2Font(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
cairo_scaled_font_t *aCairoFont,
FT2FontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold);

View File

@ -39,6 +39,7 @@
#endif
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::unicode;
#ifndef FC_POSTSCRIPT_NAME
@ -796,6 +797,33 @@ PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
FcDefaultSubstitute(aPattern);
}
void
gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
if (aIndex > 0) {
WeakPtr<UnscaledFont> front =
Move(mUnscaledFonts[aIndex]);
for (size_t i = aIndex; i > 0; i--) {
mUnscaledFonts[i] = Move(mUnscaledFonts[i-1]);
}
mUnscaledFonts[0] = Move(front);
}
}
already_AddRefed<UnscaledFontFontconfig>
gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const char* aFile, uint32_t aIndex) {
for (size_t i = 0; i < kNumEntries; i++) {
UnscaledFontFontconfig* entry =
static_cast<UnscaledFontFontconfig*>(mUnscaledFonts[i].get());
if (entry &&
!strcmp(entry->GetFile(), aFile) &&
entry->GetIndex() == aIndex) {
MoveToFront(i);
return do_AddRef(entry);
}
}
return nullptr;
}
static inline gfxFloat
SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
{
@ -848,8 +876,31 @@ gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
cairo_scaled_font_t* scaledFont =
CreateScaledFont(renderPattern, size, aFontStyle, aNeedsBold);
const FcChar8* file = ToFcChar8Ptr("");
int index = 0;
if (!mFontData) {
if (FcPatternGetString(renderPattern, FC_FILE, 0,
const_cast<FcChar8**>(&file)) != FcResultMatch ||
FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) != FcResultMatch) {
NS_WARNING("No file in Fontconfig pattern for font instance");
return nullptr;
}
}
RefPtr<UnscaledFontFontconfig> unscaledFont =
mUnscaledFontCache.Lookup(ToCharPtr(file), index);
if (!unscaledFont) {
unscaledFont =
mFontData ?
new UnscaledFontFontconfig(mFTFace) :
new UnscaledFontFontconfig(ToCharPtr(file), index);
mUnscaledFontCache.Add(unscaledFont);
}
gfxFont* newFont =
new gfxFontconfigFont(scaledFont, renderPattern, size,
new gfxFontconfigFont(unscaledFont, scaledFont,
renderPattern, size,
this, aFontStyle, aNeedsBold);
cairo_scaled_font_destroy(scaledFont);
@ -1067,13 +1118,14 @@ gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
}
}
gfxFontconfigFont::gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
FcPattern *aPattern,
gfxFloat aAdjustedSize,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold) :
gfxFontconfigFontBase(aScaledFont, aPattern, aFontEntry, aFontStyle)
gfxFontconfigFontBase(aUnscaledFont, aScaledFont, aPattern, aFontEntry, aFontStyle)
{
mAdjustedSize = aAdjustedSize;
}

View File

@ -168,6 +168,26 @@ protected:
// data font
const uint8_t* mFontData;
class UnscaledFontCache
{
public:
already_AddRefed<mozilla::gfx::UnscaledFontFontconfig>
Lookup(const char* aFile, uint32_t aIndex);
void Add(const RefPtr<mozilla::gfx::UnscaledFontFontconfig>& aUnscaledFont) {
mUnscaledFonts[kNumEntries-1] = aUnscaledFont;
MoveToFront(kNumEntries-1);
}
private:
void MoveToFront(size_t aIndex);
static const size_t kNumEntries = 3;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFonts[kNumEntries];
};
UnscaledFontCache mUnscaledFontCache;
};
class gfxFontconfigFontFamily : public gfxFontFamily {
@ -205,7 +225,8 @@ protected:
class gfxFontconfigFont : public gfxFontconfigFontBase {
public:
gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
gfxFontconfigFont(const RefPtr<mozilla::gfx::UnscaledFontFontconfig> &aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
FcPattern *aPattern,
gfxFloat aAdjustedSize,
gfxFontEntry *aFontEntry,

View File

@ -835,7 +835,8 @@ gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
mAdvanceWidth += aOther.mAdvanceWidth;
}
gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
mScaledFont(aScaledFont),
mFontEntry(aFontEntry), mIsValid(true),
@ -844,7 +845,8 @@ gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
mStyle(*aFontStyle),
mAdjustedSize(0.0),
mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
mAntialiasOption(anAAOption)
mAntialiasOption(anAAOption),
mUnscaledFont(aUnscaledFont)
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gFontCount;

View File

@ -1385,7 +1385,8 @@ protected:
}
}
gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
gfxFont(const RefPtr<mozilla::gfx::UnscaledFont>& aUnscaledFont,
gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
AntialiasOption anAAOption = kAntialiasDefault,
cairo_scaled_font_t *aScaledFont = nullptr);
@ -1801,8 +1802,14 @@ public:
virtual FontType GetType() const = 0;
const RefPtr<mozilla::gfx::UnscaledFont>& GetUnscaledFont() const {
return mUnscaledFont;
}
virtual already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(DrawTarget* aTarget)
{ return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
{
return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this);
}
bool KerningDisabled() {
return mKerningSet && !mKerningEnabled;
@ -2120,6 +2127,7 @@ protected:
// ranges supported by font
RefPtr<gfxCharacterMap> mUnicodeRangeMap;
RefPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont;
// For vertical metrics, created on demand.

View File

@ -20,6 +20,7 @@
#include "harfbuzz/hb.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
typedef struct gr_face gr_face;

View File

@ -38,11 +38,12 @@ public:
class gfxFontconfigFontBase : public gfxFT2FontBase {
public:
gfxFontconfigFontBase(cairo_scaled_font_t *aScaledFont,
gfxFontconfigFontBase(const RefPtr<mozilla::gfx::UnscaledFontFontconfig>& aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
FcPattern *aPattern,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle)
: gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle)
: gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
, mPattern(aPattern) { }
virtual FontType GetType() const override { return FONT_TYPE_FONTCONFIG; }

View File

@ -44,7 +44,7 @@ gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold,
AntialiasOption anAAOption)
: gfxFont(aFontEntry, aFontStyle, anAAOption),
: gfxFont(nullptr, aFontEntry, aFontStyle, anAAOption),
mFont(nullptr),
mFontFace(nullptr),
mMetrics(nullptr),
@ -53,6 +53,10 @@ gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
mScriptCache(nullptr)
{
Initialize();
if (mFont) {
mUnscaledFont = aFontEntry->LookupUnscaledFont(mFont);
}
}
gfxGDIFont::~gfxGDIFont()
@ -224,6 +228,12 @@ gfxGDIFont::Initialize()
mMetrics = new gfxFont::Metrics;
::memset(mMetrics, 0, sizeof(*mMetrics));
if (!mFont) {
NS_WARNING("Failed creating GDI font");
mIsValid = false;
return;
}
AutoDC dc;
SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
AutoSelectFont selectFont(dc.GetDC(), mFont);

View File

@ -33,6 +33,7 @@
#include <usp10.h>
using namespace mozilla;
using namespace mozilla::gfx;
#define ROUND(x) floor((x) + 0.5)
@ -246,6 +247,21 @@ GDIFontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
return NS_ERROR_FAILURE;
}
already_AddRefed<UnscaledFontGDI>
GDIFontEntry::LookupUnscaledFont(HFONT aFont)
{
RefPtr<UnscaledFontGDI> unscaledFont =
static_cast<UnscaledFontGDI*>(mUnscaledFont.get());
if (!unscaledFont) {
LOGFONT lf;
GetObject(aFont, sizeof(LOGFONT), &lf);
unscaledFont = new UnscaledFontGDI(lf);
mUnscaledFont = unscaledFont;
}
return unscaledFont.forget();
}
void
GDIFontEntry::FillLogFont(LOGFONTW *aLogFont,
uint16_t aWeight, gfxFloat aSize)

View File

@ -10,6 +10,7 @@
#include "gfxWindowsPlatform.h"
#include "gfxPlatformFontList.h"
#include "nsGkAtoms.h"
#include "mozilla/gfx/UnscaledFontGDI.h"
#include <windows.h>
@ -262,7 +263,7 @@ public:
gfxSparseBitSet mUnicodeRanges;
protected:
friend class gfxWindowsFont;
friend class gfxGDIFont;
GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
@ -275,7 +276,11 @@ protected:
virtual nsresult CopyFontTable(uint32_t aTableTag,
nsTArray<uint8_t>& aBuffer) override;
already_AddRefed<mozilla::gfx::UnscaledFontGDI> LookupUnscaledFont(HFONT aFont);
LOGFONTW mLogFont;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
};
// a single font family, referencing one or more faces

View File

@ -159,9 +159,11 @@ CreateVariationDictionaryOrNull(CGFontRef aCGFont,
return dict.forget();
}
gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
gfxMacFont::gfxMacFont(const RefPtr<UnscaledFontMac>& aUnscaledFont,
MacOSFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold)
: gfxFont(aFontEntry, aFontStyle),
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle),
mCGFont(nullptr),
mCTFont(nullptr),
mFontFace(nullptr),
@ -170,7 +172,7 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl
mApplySyntheticBold = aNeedsBold;
if (mVariationFont && aFontStyle->variationSettings.Length() > 0) {
CGFontRef baseFont = aFontEntry->GetFontRef();
CGFontRef baseFont = aUnscaledFont->GetFont();
if (!baseFont) {
mIsValid = false;
return;
@ -185,7 +187,7 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl
mCGFont = baseFont;
}
} else {
mCGFont = aFontEntry->GetFontRef();
mCGFont = aUnscaledFont->GetFont();
if (!mCGFont) {
mIsValid = false;
return;
@ -636,18 +638,22 @@ gfxMacFont::GetScaledFont(DrawTarget *aTarget)
NativeFont nativeFont;
nativeFont.mType = NativeFontType::MAC_FONT_FACE;
nativeFont.mFont = GetCGFontRef();
mAzureScaledFont = mozilla::gfx::Factory::CreateScaledFontWithCairo(nativeFont, GetAdjustedSize(), mScaledFont);
mAzureScaledFont =
Factory::CreateScaledFontWithCairo(nativeFont,
GetUnscaledFont(),
GetAdjustedSize(),
mScaledFont);
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}
already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
already_AddRefed<GlyphRenderingOptions>
gfxMacFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
{
if (aRunParams) {
return mozilla::gfx::Factory::CreateCGGlyphRenderingOptions(aRunParams->fontSmoothingBGColor);
return Factory::CreateCGGlyphRenderingOptions(aRunParams->fontSmoothingBGColor);
}
return nullptr;
}

View File

@ -11,12 +11,15 @@
#include "cairo.h"
#include <ApplicationServices/ApplicationServices.h>
#include "mozilla/gfx/UnscaledFontMac.h"
class MacOSFontEntry;
class gfxMacFont : public gfxFont
{
public:
gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
gfxMacFont(const RefPtr<mozilla::gfx::UnscaledFontMac>& aUnscaledFont,
MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
bool aNeedsBold);
virtual ~gfxMacFont();

View File

@ -20,6 +20,8 @@
#include "nsTArray.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/gfx/UnscaledFontMac.h"
class gfxMacPlatformFontList;
// a single member of a font family (i.e. a single face, such as Times Italic)
@ -76,6 +78,8 @@ protected:
bool mHasVariations;
bool mHasVariationsInitialized;
nsTHashtable<nsUint32HashKey> mAvailableTables;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
};
class gfxMacPlatformFontList : public gfxPlatformFontList {

Some files were not shown because too many files have changed in this diff Show More