mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound
This commit is contained in:
commit
2ea70b91fa
@ -21,7 +21,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage",
|
||||
"@mozilla.org/notificationStorage;1",
|
||||
"nsINotificationStorage");
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
|
||||
return Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageSender);
|
||||
@ -162,13 +161,13 @@ AlertsService.prototype = {
|
||||
);
|
||||
}
|
||||
}
|
||||
if (topic === kTopicAlertFinished && listener.dbId) {
|
||||
notificationStorage.delete(listener.manifestURL, listener.dbId);
|
||||
}
|
||||
}
|
||||
|
||||
// we're done with this notification
|
||||
if (topic === kTopicAlertFinished) {
|
||||
if (listener.dbId) {
|
||||
notificationStorage.delete(listener.manifestURL, listener.dbId);
|
||||
}
|
||||
delete this._listeners[data.uid];
|
||||
}
|
||||
},
|
||||
|
@ -1407,7 +1407,6 @@ pref("devtools.tilt.outro_transition", true);
|
||||
// - enableAutocompletion: Whether to enable JavaScript autocompletion.
|
||||
pref("devtools.scratchpad.recentFilesMax", 10);
|
||||
pref("devtools.scratchpad.showTrailingSpace", false);
|
||||
pref("devtools.scratchpad.enableCodeFolding", true);
|
||||
pref("devtools.scratchpad.enableAutocompletion", true);
|
||||
|
||||
// Enable the Storage Inspector
|
||||
@ -1513,6 +1512,7 @@ pref("devtools.editor.expandtab", true);
|
||||
pref("devtools.editor.keymap", "default");
|
||||
pref("devtools.editor.autoclosebrackets", true);
|
||||
pref("devtools.editor.detectindentation", true);
|
||||
pref("devtools.editor.enableCodeFolding", true);
|
||||
pref("devtools.editor.autocomplete", true);
|
||||
|
||||
// Enable the Font Inspector
|
||||
@ -1636,11 +1636,6 @@ pref("dom.debug.propagate_gesture_events_through_content", false);
|
||||
|
||||
// The request URL of the GeoLocation backend.
|
||||
pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%");
|
||||
#ifdef RELEASE_BUILD
|
||||
pref("geo.wifi.logging.enabled", false);
|
||||
#else
|
||||
pref("geo.wifi.logging.enabled", true);
|
||||
#endif
|
||||
|
||||
// Necko IPC security checks only needed for app isolation for cookies/cache/etc:
|
||||
// currently irrelevant for desktop e10s
|
||||
|
@ -233,7 +233,8 @@ let DebuggerView = {
|
||||
showAnnotationRuler: true,
|
||||
gutters: gutters,
|
||||
extraKeys: extraKeys,
|
||||
contextMenu: "sourceEditorContextMenu"
|
||||
contextMenu: "sourceEditorContextMenu",
|
||||
enableCodeFolding: false
|
||||
});
|
||||
|
||||
this.editor.appendTo(document.getElementById("editor")).then(() => {
|
||||
|
@ -34,8 +34,8 @@ const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties"
|
||||
const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
|
||||
const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
|
||||
const SHOW_TRAILING_SPACE = "devtools.scratchpad.showTrailingSpace";
|
||||
const ENABLE_CODE_FOLDING = "devtools.scratchpad.enableCodeFolding";
|
||||
const ENABLE_AUTOCOMPLETION = "devtools.scratchpad.enableAutocompletion";
|
||||
const TAB_SIZE = "devtools.editor.tabsize";
|
||||
|
||||
const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesView.xul";
|
||||
|
||||
@ -655,7 +655,7 @@ var Scratchpad = {
|
||||
*/
|
||||
prettyPrint: function SP_prettyPrint() {
|
||||
const uglyText = this.getText();
|
||||
const tabsize = Services.prefs.getIntPref("devtools.editor.tabsize");
|
||||
const tabsize = Services.prefs.getIntPref(TAB_SIZE);
|
||||
const id = Math.random();
|
||||
const deferred = promise.defer();
|
||||
|
||||
@ -1604,7 +1604,6 @@ var Scratchpad = {
|
||||
lineNumbers: true,
|
||||
contextMenu: "scratchpad-text-popup",
|
||||
showTrailingSpace: Services.prefs.getBoolPref(SHOW_TRAILING_SPACE),
|
||||
enableCodeFolding: Services.prefs.getBoolPref(ENABLE_CODE_FOLDING),
|
||||
autocomplete: Services.prefs.getBoolPref(ENABLE_AUTOCOMPLETION),
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
const { Cu, Cc, Ci, components } = require("chrome");
|
||||
|
||||
const TAB_SIZE = "devtools.editor.tabsize";
|
||||
const ENABLE_CODE_FOLDING = "devtools.editor.enableCodeFolding";
|
||||
const EXPAND_TAB = "devtools.editor.expandtab";
|
||||
const KEYMAP = "devtools.editor.keymap";
|
||||
const AUTO_CLOSE = "devtools.editor.autoclosebrackets";
|
||||
@ -188,14 +189,12 @@ function Editor(config) {
|
||||
});
|
||||
});
|
||||
|
||||
// Set the code folding gutter, if needed.
|
||||
if (this.config.enableCodeFolding) {
|
||||
this.config.foldGutter = true;
|
||||
|
||||
if (!this.config.gutters) {
|
||||
this.config.gutters = this.config.lineNumbers ? ["CodeMirror-linenumbers"] : [];
|
||||
this.config.gutters.push("CodeMirror-foldgutter");
|
||||
}
|
||||
if (!this.config.gutters) {
|
||||
this.config.gutters = [];
|
||||
}
|
||||
if (this.config.lineNumbers
|
||||
&& this.config.gutters.indexOf("CodeMirror-linenumbers") === -1) {
|
||||
this.config.gutters.push("CodeMirror-linenumbers");
|
||||
}
|
||||
|
||||
// Remember the initial value of autoCloseBrackets.
|
||||
@ -333,6 +332,7 @@ Editor.prototype = {
|
||||
this._prefObserver.on(AUTO_CLOSE, this.reloadPreferences);
|
||||
this._prefObserver.on(AUTOCOMPLETE, this.reloadPreferences);
|
||||
this._prefObserver.on(DETECT_INDENT, this.reloadPreferences);
|
||||
this._prefObserver.on(ENABLE_CODE_FOLDING, this.reloadPreferences);
|
||||
|
||||
this.reloadPreferences();
|
||||
def.resolve();
|
||||
@ -430,6 +430,7 @@ Editor.prototype = {
|
||||
this.setOption("keyMap", keyMap)
|
||||
else
|
||||
this.setOption("keyMap", "default");
|
||||
this.updateCodeFoldingGutter();
|
||||
|
||||
this.resetIndentUnit();
|
||||
this.setupAutoCompletion();
|
||||
@ -955,6 +956,12 @@ Editor.prototype = {
|
||||
} else {
|
||||
cm.setOption(o, v);
|
||||
}
|
||||
|
||||
if (o === "enableCodeFolding") {
|
||||
// The new value maybe explicitly force foldGUtter on or off, ignoring
|
||||
// the prefs service.
|
||||
this.updateCodeFoldingGutter();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1036,10 +1043,46 @@ Editor.prototype = {
|
||||
this._prefObserver.off(AUTO_CLOSE, this.reloadPreferences);
|
||||
this._prefObserver.off(AUTOCOMPLETE, this.reloadPreferences);
|
||||
this._prefObserver.off(DETECT_INDENT, this.reloadPreferences);
|
||||
this._prefObserver.off(ENABLE_CODE_FOLDING, this.reloadPreferences);
|
||||
this._prefObserver.destroy();
|
||||
}
|
||||
|
||||
this.emit("destroy");
|
||||
},
|
||||
|
||||
updateCodeFoldingGutter: function () {
|
||||
let shouldFoldGutter = this.config.enableCodeFolding,
|
||||
foldGutterIndex = this.config.gutters.indexOf("CodeMirror-foldgutter"),
|
||||
cm = editors.get(this);
|
||||
|
||||
if (shouldFoldGutter === undefined) {
|
||||
shouldFoldGutter = Services.prefs.getBoolPref(ENABLE_CODE_FOLDING);
|
||||
}
|
||||
|
||||
if (shouldFoldGutter) {
|
||||
// Add the gutter before enabling foldGutter
|
||||
if (foldGutterIndex === -1) {
|
||||
let gutters = this.config.gutters.slice();
|
||||
gutters.push("CodeMirror-foldgutter");
|
||||
this.setOption("gutters", gutters);
|
||||
}
|
||||
|
||||
this.setOption("foldGutter", true);
|
||||
} else {
|
||||
// No code should remain folded when folding is off.
|
||||
if (cm) {
|
||||
cm.execCommand("unfoldAll");
|
||||
}
|
||||
|
||||
// Remove the gutter so it doesn't take up space
|
||||
if (foldGutterIndex !== -1) {
|
||||
let gutters = this.config.gutters.slice();
|
||||
gutters.splice(foldGutterIndex, 1);
|
||||
this.setOption("gutters", gutters);
|
||||
}
|
||||
|
||||
this.setOption("foldGutter", false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
// Test to make sure that the editor reacts to preference changes
|
||||
|
||||
const TAB_SIZE = "devtools.editor.tabsize";
|
||||
const ENABLE_CODE_FOLDING = "devtools.editor.enableCodeFolding";
|
||||
const EXPAND_TAB = "devtools.editor.expandtab";
|
||||
const KEYMAP = "devtools.editor.keymap";
|
||||
const AUTO_CLOSE = "devtools.editor.autoclosebrackets";
|
||||
@ -17,6 +18,11 @@ function test() {
|
||||
waitForExplicitFinish();
|
||||
setup((ed, win) => {
|
||||
|
||||
Assert.deepEqual(ed.getOption("gutters"), [
|
||||
"CodeMirror-linenumbers",
|
||||
"breakpoints",
|
||||
"CodeMirror-foldgutter"], "gutters is correct");
|
||||
|
||||
ed.setText("Checking preferences.");
|
||||
|
||||
info ("Turning prefs off");
|
||||
@ -24,14 +30,21 @@ function test() {
|
||||
ed.setOption("autocomplete", true);
|
||||
|
||||
Services.prefs.setIntPref(TAB_SIZE, 2);
|
||||
Services.prefs.setBoolPref(ENABLE_CODE_FOLDING, false);
|
||||
Services.prefs.setBoolPref(EXPAND_TAB, false);
|
||||
Services.prefs.setCharPref(KEYMAP, "default");
|
||||
Services.prefs.setBoolPref(AUTO_CLOSE, false);
|
||||
Services.prefs.setBoolPref(AUTOCOMPLETE, false);
|
||||
Services.prefs.setBoolPref(DETECT_INDENT, false);
|
||||
|
||||
Assert.deepEqual(ed.getOption("gutters"), [
|
||||
"CodeMirror-linenumbers",
|
||||
"breakpoints"], "gutters is correct");
|
||||
|
||||
is(ed.getOption("tabSize"), 2, "tabSize is correct");
|
||||
is(ed.getOption("indentUnit"), 2, "indentUnit is correct");
|
||||
is(ed.getOption("foldGutter"), false, "foldGutter is correct");
|
||||
is(ed.getOption("enableCodeFolding"), undefined, "enableCodeFolding is correct");
|
||||
is(ed.getOption("indentWithTabs"), true, "indentWithTabs is correct");
|
||||
is(ed.getOption("keyMap"), "default", "keyMap is correct");
|
||||
is(ed.getOption("autoCloseBrackets"), "", "autoCloseBrackets is correct");
|
||||
@ -41,19 +54,46 @@ function test() {
|
||||
info ("Turning prefs on");
|
||||
|
||||
Services.prefs.setIntPref(TAB_SIZE, 4);
|
||||
Services.prefs.setBoolPref(ENABLE_CODE_FOLDING, true);
|
||||
Services.prefs.setBoolPref(EXPAND_TAB, true);
|
||||
Services.prefs.setCharPref(KEYMAP, "sublime");
|
||||
Services.prefs.setBoolPref(AUTO_CLOSE, true);
|
||||
Services.prefs.setBoolPref(AUTOCOMPLETE, true);
|
||||
|
||||
Assert.deepEqual(ed.getOption("gutters"), [
|
||||
"CodeMirror-linenumbers",
|
||||
"breakpoints",
|
||||
"CodeMirror-foldgutter"], "gutters is correct");
|
||||
|
||||
is(ed.getOption("tabSize"), 4, "tabSize is correct");
|
||||
is(ed.getOption("indentUnit"), 4, "indentUnit is correct");
|
||||
is(ed.getOption("foldGutter"), true, "foldGutter is correct");
|
||||
is(ed.getOption("enableCodeFolding"), undefined, "enableCodeFolding is correct");
|
||||
is(ed.getOption("indentWithTabs"), false, "indentWithTabs is correct");
|
||||
is(ed.getOption("keyMap"), "sublime", "keyMap is correct");
|
||||
is(ed.getOption("autoCloseBrackets"), "()[]{}''\"\"", "autoCloseBrackets is correct");
|
||||
is(ed.getOption("autocomplete"), true, "autocomplete is correct");
|
||||
ok(ed.isAutocompletionEnabled(), "Autocompletion is enabled");
|
||||
|
||||
info ("Forcing foldGutter off using enableCodeFolding");
|
||||
ed.setOption("enableCodeFolding", false);
|
||||
|
||||
is(ed.getOption("foldGutter"), false, "foldGutter is correct");
|
||||
is(ed.getOption("enableCodeFolding"), false, "enableCodeFolding is correct");
|
||||
Assert.deepEqual(ed.getOption("gutters"), [
|
||||
"CodeMirror-linenumbers",
|
||||
"breakpoints"], "gutters is correct");
|
||||
|
||||
info ("Forcing foldGutter on using enableCodeFolding");
|
||||
ed.setOption("enableCodeFolding", true);
|
||||
|
||||
is(ed.getOption("foldGutter"), true, "foldGutter is correct");
|
||||
is(ed.getOption("enableCodeFolding"), true, "enableCodeFolding is correct");
|
||||
Assert.deepEqual(ed.getOption("gutters"), [
|
||||
"CodeMirror-linenumbers",
|
||||
"breakpoints",
|
||||
"CodeMirror-foldgutter"], "gutters is correct");
|
||||
|
||||
info ("Checking indentation detection");
|
||||
|
||||
Services.prefs.setBoolPref(DETECT_INDENT, true);
|
||||
|
@ -35,14 +35,14 @@ let test = asyncTest(function*() {
|
||||
let onHighlighted = editor.once("node-highlighted");
|
||||
|
||||
info("Simulate a mousemove event on the div selector");
|
||||
editor._onMouseMove({clientX: 40, clientY: 10});
|
||||
editor._onMouseMove({clientX: 56, clientY: 10});
|
||||
yield onHighlighted;
|
||||
|
||||
ok(editor.highlighter.isShown, "The highlighter is now shown");
|
||||
is(editor.highlighter.options.selector, "div", "The selector is correct");
|
||||
|
||||
info("Simulate a mousemove event elsewhere in the editor");
|
||||
editor._onMouseMove({clientX: 0, clientY: 0});
|
||||
editor._onMouseMove({clientX: 16, clientY: 0});
|
||||
|
||||
ok(!editor.highlighter.isShown, "The highlighter is now hidden");
|
||||
});
|
||||
|
@ -496,7 +496,7 @@ let UI = {
|
||||
|
||||
Task.spawn(function() {
|
||||
if (project.type == "runtimeApp") {
|
||||
yield UI.busyUntil(AppManager.runRuntimeApp(), "running app");
|
||||
yield UI.busyUntil(AppManager.launchRuntimeApp(), "running app");
|
||||
}
|
||||
yield UI.createToolbox();
|
||||
});
|
||||
@ -1014,7 +1014,7 @@ let Cmds = {
|
||||
case "hosted":
|
||||
return UI.busyUntil(AppManager.installAndRunProject(), "installing and running app");
|
||||
case "runtimeApp":
|
||||
return UI.busyUntil(AppManager.runRuntimeApp(), "running app");
|
||||
return UI.busyUntil(AppManager.launchOrReloadRuntimeApp(), "launching / reloading app");
|
||||
case "tab":
|
||||
return UI.busyUntil(AppManager.reloadTab(), "reloading tab");
|
||||
}
|
||||
|
@ -326,7 +326,9 @@ exports.AppManager = AppManager = {
|
||||
|
||||
_selectedProject: null,
|
||||
set selectedProject(value) {
|
||||
if (value != this.selectedProject) {
|
||||
// A regular comparison still sees a difference when equal in some cases
|
||||
if (JSON.stringify(this._selectedProject) !==
|
||||
JSON.stringify(value)) {
|
||||
this._selectedProject = value;
|
||||
|
||||
// Clear out tab store's selected state, if any
|
||||
@ -439,9 +441,19 @@ exports.AppManager = AppManager = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
runRuntimeApp: function() {
|
||||
launchRuntimeApp: function() {
|
||||
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
|
||||
return promise.reject("attempting to run a non-runtime app");
|
||||
return promise.reject("attempting to launch a non-runtime app");
|
||||
}
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return AppActorFront.launchApp(client, actor, manifest);
|
||||
},
|
||||
|
||||
launchOrReloadRuntimeApp: function() {
|
||||
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
|
||||
return promise.reject("attempting to launch / reload a non-runtime app");
|
||||
}
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
|
@ -303,7 +303,7 @@ class Automation(object):
|
||||
permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite"))
|
||||
cursor = permDB.cursor();
|
||||
|
||||
cursor.execute("PRAGMA user_version=3");
|
||||
cursor.execute("PRAGMA user_version=4");
|
||||
|
||||
# SQL copied from nsPermissionManager.cpp
|
||||
cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
|
||||
@ -313,13 +313,14 @@ class Automation(object):
|
||||
permission INTEGER,
|
||||
expireType INTEGER,
|
||||
expireTime INTEGER,
|
||||
modificationTime INTEGER,
|
||||
appId INTEGER,
|
||||
isInBrowserElement INTEGER)""")
|
||||
|
||||
# Insert desired permissions
|
||||
for perm in permissions.keys():
|
||||
for host,allow in permissions[perm]:
|
||||
cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0)",
|
||||
cursor.execute("INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)",
|
||||
(host, perm, 1 if allow else 2))
|
||||
|
||||
# Commit and close
|
||||
|
@ -646,6 +646,8 @@ public:
|
||||
}
|
||||
bool HasAttributeNS(const nsAString& aNamespaceURI,
|
||||
const nsAString& aLocalName) const;
|
||||
Element* Closest(const nsAString& aSelector,
|
||||
ErrorResult& aResult);
|
||||
bool Matches(const nsAString& aSelector,
|
||||
ErrorResult& aError);
|
||||
already_AddRefed<nsIHTMLCollection>
|
||||
|
@ -2747,6 +2747,33 @@ Element::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue)
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
Element*
|
||||
Element::Closest(const nsAString& aSelector, ErrorResult& aResult)
|
||||
{
|
||||
nsCSSSelectorList* selectorList = ParseSelectorList(aSelector, aResult);
|
||||
if (!selectorList) {
|
||||
// Either we failed (and aResult already has the exception), or this
|
||||
// is a pseudo-element-only selector that matches nothing.
|
||||
return nullptr;
|
||||
}
|
||||
OwnerDoc()->FlushPendingLinkUpdates();
|
||||
TreeMatchContext matchingContext(false,
|
||||
nsRuleWalker::eRelevantLinkUnvisited,
|
||||
OwnerDoc(),
|
||||
TreeMatchContext::eNeverMatchVisited);
|
||||
matchingContext.SetHasSpecifiedScope();
|
||||
matchingContext.AddScopeElement(this);
|
||||
for (nsINode* node = this; node; node = node->GetParentNode()) {
|
||||
if (node->IsElement() &&
|
||||
nsCSSRuleProcessor::SelectorListMatches(node->AsElement(),
|
||||
matchingContext,
|
||||
selectorList)) {
|
||||
return node->AsElement();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
Element::Matches(const nsAString& aSelector, ErrorResult& aError)
|
||||
{
|
||||
|
@ -1260,6 +1260,8 @@ GK_ATOM(clip_rule, "clip-rule")
|
||||
GK_ATOM(clipPath, "clipPath")
|
||||
GK_ATOM(clipPathUnits, "clipPathUnits")
|
||||
GK_ATOM(cm, "cm")
|
||||
GK_ATOM(colorBurn, "color-burn")
|
||||
GK_ATOM(colorDodge, "color-dodge")
|
||||
GK_ATOM(colorInterpolation, "color-interpolation")
|
||||
GK_ATOM(colorInterpolationFilters, "color-interpolation-filters")
|
||||
GK_ATOM(colorProfile, "color-profile")
|
||||
@ -1288,6 +1290,7 @@ GK_ATOM(elevation, "elevation")
|
||||
GK_ATOM(erode, "erode")
|
||||
GK_ATOM(ex, "ex")
|
||||
GK_ATOM(exact, "exact")
|
||||
GK_ATOM(exclusion, "exclusion")
|
||||
GK_ATOM(exponent, "exponent")
|
||||
GK_ATOM(feBlend, "feBlend")
|
||||
GK_ATOM(feColorMatrix, "feColorMatrix")
|
||||
@ -1352,7 +1355,9 @@ GK_ATOM(glyph_orientation_vertical, "glyph-orientation-vertical")
|
||||
GK_ATOM(grad, "grad")
|
||||
GK_ATOM(gradientTransform, "gradientTransform")
|
||||
GK_ATOM(gradientUnits, "gradientUnits")
|
||||
GK_ATOM(hardLight, "hard-light")
|
||||
GK_ATOM(hkern, "hkern")
|
||||
GK_ATOM(hue, "hue")
|
||||
GK_ATOM(hueRotate, "hueRotate")
|
||||
GK_ATOM(identity, "identity")
|
||||
GK_ATOM(image_rendering, "image-rendering")
|
||||
@ -1376,6 +1381,7 @@ GK_ATOM(linearGradient, "linearGradient")
|
||||
GK_ATOM(linearRGB, "linearRGB")
|
||||
GK_ATOM(list_style_type, "list-style-type")
|
||||
GK_ATOM(luminanceToAlpha, "luminanceToAlpha")
|
||||
GK_ATOM(luminosity, "luminosity")
|
||||
GK_ATOM(magnify, "magnify")
|
||||
GK_ATOM(marker, "marker")
|
||||
GK_ATOM(marker_end, "marker-end")
|
||||
@ -1441,6 +1447,7 @@ GK_ATOM(rotate, "rotate")
|
||||
GK_ATOM(rx, "rx")
|
||||
GK_ATOM(ry, "ry")
|
||||
GK_ATOM(saturate, "saturate")
|
||||
GK_ATOM(saturation, "saturation")
|
||||
GK_ATOM(set, "set")
|
||||
GK_ATOM(seed, "seed")
|
||||
GK_ATOM(shadow, "shadow")
|
||||
@ -1448,6 +1455,7 @@ GK_ATOM(shape_rendering, "shape-rendering")
|
||||
GK_ATOM(skewX, "skewX")
|
||||
GK_ATOM(skewY, "skewY")
|
||||
GK_ATOM(slope, "slope")
|
||||
GK_ATOM(softLight, "soft-light")
|
||||
GK_ATOM(spacing, "spacing")
|
||||
GK_ATOM(spacingAndGlyphs, "spacingAndGlyphs")
|
||||
GK_ATOM(specularConstant, "specularConstant")
|
||||
|
@ -574,6 +574,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183
|
||||
[test_domparser_null_char.html]
|
||||
[test_domparsing.html]
|
||||
[test_elementTraversal.html]
|
||||
[test_element_closest.html]
|
||||
[test_encodeToStringWithMaxLength.html]
|
||||
[test_fileapi.html]
|
||||
skip-if = e10s
|
||||
|
84
content/base/test/test_element_closest.html
Normal file
84
content/base/test/test_element_closest.html
Normal file
@ -0,0 +1,84 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1055533
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1055533</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body id="body">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1055533">Mozilla Bug 1055533</a>
|
||||
<div id="test8" class="div3">
|
||||
<div id="test7" class="div2">
|
||||
<div id="test6" class="div1">
|
||||
<form id="test10" class="form2"></form>
|
||||
<form id="test5" class="form1" name="form-a">
|
||||
<input id="test1" class="input1" required>
|
||||
<fieldset class="fieldset2" id="test2">
|
||||
<select id="test3" class="select1" required>
|
||||
<option default id="test4" value="">Test4</option>
|
||||
<option selected id="test11">Test11</option>
|
||||
<option id="test12">Test12</option>
|
||||
<option id="test13">Test13</option>
|
||||
</select>
|
||||
<input id="test9" type="text" required>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script class="testbody" type="text/javascript">
|
||||
test("select" , "test12", "test3");
|
||||
test("fieldset" , "test13", "test2");
|
||||
test("div" , "test13", "test6");
|
||||
test("body" , "test3" , "body");
|
||||
|
||||
test("[default]" , "test4" , "test4");
|
||||
test("[selected]" , "test4" , "");
|
||||
test("[selected]" , "test11", "test11");
|
||||
test('[name="form-a"]' , "test12", "test5");
|
||||
test('form[name="form-a"]' , "test13", "test5");
|
||||
test("input[required]" , "test9" , "test9");
|
||||
test("select[required]" , "test9" , "");
|
||||
|
||||
test("div:not(.div1)" , "test13", "test7");
|
||||
test("div.div3" , "test6" , "test8");
|
||||
test("div#test7" , "test1" , "test7");
|
||||
|
||||
test(".div3 > .div2" , "test12", "test7");
|
||||
test(".div3 > .div1" , "test12", "");
|
||||
test("form > input[required]" , "test9" , "");
|
||||
test("fieldset > select[required]", "test12", "test3");
|
||||
|
||||
test("input + fieldset" , "test6" , "");
|
||||
test("form + form" , "test3" , "test5");
|
||||
test("form + form" , "test5" , "test5");
|
||||
|
||||
test(":empty" , "test10", "test10");
|
||||
test(":last-child" , "test11", "test2");
|
||||
test(":first-child" , "test12", "test3");
|
||||
test(":invalid" , "test11", "test2");
|
||||
|
||||
test(":scope" , "test4", "test4");
|
||||
test("select > :scope" , "test4", "test4");
|
||||
test("div > :scope" , "test4", "");
|
||||
try {
|
||||
test(":has(> :scope)" , "test4", "test3");
|
||||
} catch(e) {
|
||||
todo(false, ":has(> :scope) [:has is not implemented yet]");
|
||||
}
|
||||
function test(aSelector, aElementId, aTargetId) {
|
||||
var el = document.getElementById(aElementId).closest(aSelector);
|
||||
if (el === null) {
|
||||
is("", aTargetId, aSelector);
|
||||
} else {
|
||||
is(el.id, aTargetId, aSelector);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -539,6 +539,8 @@ public:
|
||||
bool aCompileEventHandlers) MOZ_OVERRIDE;
|
||||
virtual void UnbindFromTree(bool aDeep = true,
|
||||
bool aNullParent = true) MOZ_OVERRIDE;
|
||||
|
||||
MOZ_ALWAYS_INLINE // Avoid a crashy hook from Avast 10 Beta
|
||||
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
const nsAString& aValue, bool aNotify)
|
||||
{
|
||||
|
@ -294,13 +294,7 @@ AudioSink::PlayFromAudioQueue()
|
||||
AssertOnAudioThread();
|
||||
NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
|
||||
nsAutoPtr<AudioData> audio(AudioQueue().PopFront());
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
NS_WARN_IF_FALSE(mPlaying, "Should be playing");
|
||||
// Awaken the decode loop if it's waiting for space to free up in the
|
||||
// audio queue.
|
||||
GetReentrantMonitor().NotifyAll();
|
||||
}
|
||||
|
||||
SINK_LOG_V("playing %u frames of audio at time %lld",
|
||||
audio->mFrames, audio->mTime);
|
||||
mAudioStream->Write(audio->mAudioData, audio->mFrames);
|
||||
|
@ -95,6 +95,7 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
||||
segment.AppendFrom(&mRawSegment);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
// Start queuing raw frames to the input buffers of OMXCodecWrapper.
|
||||
VideoSegment::ChunkIterator iter(segment);
|
||||
while (!iter.IsEnded()) {
|
||||
@ -105,7 +106,8 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
||||
uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
|
||||
layers::Image* img = (chunk.IsNull() || chunk.mFrame.GetForceBlack()) ?
|
||||
nullptr : chunk.mFrame.GetImage();
|
||||
mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs);
|
||||
rv = mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mLastFrame.TakeFrom(&chunk.mFrame);
|
||||
@ -119,22 +121,21 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
||||
uint64_t totalDurationUs = mTotalFrameDuration * USECS_PER_S / mTrackRate;
|
||||
layers::Image* img = (!mLastFrame.GetImage() || mLastFrame.GetForceBlack())
|
||||
? nullptr : mLastFrame.GetImage();
|
||||
nsresult result = mEncoder->Encode(img, mFrameWidth, mFrameHeight,
|
||||
totalDurationUs,
|
||||
OMXCodecWrapper::BUFFER_EOS);
|
||||
rv = mEncoder->Encode(img, mFrameWidth, mFrameHeight, totalDurationUs,
|
||||
OMXCodecWrapper::BUFFER_EOS);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Keep sending EOS signal until OMXVideoEncoder gets it.
|
||||
if (result == NS_OK) {
|
||||
mEosSetInEncoder = true;
|
||||
}
|
||||
mEosSetInEncoder = true;
|
||||
}
|
||||
|
||||
// Dequeue an encoded frame from the output buffers of OMXCodecWrapper.
|
||||
nsresult rv;
|
||||
nsTArray<uint8_t> buffer;
|
||||
int outFlags = 0;
|
||||
int64_t outTimeStampUs = 0;
|
||||
mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags,
|
||||
GET_ENCODED_VIDEO_FRAME_TIMEOUT);
|
||||
rv = mEncoder->GetNextEncodedFrame(&buffer, &outTimeStampUs, &outFlags,
|
||||
GET_ENCODED_VIDEO_FRAME_TIMEOUT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!buffer.IsEmpty()) {
|
||||
nsRefPtr<EncodedFrame> videoData = new EncodedFrame();
|
||||
if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) {
|
||||
|
@ -199,14 +199,6 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
if (!chunk.IsNull()) {
|
||||
gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
|
||||
gfxIntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Block the video frames come from video source.
|
||||
if (chunk.mFrame.GetImage()->GetFormat() != ImageFormat::PLANAR_YCBCR) {
|
||||
LOG("Can't encode this ImageFormat %x", chunk.mFrame.GetImage()->GetFormat());
|
||||
NotifyCancel();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
nsresult rv = Init(imgsize.width, imgsize.height,
|
||||
intrinsicSize.width, intrinsicSize.height,
|
||||
aTrackRate);
|
||||
|
@ -299,9 +299,10 @@ ConvertPlanarYCbCrToNV12(const PlanarYCbCrData* aSource, uint8_t* aDestination)
|
||||
|
||||
// Convert pixels in graphic buffer to NV12 format. aSource is the layer image
|
||||
// containing source graphic buffer, and aDestination is the destination of
|
||||
// conversion. Currently only 2 source format are supported:
|
||||
// conversion. Currently 3 source format are supported:
|
||||
// - NV21/HAL_PIXEL_FORMAT_YCrCb_420_SP (from camera preview window).
|
||||
// - YV12/HAL_PIXEL_FORMAT_YV12 (from video decoder).
|
||||
// - QCOM proprietary/HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS (from Flame HW video decoder)
|
||||
static void
|
||||
ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination)
|
||||
{
|
||||
@ -309,9 +310,6 @@ ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination)
|
||||
sp<GraphicBuffer> graphicBuffer = aSource->GetGraphicBuffer();
|
||||
|
||||
int pixelFormat = graphicBuffer->getPixelFormat();
|
||||
// Only support NV21 (from camera) or YV12 (from HW decoder output) for now.
|
||||
NS_ENSURE_TRUE_VOID(pixelFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
|
||||
pixelFormat == HAL_PIXEL_FORMAT_YV12);
|
||||
|
||||
void* imgPtr = nullptr;
|
||||
graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &imgPtr);
|
||||
@ -353,6 +351,26 @@ ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination)
|
||||
yuv.mCbSkip = 0;
|
||||
ConvertPlanarYCbCrToNV12(&yuv, aDestination);
|
||||
break;
|
||||
// From QCOM video decoder on Flame. See bug 997593.
|
||||
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
|
||||
// Venus formats are doucmented in kernel/include/media/msm_media_info.h:
|
||||
yuv.mYChannel = static_cast<uint8_t*>(imgPtr);
|
||||
yuv.mYSkip = 0;
|
||||
yuv.mYSize.width = graphicBuffer->getWidth();
|
||||
yuv.mYSize.height = graphicBuffer->getHeight();
|
||||
// - Y & UV Width aligned to 128
|
||||
yuv.mYStride = (yuv.mYSize.width + 127) & ~127;
|
||||
yuv.mCbCrSize.width = yuv.mYSize.width / 2;
|
||||
yuv.mCbCrSize.height = yuv.mYSize.height / 2;
|
||||
// - Y height aligned to 32
|
||||
yuv.mCbChannel = yuv.mYChannel + (yuv.mYStride * ((yuv.mYSize.height + 31) & ~31));
|
||||
// Interleaved VU plane.
|
||||
yuv.mCbSkip = 1;
|
||||
yuv.mCrChannel = yuv.mCbChannel + 1;
|
||||
yuv.mCrSkip = 1;
|
||||
yuv.mCbCrStride = yuv.mYStride;
|
||||
ConvertPlanarYCbCrToNV12(&yuv, aDestination);
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unsupported input gralloc image type. Should never be here.");
|
||||
}
|
||||
@ -369,11 +387,40 @@ OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
|
||||
NS_ENSURE_TRUE(aWidth == mWidth && aHeight == mHeight && aTimestamp >= 0,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
Image* img = const_cast<Image*>(aImage);
|
||||
ImageFormat format = ImageFormat::PLANAR_YCBCR;
|
||||
if (img) {
|
||||
format = img->GetFormat();
|
||||
gfx::IntSize size = img->GetSize();
|
||||
// Validate input image.
|
||||
NS_ENSURE_TRUE(aWidth == size.width, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aHeight == size.height, NS_ERROR_INVALID_ARG);
|
||||
if (format == ImageFormat::PLANAR_YCBCR) {
|
||||
NS_ENSURE_TRUE(static_cast<PlanarYCbCrImage*>(img)->IsValid(),
|
||||
NS_ERROR_INVALID_ARG);
|
||||
} else if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
|
||||
// Reject unsupported gralloc-ed buffers.
|
||||
int halFormat = static_cast<GrallocImage*>(img)->GetGraphicBuffer()->getPixelFormat();
|
||||
NS_ENSURE_TRUE(halFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
|
||||
halFormat == HAL_PIXEL_FORMAT_YV12 ||
|
||||
halFormat == GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
} else {
|
||||
// TODO: support RGB to YUV color conversion.
|
||||
NS_ERROR("Unsupported input image type.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
status_t result;
|
||||
|
||||
// Dequeue an input buffer.
|
||||
uint32_t index;
|
||||
result = mCodec->dequeueInputBuffer(&index, INPUT_BUFFER_TIMEOUT_US);
|
||||
if (result == -EAGAIN) {
|
||||
// Drop the frame when out of input buffer.
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
|
||||
|
||||
const sp<ABuffer>& inBuf = mInputBufs.itemAt(index);
|
||||
@ -385,30 +432,20 @@ OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
|
||||
// Buffer should be large enough to hold input image data.
|
||||
MOZ_ASSERT(dstSize >= yLen + uvLen);
|
||||
|
||||
inBuf->setRange(0, yLen + uvLen);
|
||||
|
||||
if (!aImage) {
|
||||
dstSize = yLen + uvLen;
|
||||
inBuf->setRange(0, dstSize);
|
||||
if (!img) {
|
||||
// Generate muted/black image directly in buffer.
|
||||
dstSize = yLen + uvLen;
|
||||
// Fill Y plane.
|
||||
memset(dst, 0x10, yLen);
|
||||
// Fill UV plane.
|
||||
memset(dst + yLen, 0x80, uvLen);
|
||||
} else {
|
||||
Image* img = const_cast<Image*>(aImage);
|
||||
ImageFormat format = img->GetFormat();
|
||||
|
||||
MOZ_ASSERT(aWidth == img->GetSize().width &&
|
||||
aHeight == img->GetSize().height);
|
||||
|
||||
if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
|
||||
ConvertGrallocImageToNV12(static_cast<GrallocImage*>(img), dst);
|
||||
} else if (format == ImageFormat::PLANAR_YCBCR) {
|
||||
ConvertPlanarYCbCrToNV12(static_cast<PlanarYCbCrImage*>(img)->GetData(),
|
||||
dst);
|
||||
} else {
|
||||
// TODO: support RGB to YUV color conversion.
|
||||
NS_ERROR("Unsupported input image type.");
|
||||
dst);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +197,7 @@ public:
|
||||
void StopImpl();
|
||||
void SnapshotImpl();
|
||||
void RotateImage(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
|
||||
uint32_t ConvertPixexFormatToFOURCC(int aFormat);
|
||||
void Notify(const mozilla::hal::ScreenConfiguration& aConfiguration);
|
||||
|
||||
nsresult TakePhoto(PhotoCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
@ -968,6 +968,22 @@ MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLe
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MediaEngineWebRTCVideoSource::ConvertPixexFormatToFOURCC(int aFormat)
|
||||
{
|
||||
switch (aFormat) {
|
||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||
return libyuv::FOURCC_NV21;
|
||||
case HAL_PIXEL_FORMAT_YV12:
|
||||
return libyuv::FOURCC_YV12;
|
||||
default: {
|
||||
LOG((" xxxxx Unknown pixel format %d", aFormat));
|
||||
MOZ_ASSERT(false, "Unknown pixel format.");
|
||||
return libyuv::FOURCC_ANY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaEngineWebRTCVideoSource::RotateImage(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) {
|
||||
layers::GrallocImage *nativeImage = static_cast<layers::GrallocImage*>(aImage);
|
||||
@ -1003,7 +1019,7 @@ MediaEngineWebRTCVideoSource::RotateImage(layers::Image* aImage, uint32_t aWidth
|
||||
aWidth, aHeight,
|
||||
aWidth, aHeight,
|
||||
static_cast<libyuv::RotationMode>(mRotation),
|
||||
libyuv::FOURCC_NV21);
|
||||
ConvertPixexFormatToFOURCC(graphicBuffer->getPixelFormat()));
|
||||
graphicBuffer->unlock();
|
||||
|
||||
const uint8_t lumaBpp = 8;
|
||||
|
@ -26,6 +26,17 @@ nsSVGEnumMapping SVGFEBlendElement::sModeMap[] = {
|
||||
{&nsGkAtoms::screen, SVG_FEBLEND_MODE_SCREEN},
|
||||
{&nsGkAtoms::darken, SVG_FEBLEND_MODE_DARKEN},
|
||||
{&nsGkAtoms::lighten, SVG_FEBLEND_MODE_LIGHTEN},
|
||||
{&nsGkAtoms::overlay, SVG_FEBLEND_MODE_OVERLAY},
|
||||
{&nsGkAtoms::colorDodge, SVG_FEBLEND_MODE_COLOR_DODGE},
|
||||
{&nsGkAtoms::colorBurn, SVG_FEBLEND_MODE_COLOR_BURN},
|
||||
{&nsGkAtoms::hardLight, SVG_FEBLEND_MODE_HARD_LIGHT},
|
||||
{&nsGkAtoms::softLight, SVG_FEBLEND_MODE_SOFT_LIGHT},
|
||||
{&nsGkAtoms::difference, SVG_FEBLEND_MODE_DIFFERENCE},
|
||||
{&nsGkAtoms::exclusion, SVG_FEBLEND_MODE_EXCLUSION},
|
||||
{&nsGkAtoms::hue, SVG_FEBLEND_MODE_HUE},
|
||||
{&nsGkAtoms::saturation, SVG_FEBLEND_MODE_SATURATION},
|
||||
{&nsGkAtoms::color, SVG_FEBLEND_MODE_COLOR},
|
||||
{&nsGkAtoms::luminosity, SVG_FEBLEND_MODE_LUMINOSITY},
|
||||
{nullptr, 0}
|
||||
};
|
||||
|
||||
|
@ -151,7 +151,7 @@ skip-if = toolkit == 'cocoa' || toolkit == 'android' || toolkit == 'gonk'
|
||||
[test_2d.gradient.radial.outside2.html]
|
||||
skip-if = toolkit != 'cocoa'
|
||||
[test_2d.gradient.radial.outside3.html]
|
||||
skip-if = toolkit != 'cocoa'
|
||||
disabled = bug 1038277
|
||||
# These tests only pass on Mac OS X >= 10.5; see bug 450114
|
||||
[test_2d.gradient.radial.touch1.html]
|
||||
disabled = bug 450114
|
||||
|
@ -85,6 +85,14 @@ public:
|
||||
*/
|
||||
nsresult NotifyIME(widget::IMEMessage aMessage);
|
||||
|
||||
/**
|
||||
* the offset of first composition string
|
||||
*/
|
||||
uint32_t NativeOffsetOfStartComposition() const
|
||||
{
|
||||
return mCompositionStartOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* the offset of first selected clause or start of of compositon
|
||||
*/
|
||||
|
@ -1714,12 +1714,16 @@ ContentChild::RecvAddPermission(const IPC::Permission& permission)
|
||||
getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
// child processes don't care about modification time.
|
||||
int64_t modificationTime = 0;
|
||||
|
||||
permissionManager->AddInternal(principal,
|
||||
nsCString(permission.type),
|
||||
permission.capability,
|
||||
0,
|
||||
permission.expireType,
|
||||
permission.expireTime,
|
||||
modificationTime,
|
||||
nsPermissionManager::eNotify,
|
||||
nsPermissionManager::eNoDBOperation);
|
||||
#endif
|
||||
|
@ -166,9 +166,11 @@ parent:
|
||||
*
|
||||
* offset The starting offset of this rect
|
||||
* rect The rect of first character of selected IME composition
|
||||
* caretOffset The offset of caret position
|
||||
* caretRect The rect of IME caret
|
||||
*/
|
||||
NotifyIMESelectedCompositionRect(uint32_t offset, nsIntRect rect, nsIntRect caretRect);
|
||||
NotifyIMESelectedCompositionRect(uint32_t offset, nsIntRect[] rect,
|
||||
uint32_t caretOffset, nsIntRect caretRect);
|
||||
|
||||
/**
|
||||
* Notifies chrome that there has been a change in selection
|
||||
|
@ -1310,13 +1310,16 @@ TabParent::RecvNotifyIMETextChange(const uint32_t& aStart,
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvNotifyIMESelectedCompositionRect(const uint32_t& aOffset,
|
||||
const nsIntRect& aRect,
|
||||
const nsIntRect& aCaretRect)
|
||||
TabParent::RecvNotifyIMESelectedCompositionRect(
|
||||
const uint32_t& aOffset,
|
||||
const InfallibleTArray<nsIntRect>& aRects,
|
||||
const uint32_t& aCaretOffset,
|
||||
const nsIntRect& aCaretRect)
|
||||
{
|
||||
// add rect to cache for another query
|
||||
mIMECompositionRectOffset = aOffset;
|
||||
mIMECompositionRect = aRect;
|
||||
mIMECompositionRects = aRects;
|
||||
mIMECaretOffset = aCaretOffset;
|
||||
mIMECaretRect = aCaretRect;
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
@ -1521,23 +1524,33 @@ TabParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent)
|
||||
break;
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
{
|
||||
if (aEvent.mInput.mOffset != mIMECompositionRectOffset ||
|
||||
aEvent.mInput.mLength != 1) {
|
||||
if (aEvent.mInput.mOffset < mIMECompositionRectOffset ||
|
||||
(aEvent.mInput.mOffset + aEvent.mInput.mLength >
|
||||
mIMECompositionRectOffset + mIMECompositionRects.Length())) {
|
||||
// XXX
|
||||
// we doesn't have cache for this request.
|
||||
break;
|
||||
}
|
||||
|
||||
aEvent.mReply.mOffset = mIMECompositionRectOffset;
|
||||
aEvent.mReply.mRect = mIMECompositionRect - GetChildProcessOffset();
|
||||
uint32_t baseOffset = aEvent.mInput.mOffset - mIMECompositionRectOffset;
|
||||
uint32_t endOffset = baseOffset + aEvent.mInput.mLength;
|
||||
aEvent.mReply.mRect.SetEmpty();
|
||||
for (uint32_t i = baseOffset; i < endOffset; i++) {
|
||||
aEvent.mReply.mRect =
|
||||
aEvent.mReply.mRect.Union(mIMECompositionRects[i]);
|
||||
}
|
||||
aEvent.mReply.mOffset = aEvent.mInput.mOffset;
|
||||
aEvent.mReply.mRect = aEvent.mReply.mRect - GetChildProcessOffset();
|
||||
aEvent.mSucceeded = true;
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_CARET_RECT:
|
||||
{
|
||||
if (aEvent.mInput.mOffset != mIMECompositionRectOffset) {
|
||||
if (aEvent.mInput.mOffset != mIMECaretOffset) {
|
||||
break;
|
||||
}
|
||||
|
||||
aEvent.mReply.mOffset = mIMECompositionRectOffset;
|
||||
aEvent.mReply.mOffset = mIMECaretOffset;
|
||||
aEvent.mReply.mRect = mIMECaretRect - GetChildProcessOffset();
|
||||
aEvent.mSucceeded = true;
|
||||
}
|
||||
|
@ -163,9 +163,11 @@ public:
|
||||
const uint32_t& aEnd,
|
||||
const uint32_t& aNewEnd,
|
||||
const bool& aCausedByComposition) MOZ_OVERRIDE;
|
||||
virtual bool RecvNotifyIMESelectedCompositionRect(const uint32_t& aOffset,
|
||||
const nsIntRect& aRect,
|
||||
const nsIntRect& aCaretRect) MOZ_OVERRIDE;
|
||||
virtual bool RecvNotifyIMESelectedCompositionRect(
|
||||
const uint32_t& aOffset,
|
||||
const InfallibleTArray<nsIntRect>& aRects,
|
||||
const uint32_t& aCaretOffset,
|
||||
const nsIntRect& aCaretRect) MOZ_OVERRIDE;
|
||||
virtual bool RecvNotifyIMESelection(const uint32_t& aSeqno,
|
||||
const uint32_t& aAnchor,
|
||||
const uint32_t& aFocus,
|
||||
@ -376,7 +378,8 @@ protected:
|
||||
uint32_t mIMESeqno;
|
||||
|
||||
uint32_t mIMECompositionRectOffset;
|
||||
nsIntRect mIMECompositionRect;
|
||||
InfallibleTArray<nsIntRect> mIMECompositionRects;
|
||||
uint32_t mIMECaretOffset;
|
||||
nsIntRect mIMECaretRect;
|
||||
|
||||
// The number of event series we're currently capturing.
|
||||
|
@ -81,6 +81,8 @@ public:
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
notification->SetStoredState(true);
|
||||
|
||||
JSAutoCompartment ac(aCx, mGlobal);
|
||||
JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx));
|
||||
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
|
||||
@ -369,6 +371,18 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
}
|
||||
mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("click"));
|
||||
} else if (!strcmp("alertfinished", aTopic)) {
|
||||
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
||||
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID);
|
||||
if (notificationStorage && mNotification->IsStored()) {
|
||||
nsString origin;
|
||||
nsresult rv = Notification::GetOrigin(mNotification->GetOwner(), origin);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsString id;
|
||||
mNotification->GetID(id);
|
||||
notificationStorage->Delete(origin, id);
|
||||
}
|
||||
mNotification->SetStoredState(false);
|
||||
}
|
||||
mNotification->mIsClosed = true;
|
||||
mNotification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
|
||||
} else if (!strcmp("alertshow", aTopic)) {
|
||||
@ -384,7 +398,7 @@ Notification::Notification(const nsAString& aID, const nsAString& aTitle, const
|
||||
nsPIDOMWindow* aWindow)
|
||||
: DOMEventTargetHelper(aWindow),
|
||||
mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
|
||||
mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false)
|
||||
mTag(aTag), mIconUrl(aIconUrl), mIsClosed(false), mIsStored(false)
|
||||
{
|
||||
nsAutoString alertName;
|
||||
DebugOnly<nsresult> rv = GetOrigin(GetOwner(), alertName);
|
||||
@ -475,6 +489,8 @@ Notification::Constructor(const GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
notification->SetStoredState(true);
|
||||
|
||||
return notification.forget();
|
||||
}
|
||||
|
||||
@ -781,7 +797,7 @@ Notification::Close()
|
||||
void
|
||||
Notification::CloseInternal()
|
||||
{
|
||||
if (!mIsClosed) {
|
||||
if (mIsStored) {
|
||||
// Don't bail out if notification storage fails, since we still
|
||||
// want to send the close event through the alert service.
|
||||
nsCOMPtr<nsINotificationStorage> notificationStorage =
|
||||
@ -793,7 +809,9 @@ Notification::CloseInternal()
|
||||
notificationStorage->Delete(origin, mID);
|
||||
}
|
||||
}
|
||||
|
||||
SetStoredState(false);
|
||||
}
|
||||
if (!mIsClosed) {
|
||||
nsCOMPtr<nsIAlertsService> alertService =
|
||||
do_GetService(NS_ALERTSERVICE_CONTRACTID);
|
||||
if (alertService) {
|
||||
|
@ -77,6 +77,16 @@ public:
|
||||
aRetval = mIconUrl;
|
||||
}
|
||||
|
||||
void SetStoredState(bool val)
|
||||
{
|
||||
mIsStored = val;
|
||||
}
|
||||
|
||||
bool IsStored()
|
||||
{
|
||||
return mIsStored;
|
||||
}
|
||||
|
||||
nsIStructuredCloneContainer* GetDataCloneContainer();
|
||||
|
||||
static void RequestPermission(const GlobalObject& aGlobal,
|
||||
@ -167,6 +177,12 @@ protected:
|
||||
|
||||
bool mIsClosed;
|
||||
|
||||
// We need to make a distinction between the notification being closed i.e.
|
||||
// removed from any pending or active lists, and the notification being
|
||||
// removed from the database. NotificationDB might fail when trying to remove
|
||||
// the notification.
|
||||
bool mIsStored;
|
||||
|
||||
static uint32_t sCount;
|
||||
|
||||
private:
|
||||
|
@ -16,13 +16,26 @@ var MockServices = (function () {
|
||||
|
||||
var activeAppNotifications = Object.create(null);
|
||||
|
||||
window.addEventListener('mock-notification-close-event', function(e) {
|
||||
for (var alertName in activeAlertNotifications) {
|
||||
var notif = activeAlertNotifications[alertName];
|
||||
if (notif.title === e.detail.title) {
|
||||
notif.listener.observe(null, "alertfinished", null);
|
||||
delete activeAlertNotifications[alertName];
|
||||
delete activeAppNotifications[alertName];
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var mockAlertsService = {
|
||||
showAlertNotification: function(imageUrl, title, text, textClickable,
|
||||
cookie, alertListener, name) {
|
||||
var listener = SpecialPowers.wrap(alertListener);
|
||||
activeAlertNotifications[name] = {
|
||||
listener: listener,
|
||||
cookie: cookie
|
||||
cookie: cookie,
|
||||
title: title
|
||||
};
|
||||
|
||||
// fake async alert show event
|
||||
@ -105,6 +118,7 @@ var MockServices = (function () {
|
||||
},
|
||||
|
||||
activeAlertNotifications: activeAlertNotifications,
|
||||
|
||||
activeAppNotifications: activeAppNotifications,
|
||||
};
|
||||
})();
|
||||
|
@ -96,6 +96,14 @@ var NotificationTest = (function () {
|
||||
// TODO: how??
|
||||
},
|
||||
|
||||
fireCloseEvent: function (title) {
|
||||
window.dispatchEvent(new CustomEvent("mock-notification-close-event", {
|
||||
detail: {
|
||||
title: title
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
info: info,
|
||||
|
||||
customDataMatches: function(dataObj) {
|
||||
|
@ -119,10 +119,27 @@
|
||||
promise.then(function (notifications) {
|
||||
is(notifications.length, 3, "should return 3 notifications");
|
||||
done();
|
||||
n1.close();
|
||||
n2.close();
|
||||
n3.close();
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllNotifications
|
||||
deleteAllNotifications,
|
||||
|
||||
function (done) {
|
||||
info("Testing 'alertfinished' removes the notification from DB");
|
||||
var n = new Notification("test-title" + Math.random());
|
||||
n.onclose = function() {
|
||||
Notification.get().then(function(notifications) {
|
||||
is(notifications.length, 0, "should return 0 notifications");
|
||||
done();
|
||||
});
|
||||
}
|
||||
window.setTimeout(function() {
|
||||
NotificationTest.fireCloseEvent(n.title);
|
||||
}, 100);
|
||||
}
|
||||
];
|
||||
|
||||
MockServices.register();
|
||||
|
@ -52,6 +52,9 @@ interface Element : Node {
|
||||
[Pure]
|
||||
boolean hasAttributeNS(DOMString? namespace, DOMString localName);
|
||||
|
||||
[Throws, Pure]
|
||||
Element? closest(DOMString selector);
|
||||
|
||||
[Throws, Pure]
|
||||
boolean matches(DOMString selector);
|
||||
|
||||
|
@ -19,7 +19,17 @@ interface SVGFEBlendElement : SVGElement {
|
||||
const unsigned short SVG_FEBLEND_MODE_SCREEN = 3;
|
||||
const unsigned short SVG_FEBLEND_MODE_DARKEN = 4;
|
||||
const unsigned short SVG_FEBLEND_MODE_LIGHTEN = 5;
|
||||
|
||||
const unsigned short SVG_FEBLEND_MODE_OVERLAY = 6;
|
||||
const unsigned short SVG_FEBLEND_MODE_COLOR_DODGE = 7;
|
||||
const unsigned short SVG_FEBLEND_MODE_COLOR_BURN = 8;
|
||||
const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
|
||||
const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
|
||||
const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
|
||||
const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
|
||||
const unsigned short SVG_FEBLEND_MODE_HUE = 13;
|
||||
const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
|
||||
const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
|
||||
const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
|
||||
readonly attribute SVGAnimatedString in1;
|
||||
readonly attribute SVGAnimatedString in2;
|
||||
readonly attribute SVGAnimatedEnumeration mode;
|
||||
|
@ -357,7 +357,7 @@ nsPermissionManager::AppClearDataObserverInit()
|
||||
// nsPermissionManager Implementation
|
||||
|
||||
static const char kPermissionsFileName[] = "permissions.sqlite";
|
||||
#define HOSTS_SCHEMA_VERSION 3
|
||||
#define HOSTS_SCHEMA_VERSION 4
|
||||
|
||||
static const char kHostpermFileName[] = "hostperm.1";
|
||||
|
||||
@ -432,8 +432,12 @@ nsPermissionManager::Init()
|
||||
rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// The child process doesn't care about modification times - it neither
|
||||
// reads nor writes, nor removes them based on the date - so 0 (which
|
||||
// will end up as now()) is fine.
|
||||
uint64_t modificationTime = 0;
|
||||
AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
|
||||
perm.expireTime, eNotify, eNoDBOperation);
|
||||
perm.expireTime, modificationTime, eNotify, eNoDBOperation);
|
||||
}
|
||||
|
||||
// Stop here; we don't need the DB in the child process
|
||||
@ -546,6 +550,23 @@ nsPermissionManager::InitDB(bool aRemoveFile)
|
||||
|
||||
// fall through to the next upgrade
|
||||
|
||||
// Version 3->4 is the creation of the modificationTime field.
|
||||
case 3:
|
||||
{
|
||||
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_hosts ADD modificationTime INTEGER"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We leave the modificationTime at zero for all existing records; using
|
||||
// now() would mean, eg, that doing "remove all from the last hour"
|
||||
// within the first hour after migration would remove all permissions.
|
||||
|
||||
rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// fall through to the next upgrade
|
||||
|
||||
// current version.
|
||||
case HOSTS_SCHEMA_VERSION:
|
||||
break;
|
||||
@ -561,7 +582,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
|
||||
// check if all the expected columns exist
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"),
|
||||
"SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement FROM moz_hosts"),
|
||||
getter_AddRefs(stmt));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
break;
|
||||
@ -583,8 +604,8 @@ nsPermissionManager::InitDB(bool aRemoveFile)
|
||||
// cache frequently used statements (for insertion, deletion, and updating)
|
||||
rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO moz_hosts "
|
||||
"(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
|
||||
"(id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), getter_AddRefs(mStmtInsert));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
@ -594,7 +615,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
|
||||
|
||||
rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_hosts "
|
||||
"SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"),
|
||||
"SET permission = ?2, expireType= ?3, expireTime = ?4, modificationTime = ?5 WHERE id = ?1"),
|
||||
getter_AddRefs(mStmtUpdate));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -626,6 +647,7 @@ nsPermissionManager::CreateTable()
|
||||
",permission INTEGER"
|
||||
",expireType INTEGER"
|
||||
",expireTime INTEGER"
|
||||
",modificationTime INTEGER"
|
||||
",appId INTEGER"
|
||||
",isInBrowserElement INTEGER"
|
||||
")"));
|
||||
@ -679,8 +701,11 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// A modificationTime of zero will cause AddInternal to use now().
|
||||
int64_t modificationTime = 0;
|
||||
|
||||
return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
|
||||
aExpireType, aExpireTime, eNotify, eWriteToDB);
|
||||
aExpireType, aExpireTime, modificationTime, eNotify, eWriteToDB);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -690,6 +715,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
int64_t aID,
|
||||
uint32_t aExpireType,
|
||||
int64_t aExpireTime,
|
||||
int64_t aModificationTime,
|
||||
NotifyOperationType aNotifyOperation,
|
||||
DBOperationType aDBOperation)
|
||||
{
|
||||
@ -760,15 +786,27 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
// even if the new permission is UNKNOWN_ACTION (which means a "logical
|
||||
// remove" of the default)
|
||||
op = eOperationReplacingDefault;
|
||||
else if (aID == cIDPermissionIsDefault)
|
||||
// We are adding a default permission but a "real" permission already
|
||||
// exists. This almost-certainly means we just did a removeAllSince and
|
||||
// are re-importing defaults - so we can ignore this.
|
||||
op = eOperationNone;
|
||||
else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
|
||||
op = eOperationRemoving;
|
||||
else
|
||||
op = eOperationChanging;
|
||||
}
|
||||
|
||||
// child processes should *always* be passed a modificationTime of zero.
|
||||
MOZ_ASSERT(!IsChildProcess() || aModificationTime == 0);
|
||||
|
||||
// do the work for adding, deleting, or changing a permission:
|
||||
// update the in-memory list, write to the db, and notify consumers.
|
||||
int64_t id;
|
||||
if (aModificationTime == 0) {
|
||||
aModificationTime = PR_Now() / 1000;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case eOperationNone:
|
||||
{
|
||||
@ -786,7 +824,9 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
id = aID;
|
||||
}
|
||||
|
||||
entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
|
||||
entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission,
|
||||
aExpireType, aExpireTime,
|
||||
aModificationTime));
|
||||
|
||||
if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
|
||||
uint32_t appId;
|
||||
@ -797,7 +837,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
|
||||
UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement);
|
||||
}
|
||||
|
||||
if (aNotifyOperation == eNotify) {
|
||||
@ -824,7 +864,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
// We care only about the id here so we pass dummy values for all other
|
||||
// parameters.
|
||||
UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0, 0, false);
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0, 0, 0, false);
|
||||
|
||||
if (aNotifyOperation == eNotify) {
|
||||
NotifyObserversWithPermission(host,
|
||||
@ -866,12 +906,13 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
entry->GetPermissions()[index].mPermission = aPermission;
|
||||
entry->GetPermissions()[index].mExpireType = aExpireType;
|
||||
entry->GetPermissions()[index].mExpireTime = aExpireTime;
|
||||
entry->GetPermissions()[index].mModificationTime = aModificationTime;
|
||||
|
||||
if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
|
||||
// We care only about the id, the permission and expireType/expireTime here.
|
||||
// We care only about the id, the permission and expireType/expireTime/modificationTime here.
|
||||
// We pass dummy values for all other parameters.
|
||||
UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
|
||||
aPermission, aExpireType, aExpireTime, 0, false);
|
||||
aPermission, aExpireType, aExpireTime, aModificationTime, 0, false);
|
||||
|
||||
if (aNotifyOperation == eNotify) {
|
||||
NotifyObserversWithPermission(host,
|
||||
@ -915,6 +956,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
entry->GetPermissions()[index].mPermission = aPermission;
|
||||
entry->GetPermissions()[index].mExpireType = aExpireType;
|
||||
entry->GetPermissions()[index].mExpireTime = aExpireTime;
|
||||
entry->GetPermissions()[index].mModificationTime = aModificationTime;
|
||||
|
||||
// If requested, create the entry in the DB.
|
||||
if (aDBOperation == eWriteToDB) {
|
||||
@ -926,7 +968,8 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
|
||||
UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission,
|
||||
aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement);
|
||||
}
|
||||
|
||||
if (aNotifyOperation == eNotify) {
|
||||
@ -983,6 +1026,7 @@ nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
|
||||
0,
|
||||
nsIPermissionManager::EXPIRE_NEVER,
|
||||
0,
|
||||
0,
|
||||
eNotify,
|
||||
eWriteToDB);
|
||||
}
|
||||
@ -994,6 +1038,13 @@ nsPermissionManager::RemoveAll()
|
||||
return RemoveAllInternal(true);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPermissionManager::RemoveAllSince(int64_t aSince)
|
||||
{
|
||||
ENSURE_NOT_CHILD_PROCESS;
|
||||
return RemoveAllModifiedSince(aSince);
|
||||
}
|
||||
|
||||
void
|
||||
nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
|
||||
{
|
||||
@ -1323,12 +1374,16 @@ nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
|
||||
// helper struct for passing arguments into hash enumeration callback.
|
||||
struct nsGetEnumeratorData
|
||||
{
|
||||
nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes)
|
||||
nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray,
|
||||
const nsTArray<nsCString> *aTypes,
|
||||
int64_t aSince = 0)
|
||||
: array(aArray)
|
||||
, types(aTypes) {}
|
||||
, types(aTypes)
|
||||
, since(aSince) {}
|
||||
|
||||
nsCOMArray<nsIPermission> *array;
|
||||
const nsTArray<nsCString> *types;
|
||||
int64_t since;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
@ -1395,6 +1450,76 @@ NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aT
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AddPermissionsModifiedSinceToList(
|
||||
nsPermissionManager::PermissionHashKey* entry, void* arg)
|
||||
{
|
||||
nsGetEnumeratorData* data = static_cast<nsGetEnumeratorData *>(arg);
|
||||
|
||||
for (size_t i = 0; i < entry->GetPermissions().Length(); ++i) {
|
||||
const nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
|
||||
|
||||
if (data->since > permEntry.mModificationTime) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsPermission* perm = new nsPermission(entry->GetKey()->mHost,
|
||||
entry->GetKey()->mAppId,
|
||||
entry->GetKey()->mIsInBrowserElement,
|
||||
data->types->ElementAt(permEntry.mType),
|
||||
permEntry.mPermission,
|
||||
permEntry.mExpireType,
|
||||
permEntry.mExpireTime);
|
||||
|
||||
data->array->AppendObject(perm);
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime)
|
||||
{
|
||||
ENSURE_NOT_CHILD_PROCESS;
|
||||
|
||||
// roll an nsCOMArray of all our permissions, then hand out an enumerator
|
||||
nsCOMArray<nsIPermission> array;
|
||||
nsGetEnumeratorData data(&array, &mTypeArray, aModificationTime);
|
||||
|
||||
mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data);
|
||||
|
||||
for (int32_t i = 0; i<array.Count(); ++i) {
|
||||
nsAutoCString host;
|
||||
bool isInBrowserElement = false;
|
||||
nsAutoCString type;
|
||||
uint32_t appId = 0;
|
||||
|
||||
array[i]->GetHost(host);
|
||||
array[i]->GetIsInBrowserElement(&isInBrowserElement);
|
||||
array[i]->GetType(type);
|
||||
array[i]->GetAppId(&appId);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
if (NS_FAILED(GetPrincipal(host, appId, isInBrowserElement,
|
||||
getter_AddRefs(principal)))) {
|
||||
NS_ERROR("GetPrincipal() failed!");
|
||||
continue;
|
||||
}
|
||||
// AddInternal handles removal, so let it do the work...
|
||||
AddInternal(
|
||||
principal,
|
||||
type,
|
||||
nsIPermissionManager::UNKNOWN_ACTION,
|
||||
0,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0, 0,
|
||||
nsPermissionManager::eNotify,
|
||||
nsPermissionManager::eWriteToDB);
|
||||
}
|
||||
// now re-import any defaults as they may now be required if we just deleted
|
||||
// an override.
|
||||
ImportDefaults();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg)
|
||||
{
|
||||
@ -1475,6 +1600,7 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
|
||||
0,
|
||||
nsIPermissionManager::EXPIRE_NEVER,
|
||||
0,
|
||||
0,
|
||||
nsPermissionManager::eNotify,
|
||||
nsPermissionManager::eNoDBOperation);
|
||||
}
|
||||
@ -1646,7 +1772,7 @@ nsPermissionManager::Read()
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement "
|
||||
"SELECT id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement "
|
||||
"FROM moz_hosts"), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -1655,6 +1781,7 @@ nsPermissionManager::Read()
|
||||
uint32_t permission;
|
||||
uint32_t expireType;
|
||||
int64_t expireTime;
|
||||
int64_t modificationTime;
|
||||
uint32_t appId;
|
||||
bool isInBrowserElement;
|
||||
bool hasResult;
|
||||
@ -1682,15 +1809,17 @@ nsPermissionManager::Read()
|
||||
permission = stmt->AsInt32(3);
|
||||
expireType = stmt->AsInt32(4);
|
||||
|
||||
// convert into int64_t value (milliseconds)
|
||||
// convert into int64_t values (milliseconds)
|
||||
expireTime = stmt->AsInt64(5);
|
||||
modificationTime = stmt->AsInt64(6);
|
||||
|
||||
if (stmt->AsInt64(6) < 0) {
|
||||
if (stmt->AsInt64(7) < 0) {
|
||||
readError = true;
|
||||
continue;
|
||||
}
|
||||
appId = static_cast<uint32_t>(stmt->AsInt64(6));
|
||||
isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
|
||||
|
||||
appId = static_cast<uint32_t>(stmt->AsInt64(7));
|
||||
isInBrowserElement = static_cast<bool>(stmt->AsInt32(8));
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
|
||||
@ -1700,7 +1829,7 @@ nsPermissionManager::Read()
|
||||
}
|
||||
|
||||
rv = AddInternal(principal, type, permission, id, expireType, expireTime,
|
||||
eDontNotify, eNoDBOperation);
|
||||
modificationTime, eDontNotify, eNoDBOperation);
|
||||
if (NS_FAILED(rv)) {
|
||||
readError = true;
|
||||
continue;
|
||||
@ -1848,8 +1977,14 @@ nsPermissionManager::_DoImport(nsIInputStream *inputStream, mozIStorageConnectio
|
||||
nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// the import file format doesn't handle modification times, so we use
|
||||
// 0, which AddInternal will convert to now()
|
||||
int64_t modificationTime = 0;
|
||||
|
||||
rv = AddInternal(principal, lineArray[1], permission, id,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, operation);
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0,
|
||||
modificationTime,
|
||||
eDontNotify, operation);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -1880,6 +2015,7 @@ nsPermissionManager::UpdateDB(OperationType aOp,
|
||||
uint32_t aPermission,
|
||||
uint32_t aExpireType,
|
||||
int64_t aExpireTime,
|
||||
int64_t aModificationTime,
|
||||
uint32_t aAppId,
|
||||
bool aIsInBrowserElement)
|
||||
{
|
||||
@ -1912,10 +2048,13 @@ nsPermissionManager::UpdateDB(OperationType aOp,
|
||||
rv = aStmt->BindInt64ByIndex(5, aExpireTime);
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
rv = aStmt->BindInt64ByIndex(6, aAppId);
|
||||
rv = aStmt->BindInt64ByIndex(6, aModificationTime);
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement);
|
||||
rv = aStmt->BindInt64ByIndex(7, aAppId);
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
rv = aStmt->BindInt64ByIndex(8, aIsInBrowserElement);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1937,6 +2076,9 @@ nsPermissionManager::UpdateDB(OperationType aOp,
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
rv = aStmt->BindInt64ByIndex(3, aExpireTime);
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
rv = aStmt->BindInt64ByIndex(4, aModificationTime);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -37,12 +37,14 @@ public:
|
||||
{
|
||||
public:
|
||||
PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
|
||||
uint32_t aExpireType, int64_t aExpireTime)
|
||||
uint32_t aExpireType, int64_t aExpireTime,
|
||||
int64_t aModificationTime)
|
||||
: mID(aID)
|
||||
, mType(aType)
|
||||
, mPermission(aPermission)
|
||||
, mExpireType(aExpireType)
|
||||
, mExpireTime(aExpireTime)
|
||||
, mModificationTime(aModificationTime)
|
||||
, mNonSessionPermission(aPermission)
|
||||
, mNonSessionExpireType(aExpireType)
|
||||
, mNonSessionExpireTime(aExpireTime)
|
||||
@ -53,6 +55,7 @@ public:
|
||||
uint32_t mPermission;
|
||||
uint32_t mExpireType;
|
||||
int64_t mExpireTime;
|
||||
int64_t mModificationTime;
|
||||
uint32_t mNonSessionPermission;
|
||||
uint32_t mNonSessionExpireType;
|
||||
uint32_t mNonSessionExpireTime;
|
||||
@ -154,7 +157,7 @@ public:
|
||||
|
||||
// unknown permission... return relevant data
|
||||
return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0);
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -200,6 +203,7 @@ public:
|
||||
int64_t aID,
|
||||
uint32_t aExpireType,
|
||||
int64_t aExpireTime,
|
||||
int64_t aModificationTime,
|
||||
NotifyOperationType aNotifyOperation,
|
||||
DBOperationType aDBOperation);
|
||||
|
||||
@ -260,6 +264,7 @@ private:
|
||||
uint32_t aPermission,
|
||||
uint32_t aExpireType,
|
||||
int64_t aExpireTime,
|
||||
int64_t aModificationTime,
|
||||
uint32_t aAppId,
|
||||
bool aIsInBrowserElement);
|
||||
|
||||
@ -299,6 +304,13 @@ private:
|
||||
RemoveExpiredPermissionsForAppEnumerator(PermissionHashKey* entry,
|
||||
void* nonused);
|
||||
|
||||
|
||||
/**
|
||||
* This method removes all permissions modified after the specified time.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAllModifiedSince(int64_t aModificationTime);
|
||||
|
||||
nsCOMPtr<nsIObserverService> mObserverService;
|
||||
nsCOMPtr<nsIIDNService> mIDNService;
|
||||
|
||||
|
@ -3,8 +3,15 @@
|
||||
|
||||
// The origin we use in most of the tests.
|
||||
const TEST_ORIGIN = "example.org";
|
||||
const TEST_ORIGIN_2 = "example.com";
|
||||
const TEST_PERMISSION = "test-permission";
|
||||
|
||||
function promiseTimeout(delay) {
|
||||
let deferred = Promise.defer();
|
||||
do_timeout(delay, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
@ -28,6 +35,7 @@ add_task(function* do_test() {
|
||||
conv.writeString("# this is a comment\n");
|
||||
conv.writeString("\n"); // a blank line!
|
||||
conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN + "\n");
|
||||
conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2 + "\n");
|
||||
ostream.close();
|
||||
|
||||
// Set the preference used by the permission manager so the file is read.
|
||||
@ -92,6 +100,47 @@ add_task(function* do_test() {
|
||||
do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION, findCapabilityViaEnum());
|
||||
yield checkCapabilityViaDB(Ci.nsIPermissionManager.PROMPT_ACTION);
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// check default permissions and removeAllSince work as expected.
|
||||
pm.removeAll(); // ensure only defaults are there.
|
||||
|
||||
let permURI2 = NetUtil.newURI("http://" + TEST_ORIGIN_2);
|
||||
let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2);
|
||||
|
||||
// default for both principals is allow.
|
||||
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
|
||||
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
|
||||
|
||||
// Add a default override for TEST_ORIGIN_2 - this one should *not* be
|
||||
// restored in removeAllSince()
|
||||
pm.addFromPrincipal(principal2, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
|
||||
pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
|
||||
yield promiseTimeout(20);
|
||||
|
||||
let since = Number(Date.now());
|
||||
yield promiseTimeout(20);
|
||||
|
||||
// explicitly add a permission which overrides the default for the first
|
||||
// principal - this one *should* be removed by removeAllSince.
|
||||
pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
|
||||
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
|
||||
|
||||
// do a removeAllSince.
|
||||
pm.removeAllSince(since);
|
||||
|
||||
// the default for the first principal should re-appear as we modified it
|
||||
// later then |since|
|
||||
do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
|
||||
|
||||
// but the permission for principal2 should remain as we added that before |since|.
|
||||
do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
|
||||
pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
|
||||
|
||||
// remove the temp file we created.
|
||||
file.remove(false);
|
||||
});
|
||||
|
@ -113,13 +113,25 @@ function run_test() {
|
||||
);
|
||||
}
|
||||
|
||||
let earliestNow = Number(Date.now());
|
||||
// Initialize the permission manager service
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
let latestNow = Number(Date.now());
|
||||
|
||||
// The schema should still be 3. We want this test to be updated for each
|
||||
// schema update.
|
||||
do_check_eq(connection.schemaVersion, 3);
|
||||
// The schema should be upgraded to 4, and a 'modificationTime' column should
|
||||
// exist with all records having a value of 0.
|
||||
do_check_eq(connection.schemaVersion, 4);
|
||||
|
||||
let select = connection.createStatement("SELECT modificationTime FROM moz_hosts")
|
||||
let numMigrated = 0;
|
||||
while (select.executeStep()) {
|
||||
let thisModTime = select.getInt64(0);
|
||||
do_check_true(thisModTime == 0, "new modifiedTime field is correct");
|
||||
numMigrated += 1;
|
||||
}
|
||||
// check we found at least 1 record that was migrated.
|
||||
do_check_true(numMigrated > 0, "we found at least 1 record that was migrated");
|
||||
|
||||
// This permission should always be there.
|
||||
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
|
69
extensions/cookie/test/unit/test_permmanager_removesince.js
Normal file
69
extensions/cookie/test/unit/test_permmanager_removesince.js
Normal file
@ -0,0 +1,69 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that removing permissions since a specified time behaves as expected.
|
||||
|
||||
let test_generator = do_run_test();
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
test_generator.next();
|
||||
}
|
||||
|
||||
function continue_test()
|
||||
{
|
||||
do_run_generator(test_generator);
|
||||
}
|
||||
|
||||
function do_run_test() {
|
||||
// Set up a profile.
|
||||
let profile = do_get_profile();
|
||||
|
||||
let pm = Services.perms;
|
||||
|
||||
// to help with testing edge-cases, we will arrange for .removeAllSince to
|
||||
// remove *all* permissions from one principal and one permission from another.
|
||||
let permURI1 = NetUtil.newURI("http://example.com");
|
||||
let principal1 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI1);
|
||||
|
||||
let permURI2 = NetUtil.newURI("http://example.org");
|
||||
let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2);
|
||||
|
||||
// add a permission now - this isn't going to be removed.
|
||||
pm.addFromPrincipal(principal1, "test/remove-since", 1);
|
||||
|
||||
// sleep briefly, then record the time - we'll remove all since then.
|
||||
do_timeout(20, continue_test);
|
||||
yield;
|
||||
|
||||
let since = Number(Date.now());
|
||||
|
||||
// *sob* - on Windows at least, the now recorded by nsPermissionManager.cpp
|
||||
// might be a couple of ms *earlier* than what JS sees. So another sleep
|
||||
// to ensure our |since| is greater than the time of the permissions we
|
||||
// are now adding. Sadly this means we'll never be able to test when since
|
||||
// exactly equals the modTime, but there you go...
|
||||
do_timeout(20, continue_test);
|
||||
yield;
|
||||
|
||||
// add another item - this second one should get nuked.
|
||||
pm.addFromPrincipal(principal1, "test/remove-since-2", 1);
|
||||
|
||||
// add 2 items for the second principal - both will be removed.
|
||||
pm.addFromPrincipal(principal2, "test/remove-since", 1);
|
||||
pm.addFromPrincipal(principal2, "test/remove-since-2", 1);
|
||||
|
||||
// do the removal.
|
||||
pm.removeAllSince(since);
|
||||
|
||||
// principal1 - the first one should remain.
|
||||
do_check_eq(1, pm.testPermissionFromPrincipal(principal1, "test/remove-since"));
|
||||
// but the second should have been removed.
|
||||
do_check_eq(0, pm.testPermissionFromPrincipal(principal1, "test/remove-since-2"));
|
||||
|
||||
// principal2 - both should have been removed.
|
||||
do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since"));
|
||||
do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since-2"));
|
||||
|
||||
do_finish_generator_test(test_generator);
|
||||
}
|
@ -23,6 +23,7 @@ support-files =
|
||||
[test_permmanager_getPermissionObject.js]
|
||||
[test_permmanager_notifications.js]
|
||||
[test_permmanager_removeall.js]
|
||||
[test_permmanager_removesince.js]
|
||||
[test_permmanager_load_invalid_entries.js]
|
||||
skip-if = debug == true
|
||||
[test_permmanager_idn.js]
|
||||
|
@ -37,6 +37,7 @@ typedef _cairo_scaled_font cairo_scaled_font_t;
|
||||
|
||||
struct ID3D10Device1;
|
||||
struct ID3D10Texture2D;
|
||||
struct ID3D11Texture2D;
|
||||
struct ID3D11Device;
|
||||
struct ID2D1Device;
|
||||
struct IDWriteRenderingParams;
|
||||
@ -1190,9 +1191,12 @@ public:
|
||||
static void SetDirect3D10Device(ID3D10Device1 *aDevice);
|
||||
static ID3D10Device1 *GetDirect3D10Device();
|
||||
#ifdef USE_D2D1_1
|
||||
static TemporaryRef<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
|
||||
|
||||
static void SetDirect3D11Device(ID3D11Device *aDevice);
|
||||
static ID3D11Device *GetDirect3D11Device();
|
||||
static ID2D1Device *GetD2D1Device();
|
||||
static bool SupportsD2D1();
|
||||
#endif
|
||||
|
||||
static TemporaryRef<GlyphRenderingOptions>
|
||||
|
@ -333,13 +333,8 @@ DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface)
|
||||
{
|
||||
RefPtr<ID2D1Image> image;
|
||||
|
||||
if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
|
||||
image = static_cast<SourceSurfaceD2D1*>(aSurface)->GetImage();
|
||||
static_cast<SourceSurfaceD2D1*>(aSurface)->EnsureIndependent();
|
||||
} else {
|
||||
Rect r(Point(), Size(aSurface->GetSize()));
|
||||
image = GetBitmapForSurface(aSurface, r);
|
||||
}
|
||||
Rect r(Point(), Size(aSurface->GetSize()));
|
||||
image = GetBitmapForSurface(aSurface, r);
|
||||
|
||||
return image;
|
||||
}
|
||||
@ -2692,6 +2687,7 @@ DrawTargetD2D::factory()
|
||||
#else
|
||||
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
|
||||
#endif
|
||||
//options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
|
||||
|
||||
HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
|
||||
__uuidof(ID2D1Factory),
|
||||
|
@ -39,7 +39,30 @@ DrawTargetD2D1::~DrawTargetD2D1()
|
||||
{
|
||||
PopAllClips();
|
||||
|
||||
if (mSnapshot) {
|
||||
// We may hold the only reference. MarkIndependent will clear mSnapshot;
|
||||
// keep the snapshot object alive so it doesn't get destroyed while
|
||||
// MarkIndependent is running.
|
||||
RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
|
||||
// mSnapshot can be treated as independent of this DrawTarget since we know
|
||||
// this DrawTarget won't change again.
|
||||
deathGrip->MarkIndependent();
|
||||
// mSnapshot will be cleared now.
|
||||
}
|
||||
|
||||
mDC->EndDraw();
|
||||
|
||||
// Targets depending on us can break that dependency, since we're obviously not going to
|
||||
// be modified in the future.
|
||||
for (auto iter = mDependentTargets.begin();
|
||||
iter != mDependentTargets.end(); iter++) {
|
||||
(*iter)->mDependingOnTargets.erase(this);
|
||||
}
|
||||
// Our dependencies on other targets no longer matter.
|
||||
for (TargetSet::iterator iter = mDependingOnTargets.begin();
|
||||
iter != mDependingOnTargets.end(); iter++) {
|
||||
(*iter)->mDependentTargets.erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryRef<SourceSurface>
|
||||
@ -61,6 +84,13 @@ void
|
||||
DrawTargetD2D1::Flush()
|
||||
{
|
||||
mDC->Flush();
|
||||
|
||||
// We no longer depend on any target.
|
||||
for (TargetSet::iterator iter = mDependingOnTargets.begin();
|
||||
iter != mDependingOnTargets.end(); iter++) {
|
||||
(*iter)->mDependentTargets.erase(this);
|
||||
}
|
||||
mDependingOnTargets.clear();
|
||||
}
|
||||
|
||||
void
|
||||
@ -70,13 +100,6 @@ DrawTargetD2D1::DrawSurface(SourceSurface *aSurface,
|
||||
const DrawSurfaceOptions &aSurfOptions,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, ExtendMode::CLAMP);
|
||||
|
||||
if (!image) {
|
||||
gfxWarning() << *this << ": Unable to get D2D image for surface.";
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
||||
|
||||
D2D1_RECT_F samplingBounds;
|
||||
@ -95,10 +118,21 @@ DrawTargetD2D1::DrawSurface(SourceSurface *aSurface,
|
||||
// Here we scale the source pattern up to the size and position where we want
|
||||
// it to be.
|
||||
Matrix transform;
|
||||
transform.PreTranslate(aDest.x, aDest.y);
|
||||
transform.PreTranslate(aDest.x - aSource.x * xScale, aDest.y - aSource.y * yScale);
|
||||
transform.PreScale(xScale, yScale);
|
||||
|
||||
mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds),
|
||||
RefPtr<ID2D1Image> image = GetImageForSurface(aSurface, transform, ExtendMode::CLAMP);
|
||||
|
||||
if (!image) {
|
||||
gfxWarning() << *this << ": Unable to get D2D image for surface.";
|
||||
return;
|
||||
}
|
||||
|
||||
mDC->CreateImageBrush(image,
|
||||
D2D1::ImageBrushProperties(samplingBounds,
|
||||
D2D1_EXTEND_MODE_CLAMP,
|
||||
D2D1_EXTEND_MODE_CLAMP,
|
||||
D2DInterpolationMode(aSurfOptions.mFilter)),
|
||||
D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
|
||||
byRef(brush));
|
||||
mDC->FillRectangle(D2DRect(aDest), brush);
|
||||
@ -119,7 +153,11 @@ DrawTargetD2D1::DrawFilter(FilterNode *aNode,
|
||||
|
||||
PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
mDC->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
|
||||
|
||||
FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
|
||||
}
|
||||
|
||||
void
|
||||
@ -150,22 +188,11 @@ DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a };
|
||||
shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
|
||||
|
||||
// Step 2, move the shadow effect into place.
|
||||
RefPtr<ID2D1Effect> affineTransformEffect;
|
||||
mDC->CreateEffect(CLSID_D2D12DAffineTransform, byRef(affineTransformEffect));
|
||||
affineTransformEffect->SetInputEffect(0, shadowEffect);
|
||||
D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation(aOffset.x, aOffset.y);
|
||||
affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, matrix);
|
||||
D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset);
|
||||
mDC->DrawImage(shadowEffect, &shadowPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
|
||||
|
||||
// Step 3, create an effect that combines shadow and bitmap in one image.
|
||||
RefPtr<ID2D1Effect> compositeEffect;
|
||||
mDC->CreateEffect(CLSID_D2D1Composite, byRef(compositeEffect));
|
||||
compositeEffect->SetInputEffect(0, affineTransformEffect);
|
||||
compositeEffect->SetInput(1, image);
|
||||
compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2DCompositionMode(aOperator));
|
||||
|
||||
D2D1_POINT_2F surfPoint = D2DPoint(aDest);
|
||||
mDC->DrawImage(compositeEffect, &surfPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
|
||||
D2D1_POINT_2F imgPoint = D2DPoint(aDest);
|
||||
mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
|
||||
}
|
||||
|
||||
void
|
||||
@ -173,9 +200,46 @@ DrawTargetD2D1::ClearRect(const Rect &aRect)
|
||||
{
|
||||
MarkChanged();
|
||||
|
||||
mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
PopAllClips();
|
||||
|
||||
PushClipRect(aRect);
|
||||
|
||||
if (mTransformDirty ||
|
||||
!mTransform.IsIdentity()) {
|
||||
mDC->SetTransform(D2D1::IdentityMatrix());
|
||||
mTransformDirty = true;
|
||||
}
|
||||
|
||||
D2D1_RECT_F clipRect;
|
||||
bool isPixelAligned;
|
||||
if (mTransform.IsRectilinear() &&
|
||||
GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
|
||||
mDC->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
mDC->Clear();
|
||||
mDC->PopAxisAlignedClip();
|
||||
|
||||
PopClip();
|
||||
return;
|
||||
}
|
||||
|
||||
mDC->SetTarget(mTempBitmap);
|
||||
mDC->Clear();
|
||||
|
||||
IntRect addClipRect;
|
||||
RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
|
||||
|
||||
RefPtr<ID2D1SolidColorBrush> brush;
|
||||
mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
|
||||
mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
mDC->FillGeometry(geom, brush);
|
||||
mDC->PopAxisAlignedClip();
|
||||
|
||||
mDC->SetTarget(mBitmap);
|
||||
mDC->DrawImage(mTempBitmap, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT);
|
||||
|
||||
PopClip();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
@ -184,12 +248,12 @@ DrawTargetD2D1::MaskSurface(const Pattern &aSource,
|
||||
Point aOffset,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aSource);
|
||||
|
||||
RefPtr<ID2D1Bitmap> bitmap;
|
||||
|
||||
RefPtr<ID2D1Image> image = GetImageForSurface(aMask, ExtendMode::CLAMP);
|
||||
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aSource);
|
||||
|
||||
// FillOpacityMask only works if the antialias mode is MODE_ALIASED
|
||||
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
|
||||
@ -217,6 +281,8 @@ DrawTargetD2D1::CopySurface(SourceSurface *aSurface,
|
||||
{
|
||||
MarkChanged();
|
||||
|
||||
PopAllClips();
|
||||
|
||||
mDC->SetTransform(D2D1::IdentityMatrix());
|
||||
mTransformDirty = true;
|
||||
|
||||
@ -228,6 +294,26 @@ DrawTargetD2D1::CopySurface(SourceSurface *aSurface,
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFormat == SurfaceFormat::A8) {
|
||||
RefPtr<ID2D1Bitmap> bitmap;
|
||||
image->QueryInterface((ID2D1Bitmap**)byRef(bitmap));
|
||||
|
||||
mDC->PushAxisAlignedClip(D2D1::RectF(aDestination.x, aDestination.y,
|
||||
aDestination.x + aSourceRect.width,
|
||||
aDestination.y + aSourceRect.height),
|
||||
D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
mDC->Clear();
|
||||
mDC->PopAxisAlignedClip();
|
||||
|
||||
RefPtr<ID2D1SolidColorBrush> brush;
|
||||
mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
|
||||
D2D1::BrushProperties(), byRef(brush));
|
||||
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
|
||||
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
return;
|
||||
}
|
||||
|
||||
mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
|
||||
D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y),
|
||||
Float(aSourceRect.XMost()), Float(aSourceRect.YMost())),
|
||||
@ -241,6 +327,8 @@ DrawTargetD2D1::FillRect(const Rect &aRect,
|
||||
{
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
||||
mDC->FillRectangle(D2DRect(aRect), brush);
|
||||
|
||||
@ -255,6 +343,8 @@ DrawTargetD2D1::StrokeRect(const Rect &aRect,
|
||||
{
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
||||
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
||||
|
||||
@ -272,6 +362,8 @@ DrawTargetD2D1::StrokeLine(const Point &aStart,
|
||||
{
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
||||
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
||||
|
||||
@ -294,6 +386,8 @@ DrawTargetD2D1::Stroke(const Path *aPath,
|
||||
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
||||
RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
|
||||
|
||||
@ -315,6 +409,8 @@ DrawTargetD2D1::Fill(const Path *aPath,
|
||||
|
||||
PrepareForDrawing(aOptions.mCompositionOp, aPattern);
|
||||
|
||||
mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
|
||||
|
||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
|
||||
|
||||
mDC->FillGeometry(d2dPath->mGeometry, brush);
|
||||
@ -436,6 +532,8 @@ DrawTargetD2D1::PushClip(const Path *aPath)
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
|
||||
|
||||
PushedClip clip;
|
||||
@ -474,6 +572,8 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
||||
return PushClip(path);
|
||||
}
|
||||
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
PushedClip clip;
|
||||
Rect rect = mTransform.TransformBounds(aRect);
|
||||
IntRect intRect;
|
||||
@ -495,6 +595,8 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
||||
void
|
||||
DrawTargetD2D1::PopClip()
|
||||
{
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
if (mClipsArePushed) {
|
||||
if (mPushedClips.back().mPath) {
|
||||
mDC->PopLayer();
|
||||
@ -564,6 +666,11 @@ DrawTargetD2D1::CreatePathBuilder(FillRule aFillRule) const
|
||||
TemporaryRef<GradientStops>
|
||||
DrawTargetD2D1::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
|
||||
{
|
||||
if (aNumStops == 0) {
|
||||
gfxWarning() << *this << ": Failed to create GradientStopCollection with no stops.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
|
||||
|
||||
for (uint32_t i = 0; i < aNumStops; i++) {
|
||||
@ -594,10 +701,10 @@ DrawTargetD2D1::CreateFilter(FilterType aType)
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
|
||||
DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
|
||||
hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
@ -605,6 +712,62 @@ DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<IDXGISurface> dxgiSurface;
|
||||
aTexture->QueryInterface(__uuidof(IDXGISurface),
|
||||
(void**)((IDXGISurface**)byRef(dxgiSurface)));
|
||||
if (!dxgiSurface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D2D1_BITMAP_PROPERTIES1 props;
|
||||
props.dpiX = 96;
|
||||
props.dpiY = 96;
|
||||
props.pixelFormat = D2DPixelFormat(aFormat);
|
||||
props.colorContext = nullptr;
|
||||
props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
|
||||
hr = mDC->CreateBitmapFromDxgiSurface(dxgiSurface, props, (ID2D1Bitmap1**)byRef(mBitmap));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << *this << ": Error " << hr << " failed to create new bitmap.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mFormat = aFormat;
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
aTexture->GetDesc(&desc);
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
mSize.width = desc.Width;
|
||||
mSize.height = desc.Height;
|
||||
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
|
||||
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap));
|
||||
|
||||
mDC->SetTarget(mBitmap);
|
||||
|
||||
mDC->BeginDraw();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxWarning() << *this << ": Error " << hr << " failed to initialize new DeviceContext.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mDC->GetMaximumBitmapSize() < UINT32(aSize.width) ||
|
||||
mDC->GetMaximumBitmapSize() < UINT32(aSize.height)) {
|
||||
// This is 'ok'
|
||||
gfxDebug() << *this << ": Attempt to use unsupported surface size for D2D 1.1.";
|
||||
return false;
|
||||
}
|
||||
|
||||
D2D1_BITMAP_PROPERTIES1 props;
|
||||
props.dpiX = 96;
|
||||
props.dpiY = 96;
|
||||
@ -618,12 +781,17 @@ DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
|
||||
return false;
|
||||
}
|
||||
|
||||
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
|
||||
props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap));
|
||||
|
||||
mDC->SetTarget(mBitmap);
|
||||
|
||||
mDC->BeginDraw();
|
||||
|
||||
mDC->Clear();
|
||||
|
||||
mFormat = aFormat;
|
||||
mSize = aSize;
|
||||
|
||||
@ -718,14 +886,42 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
||||
|
||||
mDC->SetTarget(mBitmap);
|
||||
|
||||
if (patternSupported) {
|
||||
mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
|
||||
return;
|
||||
}
|
||||
|
||||
mDC->SetTransform(D2D1::IdentityMatrix());
|
||||
mTransformDirty = true;
|
||||
|
||||
if (patternSupported) {
|
||||
if (D2DSupportsCompositeMode(aOp)) {
|
||||
mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mBlendEffect) {
|
||||
mDC->CreateEffect(CLSID_D2D1Blend, byRef(mBlendEffect));
|
||||
|
||||
if (!mBlendEffect) {
|
||||
gfxWarning() << "Failed to create blend effect!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Bitmap> tmpBitmap;
|
||||
mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
|
||||
|
||||
// This flush is important since the copy method will not know about the context drawing to the surface.
|
||||
mDC->Flush();
|
||||
|
||||
// We need to use a copy here because affects don't accept a surface on
|
||||
// both their in- and outputs.
|
||||
tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
|
||||
|
||||
mBlendEffect->SetInput(0, tmpBitmap);
|
||||
mBlendEffect->SetInput(1, mTempBitmap);
|
||||
mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
|
||||
|
||||
mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Effect> radialGradientEffect;
|
||||
|
||||
mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect));
|
||||
@ -753,6 +949,128 @@ DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource)
|
||||
}
|
||||
}
|
||||
|
||||
static D2D1_RECT_F
|
||||
IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
|
||||
{
|
||||
D2D1_RECT_F result;
|
||||
result.left = max(aRect1.left, aRect2.left);
|
||||
result.top = max(aRect1.top, aRect2.top);
|
||||
result.right = min(aRect1.right, aRect2.right);
|
||||
result.bottom = min(aRect1.bottom, aRect2.bottom);
|
||||
|
||||
result.right = max(result.right, result.left);
|
||||
result.bottom = max(result.bottom, result.top);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
|
||||
{
|
||||
if (!mPushedClips.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
|
||||
for (auto iter = mPushedClips.begin();iter != mPushedClips.end(); iter++) {
|
||||
if (iter->mPath) {
|
||||
return false;
|
||||
}
|
||||
aClipRect = IntersectRect(aClipRect, iter->mBounds);
|
||||
if (!iter->mIsPixelAligned) {
|
||||
aIsPixelAligned = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Geometry>
|
||||
DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
||||
{
|
||||
if (mCurrentClippedGeometry) {
|
||||
*aClipBounds = mCurrentClipBounds;
|
||||
return mCurrentClippedGeometry;
|
||||
}
|
||||
|
||||
mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
|
||||
|
||||
// if pathGeom is null then pathRect represents the path.
|
||||
RefPtr<ID2D1Geometry> pathGeom;
|
||||
D2D1_RECT_F pathRect;
|
||||
bool pathRectIsAxisAligned = false;
|
||||
auto iter = mPushedClips.begin();
|
||||
|
||||
if (iter->mPath) {
|
||||
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
||||
} else {
|
||||
pathRect = iter->mBounds;
|
||||
pathRectIsAxisAligned = iter->mIsPixelAligned;
|
||||
}
|
||||
|
||||
iter++;
|
||||
for (;iter != mPushedClips.end(); iter++) {
|
||||
// Do nothing but add it to the current clip bounds.
|
||||
if (!iter->mPath && iter->mIsPixelAligned) {
|
||||
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
||||
IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
|
||||
int32_t(iter->mBounds.right - iter->mBounds.left),
|
||||
int32_t(iter->mBounds.bottom - iter->mBounds.top)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pathGeom) {
|
||||
if (pathRectIsAxisAligned) {
|
||||
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
||||
IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
|
||||
int32_t(pathRect.right - pathRect.left),
|
||||
int32_t(pathRect.bottom - pathRect.top)));
|
||||
}
|
||||
if (iter->mPath) {
|
||||
// See if pathRect needs to go into the path geometry.
|
||||
if (!pathRectIsAxisAligned) {
|
||||
pathGeom = ConvertRectToGeometry(pathRect);
|
||||
} else {
|
||||
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
||||
}
|
||||
} else {
|
||||
pathRect = IntersectRect(pathRect, iter->mBounds);
|
||||
pathRectIsAxisAligned = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ID2D1PathGeometry> newGeom;
|
||||
factory()->CreatePathGeometry(byRef(newGeom));
|
||||
|
||||
RefPtr<ID2D1GeometrySink> currentSink;
|
||||
newGeom->Open(byRef(currentSink));
|
||||
|
||||
if (iter->mPath) {
|
||||
pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
|
||||
iter->mTransform, currentSink);
|
||||
} else {
|
||||
RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
|
||||
pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
|
||||
D2D1::IdentityMatrix(), currentSink);
|
||||
}
|
||||
|
||||
currentSink->Close();
|
||||
|
||||
pathGeom = newGeom.forget();
|
||||
}
|
||||
|
||||
// For now we need mCurrentClippedGeometry to always be non-nullptr. This
|
||||
// method might seem a little strange but it is just fine, if pathGeom is
|
||||
// nullptr pathRect will always still contain 1 clip unaccounted for
|
||||
// regardless of mCurrentClipBounds.
|
||||
if (!pathGeom) {
|
||||
pathGeom = ConvertRectToGeometry(pathRect);
|
||||
}
|
||||
mCurrentClippedGeometry = pathGeom.forget();
|
||||
*aClipBounds = mCurrentClipBounds;
|
||||
return mCurrentClippedGeometry;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PopAllClips()
|
||||
{
|
||||
@ -791,6 +1109,14 @@ DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Brush>
|
||||
DrawTargetD2D1::CreateTransparentBlackBrush()
|
||||
{
|
||||
RefPtr<ID2D1SolidColorBrush> brush;
|
||||
mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), byRef(brush));
|
||||
return brush;
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Brush>
|
||||
DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
||||
{
|
||||
@ -818,7 +1144,7 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
||||
|
||||
if (!stops) {
|
||||
gfxDebug() << "No stops specified for gradient pattern.";
|
||||
return nullptr;
|
||||
return CreateTransparentBlackBrush();
|
||||
}
|
||||
|
||||
if (pat->mBegin == pat->mEnd) {
|
||||
@ -848,7 +1174,7 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
||||
|
||||
if (!stops) {
|
||||
gfxDebug() << "No stops specified for gradient pattern.";
|
||||
return nullptr;
|
||||
return CreateTransparentBlackBrush();
|
||||
}
|
||||
|
||||
// This will not be a complex radial gradient brush.
|
||||
@ -868,7 +1194,7 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
||||
|
||||
if (!pat->mSurface) {
|
||||
gfxDebug() << "No source surface specified for surface pattern";
|
||||
return nullptr;
|
||||
return CreateTransparentBlackBrush();
|
||||
}
|
||||
|
||||
D2D1_RECT_F samplingBounds;
|
||||
@ -895,7 +1221,7 @@ DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
||||
}
|
||||
|
||||
gfxWarning() << "Invalid pattern type detected.";
|
||||
return nullptr;
|
||||
return CreateTransparentBlackBrush();
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Image>
|
||||
|
@ -125,6 +125,7 @@ public:
|
||||
virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
|
||||
|
||||
bool Init(const IntSize &aSize, SurfaceFormat aFormat);
|
||||
bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
|
||||
uint32_t GetByteSize() const;
|
||||
|
||||
TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
|
||||
@ -170,10 +171,19 @@ private:
|
||||
}
|
||||
void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
|
||||
|
||||
// This returns the clipped geometry, in addition it returns aClipBounds which
|
||||
// represents the intersection of all pixel-aligned rectangular clips that
|
||||
// are currently set. The returned clipped geometry must be clipped by these
|
||||
// bounds to correctly reflect the total clip. This is in device space.
|
||||
TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
|
||||
|
||||
bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
|
||||
|
||||
void PopAllClips();
|
||||
void PushClipsToDC(ID2D1DeviceContext *aDC);
|
||||
void PopClipsFromDC(ID2D1DeviceContext *aDC);
|
||||
|
||||
TemporaryRef<ID2D1Brush> CreateTransparentBlackBrush();
|
||||
TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
|
||||
|
||||
void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform);
|
||||
@ -182,6 +192,7 @@ private:
|
||||
|
||||
RefPtr<ID3D11Device> mDevice;
|
||||
RefPtr<ID3D11Texture2D> mTexture;
|
||||
RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
|
||||
// This is only valid if mCurrentClippedGeometry is non-null. And will
|
||||
// only be the intersection of all pixel-aligned retangular clips. This is in
|
||||
// device space.
|
||||
@ -189,6 +200,7 @@ private:
|
||||
mutable RefPtr<ID2D1DeviceContext> mDC;
|
||||
RefPtr<ID2D1Bitmap1> mBitmap;
|
||||
RefPtr<ID2D1Bitmap1> mTempBitmap;
|
||||
RefPtr<ID2D1Effect> mBlendEffect;
|
||||
|
||||
// We store this to prevent excessive SetTextRenderingParams calls.
|
||||
RefPtr<IDWriteRenderingParams> mTextRenderingParams;
|
||||
|
@ -548,6 +548,7 @@ Factory::SetDirect3D10Device(ID3D10Device1 *aDevice)
|
||||
|
||||
ID3D10Device1*
|
||||
Factory::GetDirect3D10Device()
|
||||
|
||||
{
|
||||
#ifdef DEBUG
|
||||
UINT mode = mD3D10Device->GetExceptionMode();
|
||||
@ -557,6 +558,28 @@ Factory::GetDirect3D10Device()
|
||||
}
|
||||
|
||||
#ifdef USE_D2D1_1
|
||||
TemporaryRef<DrawTarget>
|
||||
Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat)
|
||||
{
|
||||
RefPtr<DrawTargetD2D1> newTarget;
|
||||
|
||||
newTarget = new DrawTargetD2D1();
|
||||
if (newTarget->Init(aTexture, aFormat)) {
|
||||
RefPtr<DrawTarget> retVal = newTarget;
|
||||
|
||||
if (mRecorder) {
|
||||
retVal = new DrawTargetRecording(mRecorder, retVal, true);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
gfxWarning() << "Failed to create draw target for D3D10 texture.";
|
||||
|
||||
// Failed
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Factory::SetDirect3D11Device(ID3D11Device *aDevice)
|
||||
{
|
||||
@ -580,6 +603,12 @@ Factory::GetD2D1Device()
|
||||
{
|
||||
return mD2D1Device;
|
||||
}
|
||||
|
||||
bool
|
||||
Factory::SupportsD2D1()
|
||||
{
|
||||
return !!D2DFactory1();
|
||||
}
|
||||
#endif
|
||||
|
||||
TemporaryRef<GlyphRenderingOptions>
|
||||
|
@ -57,6 +57,29 @@ D2D1_BLEND_MODE D2DBlendMode(uint32_t aMode)
|
||||
return D2D1_BLEND_MODE_MULTIPLY;
|
||||
case BLEND_MODE_SCREEN:
|
||||
return D2D1_BLEND_MODE_SCREEN;
|
||||
case BLEND_MODE_OVERLAY:
|
||||
return D2D1_BLEND_MODE_OVERLAY;
|
||||
case BLEND_MODE_COLOR_DODGE:
|
||||
return D2D1_BLEND_MODE_COLOR_DODGE;
|
||||
case BLEND_MODE_COLOR_BURN:
|
||||
return D2D1_BLEND_MODE_COLOR_BURN;
|
||||
case BLEND_MODE_HARD_LIGHT:
|
||||
return D2D1_BLEND_MODE_HARD_LIGHT;
|
||||
case BLEND_MODE_SOFT_LIGHT:
|
||||
return D2D1_BLEND_MODE_SOFT_LIGHT;
|
||||
case BLEND_MODE_DIFFERENCE:
|
||||
return D2D1_BLEND_MODE_DIFFERENCE;
|
||||
case BLEND_MODE_EXCLUSION:
|
||||
return D2D1_BLEND_MODE_EXCLUSION;
|
||||
case BLEND_MODE_HUE:
|
||||
return D2D1_BLEND_MODE_HUE;
|
||||
case BLEND_MODE_SATURATION:
|
||||
return D2D1_BLEND_MODE_SATURATION;
|
||||
case BLEND_MODE_COLOR:
|
||||
return D2D1_BLEND_MODE_COLOR;
|
||||
case BLEND_MODE_LUMINOSITY:
|
||||
return D2D1_BLEND_MODE_LUMINOSITY;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown enum value!");
|
||||
}
|
||||
|
@ -994,6 +994,46 @@ FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
static CompositionOp ToBlendOp(BlendMode aOp)
|
||||
{
|
||||
switch (aOp) {
|
||||
case BLEND_MODE_MULTIPLY:
|
||||
return CompositionOp::OP_MULTIPLY;
|
||||
case BLEND_MODE_SCREEN:
|
||||
return CompositionOp::OP_SCREEN;
|
||||
case BLEND_MODE_OVERLAY:
|
||||
return CompositionOp::OP_OVERLAY;
|
||||
case BLEND_MODE_DARKEN:
|
||||
return CompositionOp::OP_DARKEN;
|
||||
case BLEND_MODE_LIGHTEN:
|
||||
return CompositionOp::OP_LIGHTEN;
|
||||
case BLEND_MODE_COLOR_DODGE:
|
||||
return CompositionOp::OP_COLOR_DODGE;
|
||||
case BLEND_MODE_COLOR_BURN:
|
||||
return CompositionOp::OP_COLOR_BURN;
|
||||
case BLEND_MODE_HARD_LIGHT:
|
||||
return CompositionOp::OP_HARD_LIGHT;
|
||||
case BLEND_MODE_SOFT_LIGHT:
|
||||
return CompositionOp::OP_SOFT_LIGHT;
|
||||
case BLEND_MODE_DIFFERENCE:
|
||||
return CompositionOp::OP_DIFFERENCE;
|
||||
case BLEND_MODE_EXCLUSION:
|
||||
return CompositionOp::OP_EXCLUSION;
|
||||
case BLEND_MODE_HUE:
|
||||
return CompositionOp::OP_HUE;
|
||||
case BLEND_MODE_SATURATION:
|
||||
return CompositionOp::OP_SATURATION;
|
||||
case BLEND_MODE_COLOR:
|
||||
return CompositionOp::OP_COLOR;
|
||||
case BLEND_MODE_LUMINOSITY:
|
||||
return CompositionOp::OP_LUMINOSITY;
|
||||
default:
|
||||
return CompositionOp::OP_OVER;
|
||||
}
|
||||
|
||||
return CompositionOp::OP_OVER;
|
||||
}
|
||||
|
||||
TemporaryRef<DataSourceSurface>
|
||||
FilterNodeBlendSoftware::Render(const IntRect& aRect)
|
||||
{
|
||||
@ -1010,14 +1050,38 @@ FilterNodeBlendSoftware::Render(const IntRect& aRect)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Second case: both are non-transparent.
|
||||
if (input1 && input2) {
|
||||
// Apply normal filtering.
|
||||
return FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
|
||||
// Second case: one of them is transparent. Return the non-transparent one.
|
||||
if (!input1 || !input2) {
|
||||
return input1 ? input1.forget() : input2.forget();
|
||||
}
|
||||
|
||||
// Third case: one of them is transparent. Return the non-transparent one.
|
||||
return input1 ? input1.forget() : input2.forget();
|
||||
// Third case: both are non-transparent.
|
||||
// Apply normal filtering.
|
||||
RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
|
||||
if (target != nullptr) {
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
IntSize size = input1->GetSize();
|
||||
target =
|
||||
Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
|
||||
if (MOZ2D_WARN_IF(!target)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
|
||||
|
||||
RefPtr<DrawTarget> dt =
|
||||
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
||||
target->GetData(),
|
||||
target->GetSize(),
|
||||
target->Stride(),
|
||||
target->GetFormat());
|
||||
|
||||
Rect r(0, 0, size.width, size.height);
|
||||
dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
|
||||
dt->Flush();
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -53,7 +53,7 @@ FilterProcessing::ApplyBlending(DataSourceSurface* aInput1, DataSourceSurface* a
|
||||
return ApplyBlending_SSE2(aInput1, aInput2, aBlendMode);
|
||||
#endif
|
||||
}
|
||||
return ApplyBlending_Scalar(aInput1, aInput2, aBlendMode);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -67,7 +67,6 @@ public:
|
||||
protected:
|
||||
static void ExtractAlpha_Scalar(const IntSize& size, uint8_t* sourceData, int32_t sourceStride, uint8_t* alphaData, int32_t alphaStride);
|
||||
static TemporaryRef<DataSourceSurface> ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface);
|
||||
static TemporaryRef<DataSourceSurface> ApplyBlending_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2, BlendMode aBlendMode);
|
||||
static void ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
uint8_t* aDestData, int32_t aDestStride,
|
||||
const IntRect& aDestRect, int32_t aRadius,
|
||||
|
@ -29,84 +29,6 @@ FilterProcessing::ConvertToB8G8R8A8_Scalar(SourceSurface* aSurface)
|
||||
return ConvertToB8G8R8A8_SIMD<simd::Scalaru8x16_t>(aSurface);
|
||||
}
|
||||
|
||||
template<BlendMode aBlendMode>
|
||||
static TemporaryRef<DataSourceSurface>
|
||||
ApplyBlending_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2)
|
||||
{
|
||||
IntSize size = aInput1->GetSize();
|
||||
RefPtr<DataSourceSurface> target =
|
||||
Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
|
||||
if (MOZ2D_WARN_IF(!target)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t* source1Data = aInput1->GetData();
|
||||
uint8_t* source2Data = aInput2->GetData();
|
||||
uint8_t* targetData = target->GetData();
|
||||
uint32_t targetStride = target->Stride();
|
||||
uint32_t source1Stride = aInput1->Stride();
|
||||
uint32_t source2Stride = aInput2->Stride();
|
||||
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
for (int32_t x = 0; x < size.width; x++) {
|
||||
uint32_t targetIndex = y * targetStride + 4 * x;
|
||||
uint32_t source1Index = y * source1Stride + 4 * x;
|
||||
uint32_t source2Index = y * source2Stride + 4 * x;
|
||||
uint32_t qa = source1Data[source1Index + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
|
||||
uint32_t qb = source2Data[source2Index + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
|
||||
for (int32_t i = std::min(B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_R);
|
||||
i <= std::max(B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_R); i++) {
|
||||
uint32_t ca = source1Data[source1Index + i];
|
||||
uint32_t cb = source2Data[source2Index + i];
|
||||
uint32_t val;
|
||||
switch (aBlendMode) {
|
||||
case BLEND_MODE_MULTIPLY:
|
||||
val = ((255 - qa) * cb + (255 - qb + cb) * ca);
|
||||
break;
|
||||
case BLEND_MODE_SCREEN:
|
||||
val = 255 * (cb + ca) - ca * cb;
|
||||
break;
|
||||
case BLEND_MODE_DARKEN:
|
||||
val = umin((255 - qa) * cb + 255 * ca,
|
||||
(255 - qb) * ca + 255 * cb);
|
||||
break;
|
||||
case BLEND_MODE_LIGHTEN:
|
||||
val = umax((255 - qa) * cb + 255 * ca,
|
||||
(255 - qb) * ca + 255 * cb);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
val = umin(FilterProcessing::FastDivideBy255<unsigned>(val), 255U);
|
||||
targetData[targetIndex + i] = static_cast<uint8_t>(val);
|
||||
}
|
||||
uint32_t alpha = 255 * 255 - (255 - qa) * (255 - qb);
|
||||
targetData[targetIndex + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
|
||||
FilterProcessing::FastDivideBy255<uint8_t>(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
return target.forget();
|
||||
}
|
||||
|
||||
TemporaryRef<DataSourceSurface>
|
||||
FilterProcessing::ApplyBlending_Scalar(DataSourceSurface* aInput1, DataSourceSurface* aInput2,
|
||||
BlendMode aBlendMode)
|
||||
{
|
||||
switch (aBlendMode) {
|
||||
case BLEND_MODE_MULTIPLY:
|
||||
return gfx::ApplyBlending_Scalar<BLEND_MODE_MULTIPLY>(aInput1, aInput2);
|
||||
case BLEND_MODE_SCREEN:
|
||||
return gfx::ApplyBlending_Scalar<BLEND_MODE_SCREEN>(aInput1, aInput2);
|
||||
case BLEND_MODE_DARKEN:
|
||||
return gfx::ApplyBlending_Scalar<BLEND_MODE_DARKEN>(aInput1, aInput2);
|
||||
case BLEND_MODE_LIGHTEN:
|
||||
return gfx::ApplyBlending_Scalar<BLEND_MODE_LIGHTEN>(aInput1, aInput2);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<MorphologyOperator Operator>
|
||||
static void
|
||||
ApplyMorphologyHorizontal_Scalar(uint8_t* aSourceData, int32_t aSourceStride,
|
||||
|
@ -45,7 +45,18 @@ enum BlendMode
|
||||
BLEND_MODE_MULTIPLY = 0,
|
||||
BLEND_MODE_SCREEN,
|
||||
BLEND_MODE_DARKEN,
|
||||
BLEND_MODE_LIGHTEN
|
||||
BLEND_MODE_LIGHTEN,
|
||||
BLEND_MODE_OVERLAY,
|
||||
BLEND_MODE_COLOR_DODGE,
|
||||
BLEND_MODE_COLOR_BURN,
|
||||
BLEND_MODE_HARD_LIGHT,
|
||||
BLEND_MODE_SOFT_LIGHT,
|
||||
BLEND_MODE_DIFFERENCE,
|
||||
BLEND_MODE_EXCLUSION,
|
||||
BLEND_MODE_HUE,
|
||||
BLEND_MODE_SATURATION,
|
||||
BLEND_MODE_COLOR,
|
||||
BLEND_MODE_LUMINOSITY
|
||||
};
|
||||
|
||||
enum BlendFilterInputs
|
||||
|
@ -194,6 +194,26 @@ static inline D2D1_PIXEL_FORMAT D2DPixelFormat(SurfaceFormat aFormat)
|
||||
}
|
||||
|
||||
#ifdef USE_D2D1_1
|
||||
static inline bool D2DSupportsCompositeMode(CompositionOp aOp)
|
||||
{
|
||||
switch(aOp) {
|
||||
case CompositionOp::OP_OVER:
|
||||
case CompositionOp::OP_ADD:
|
||||
case CompositionOp::OP_ATOP:
|
||||
case CompositionOp::OP_OUT:
|
||||
case CompositionOp::OP_IN:
|
||||
case CompositionOp::OP_SOURCE:
|
||||
case CompositionOp::OP_DEST_IN:
|
||||
case CompositionOp::OP_DEST_OUT:
|
||||
case CompositionOp::OP_DEST_OVER:
|
||||
case CompositionOp::OP_DEST_ATOP:
|
||||
case CompositionOp::OP_XOR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
|
||||
{
|
||||
switch(aOp) {
|
||||
@ -223,6 +243,44 @@ static inline D2D1_COMPOSITE_MODE D2DCompositionMode(CompositionOp aOp)
|
||||
return D2D1_COMPOSITE_MODE_SOURCE_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
static inline D2D1_BLEND_MODE D2DBlendMode(CompositionOp aOp)
|
||||
{
|
||||
switch (aOp) {
|
||||
case CompositionOp::OP_MULTIPLY:
|
||||
return D2D1_BLEND_MODE_MULTIPLY;
|
||||
case CompositionOp::OP_SCREEN:
|
||||
return D2D1_BLEND_MODE_SCREEN;
|
||||
case CompositionOp::OP_OVERLAY:
|
||||
return D2D1_BLEND_MODE_OVERLAY;
|
||||
case CompositionOp::OP_DARKEN:
|
||||
return D2D1_BLEND_MODE_DARKEN;
|
||||
case CompositionOp::OP_LIGHTEN:
|
||||
return D2D1_BLEND_MODE_LIGHTEN;
|
||||
case CompositionOp::OP_COLOR_DODGE:
|
||||
return D2D1_BLEND_MODE_COLOR_DODGE;
|
||||
case CompositionOp::OP_COLOR_BURN:
|
||||
return D2D1_BLEND_MODE_COLOR_BURN;
|
||||
case CompositionOp::OP_HARD_LIGHT:
|
||||
return D2D1_BLEND_MODE_HARD_LIGHT;
|
||||
case CompositionOp::OP_SOFT_LIGHT:
|
||||
return D2D1_BLEND_MODE_SOFT_LIGHT;
|
||||
case CompositionOp::OP_DIFFERENCE:
|
||||
return D2D1_BLEND_MODE_DIFFERENCE;
|
||||
case CompositionOp::OP_EXCLUSION:
|
||||
return D2D1_BLEND_MODE_EXCLUSION;
|
||||
case CompositionOp::OP_HUE:
|
||||
return D2D1_BLEND_MODE_HUE;
|
||||
case CompositionOp::OP_SATURATION:
|
||||
return D2D1_BLEND_MODE_SATURATION;
|
||||
case CompositionOp::OP_COLOR:
|
||||
return D2D1_BLEND_MODE_COLOR;
|
||||
case CompositionOp::OP_LUMINOSITY:
|
||||
return D2D1_BLEND_MODE_LUMINOSITY;
|
||||
default:
|
||||
return D2D1_BLEND_MODE_MULTIPLY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool IsPatternSupportedByD2D(const Pattern &aPattern)
|
||||
|
@ -147,14 +147,18 @@ RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
|
||||
float A;
|
||||
float radius1;
|
||||
float sq_radius1;
|
||||
float padding2[3];
|
||||
float repeat_correct;
|
||||
float allow_odd;
|
||||
float padding2[1];
|
||||
float transform[8];
|
||||
};
|
||||
|
||||
PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0,
|
||||
{ mCenter1.x, mCenter1.y },
|
||||
A, mRadius1, mRadius1 * mRadius1,
|
||||
{ 0, 0, 0 }, { mat._11, mat._21, mat._31, 0,
|
||||
mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1 : 0,
|
||||
mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1 : 0,
|
||||
{ 0 }, { mat._11, mat._21, mat._31, 0,
|
||||
mat._12, mat._22, mat._32, 0 } };
|
||||
|
||||
hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
|
||||
@ -365,13 +369,15 @@ RadialGradientEffectD2D1::CreateGradientTexture()
|
||||
UINT32 width = 4096;
|
||||
UINT32 stride = 4096 * 4;
|
||||
D2D1_RESOURCE_TEXTURE_PROPERTIES props;
|
||||
props.dimensions = 1;
|
||||
props.extents = &width;
|
||||
// Older shader models do not support 1D textures. So just use a width x 1 texture.
|
||||
props.dimensions = 2;
|
||||
UINT32 dims[] = { width, 1 };
|
||||
props.extents = dims;
|
||||
props.channelDepth = D2D1_CHANNEL_DEPTH_4;
|
||||
props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM;
|
||||
props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
D2D1_EXTEND_MODE extendMode = mStopCollection->GetExtendMode();
|
||||
props.extendModes = &extendMode;
|
||||
D2D1_EXTEND_MODE extendMode[] = { mStopCollection->GetExtendMode(), mStopCollection->GetExtendMode() };
|
||||
props.extendModes = extendMode;
|
||||
|
||||
HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, byRef(tex));
|
||||
|
||||
|
@ -309,7 +309,7 @@ ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize,
|
||||
TemporaryRef<Path>
|
||||
ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
|
||||
{
|
||||
if (aTarget->GetBackendType() != BackendType::DIRECT2D) {
|
||||
if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) {
|
||||
return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
|
||||
}
|
||||
|
||||
|
14342
gfx/2d/ShadersD2D.h
14342
gfx/2d/ShadersD2D.h
File diff suppressed because it is too large
Load Diff
1430
gfx/2d/ShadersD2D1.h
1430
gfx/2d/ShadersD2D1.h
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,13 @@ cbuffer constants : register(b0)
|
||||
float A : packoffset(c1.z);
|
||||
float radius1 : packoffset(c1.w);
|
||||
float sq_radius1 : packoffset(c2.x);
|
||||
|
||||
// The next two values are used for a hack to compensate for an apparent
|
||||
// bug in D2D where the GradientSampler SamplerState doesn't get the
|
||||
// correct addressing modes.
|
||||
float repeat_correct : packoffset(c2.y);
|
||||
float allow_odd : packoffset(c2.z);
|
||||
|
||||
float3x2 transform : packoffset(c3.x);
|
||||
}
|
||||
|
||||
@ -52,7 +59,13 @@ float4 SampleRadialGradientPS(
|
||||
|
||||
float upper_t = lerp(t.y, t.x, isValid.x);
|
||||
|
||||
float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t, 0.5));
|
||||
// Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
|
||||
float oddeven = abs(fmod(floor(upper_t), 2)) * allow_odd;
|
||||
|
||||
// Now let's calculate even or odd addressing in a branchless manner.
|
||||
float upper_t_repeated = ((upper_t - floor(upper_t)) * (1.0f - oddeven)) + ((ceil(upper_t) - upper_t) * oddeven);
|
||||
|
||||
float4 output = GradientTexture.Sample(GradientSampler, float2(upper_t * (1.0f - repeat_correct) + upper_t_repeated * repeat_correct, 0.5));
|
||||
// Premultiply
|
||||
output.rgb *= output.a;
|
||||
// Multiply the output color by the input mask for the operation.
|
||||
@ -84,7 +97,13 @@ float4 SampleRadialGradientA0PS(
|
||||
|
||||
float t = 0.5 * C / B;
|
||||
|
||||
float4 output = GradientTexture.Sample(GradientSampler, float2(t, 0.5));
|
||||
// Addressing mode bug work-around.. first let's see if we should consider odd repetitions separately.
|
||||
float oddeven = abs(fmod(floor(t), 2)) * allow_odd;
|
||||
|
||||
// Now let's calculate even or odd addressing in a branchless manner.
|
||||
float t_repeated = ((t - floor(t)) * (1.0f - oddeven)) + ((ceil(t) - t) * oddeven);
|
||||
|
||||
float4 output = GradientTexture.Sample(GradientSampler, float2(t * (1.0f - repeat_correct) + t_repeated * repeat_correct, 0.5));
|
||||
// Premultiply
|
||||
output.rgb *= output.a;
|
||||
// Multiply the output color by the input mask for the operation.
|
||||
@ -95,3 +114,4 @@ float4 SampleRadialGradientA0PS(
|
||||
// -radius1 >= t * diff.z
|
||||
return output * abs(step(t * diff.z, -radius1) - 1.0f);
|
||||
};
|
||||
|
||||
|
@ -95,7 +95,6 @@ SourceSurfaceD2D1::DrawTargetWillChange()
|
||||
mImage = mRealizedBitmap;
|
||||
|
||||
DrawTargetD2D1::mVRAMUsageSS += mSize.width * mSize.height * BytesPerPixel(mFormat);
|
||||
mDrawTarget = nullptr;
|
||||
|
||||
// We now no longer depend on the source surface content remaining the same.
|
||||
MarkIndependent();
|
||||
|
@ -57,7 +57,7 @@ private:
|
||||
|
||||
SurfaceFormat mFormat;
|
||||
IntSize mSize;
|
||||
RefPtr<DrawTargetD2D1> mDrawTarget;
|
||||
DrawTargetD2D1* mDrawTarget;
|
||||
};
|
||||
|
||||
class DataSourceSurfaceD2D1 : public DataSourceSurface
|
||||
|
@ -103,7 +103,9 @@ RotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget* aTarget,
|
||||
// (to avoid flickering) but direct2d is ok since it defers rendering.
|
||||
// We should try abstract this logic in a helper when we have other use
|
||||
// cases.
|
||||
if (aTarget->GetBackendType() == BackendType::DIRECT2D && aOperator == CompositionOp::OP_SOURCE) {
|
||||
if ((aTarget->GetBackendType() == BackendType::DIRECT2D ||
|
||||
aTarget->GetBackendType() == BackendType::DIRECT2D1_1) &&
|
||||
aOperator == CompositionOp::OP_SOURCE) {
|
||||
aOperator = CompositionOp::OP_OVER;
|
||||
if (snapshot->GetFormat() == SurfaceFormat::B8G8R8A8) {
|
||||
aTarget->ClearRect(ToRect(fillRect));
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#include "gfxD2DSurface.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "ReadbackManagerD3D11.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -172,8 +173,12 @@ TextureClientD3D11::TextureClientD3D11(gfx::SurfaceFormat aFormat, TextureFlags
|
||||
|
||||
TextureClientD3D11::~TextureClientD3D11()
|
||||
{
|
||||
if (mTexture && mActor) {
|
||||
KeepUntilFullDeallocation(new TKeepAlive<ID3D10Texture2D>(mTexture));
|
||||
if (mActor) {
|
||||
if (mTexture) {
|
||||
KeepUntilFullDeallocation(new TKeepAlive<ID3D10Texture2D>(mTexture10));
|
||||
} else if (mTexture10) {
|
||||
KeepUntilFullDeallocation(new TKeepAlive<ID3D11Texture2D>(mTexture));
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
// An Azure DrawTarget needs to be locked when it gets nullptr'ed as this is
|
||||
@ -181,11 +186,19 @@ TextureClientD3D11::~TextureClientD3D11()
|
||||
// shouldn't -really- need the lock but the debug layer chokes on this.
|
||||
if (mDrawTarget) {
|
||||
MOZ_ASSERT(!mIsLocked);
|
||||
MOZ_ASSERT(mTexture);
|
||||
MOZ_ASSERT(mTexture || mTexture10);
|
||||
MOZ_ASSERT(mDrawTarget->refCount() == 1);
|
||||
LockD3DTexture(mTexture.get());
|
||||
if (mTexture) {
|
||||
LockD3DTexture(mTexture.get());
|
||||
} else {
|
||||
LockD3DTexture(mTexture10.get());
|
||||
}
|
||||
mDrawTarget = nullptr;
|
||||
UnlockD3DTexture(mTexture.get());
|
||||
if (mTexture) {
|
||||
UnlockD3DTexture(mTexture.get());
|
||||
} else {
|
||||
UnlockD3DTexture(mTexture10.get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -206,12 +219,18 @@ TextureClientD3D11::CreateSimilar(TextureFlags aFlags,
|
||||
bool
|
||||
TextureClientD3D11::Lock(OpenMode aMode)
|
||||
{
|
||||
if (!mTexture) {
|
||||
if (!IsAllocated()) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(!mIsLocked, "The Texture is already locked!");
|
||||
|
||||
mIsLocked = LockD3DTexture(mTexture.get());
|
||||
if (mTexture) {
|
||||
MOZ_ASSERT(!mTexture10);
|
||||
mIsLocked = LockD3DTexture(mTexture.get());
|
||||
} else {
|
||||
MOZ_ASSERT(!mTexture);
|
||||
mIsLocked = LockD3DTexture(mTexture10.get());
|
||||
}
|
||||
if (!mIsLocked) {
|
||||
return false;
|
||||
}
|
||||
@ -254,11 +273,11 @@ TextureClientD3D11::Unlock()
|
||||
mDrawTarget->Flush();
|
||||
}
|
||||
|
||||
if (mReadbackSink) {
|
||||
if (mReadbackSink && mTexture10) {
|
||||
ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
|
||||
|
||||
D3D10_TEXTURE2D_DESC desc;
|
||||
mTexture->GetDesc(&desc);
|
||||
mTexture10->GetDesc(&desc);
|
||||
desc.BindFlags = 0;
|
||||
desc.Usage = D3D10_USAGE_STAGING;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
|
||||
@ -268,7 +287,7 @@ TextureClientD3D11::Unlock()
|
||||
HRESULT hr = device->CreateTexture2D(&desc, nullptr, byRef(tex));
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
device->CopyResource(tex, mTexture);
|
||||
device->CopyResource(tex, mTexture10);
|
||||
|
||||
gfxWindowsPlatform::GetPlatform()->GetReadbackManager()->PostTask(tex, mReadbackSink);
|
||||
} else {
|
||||
@ -278,7 +297,11 @@ TextureClientD3D11::Unlock()
|
||||
|
||||
// The DrawTarget is created only once, and is only usable between calls
|
||||
// to Lock and Unlock.
|
||||
UnlockD3DTexture(mTexture.get());
|
||||
if (mTexture) {
|
||||
UnlockD3DTexture(mTexture.get());
|
||||
} else {
|
||||
UnlockD3DTexture(mTexture10.get());
|
||||
}
|
||||
mIsLocked = false;
|
||||
}
|
||||
|
||||
@ -287,7 +310,7 @@ TextureClientD3D11::BorrowDrawTarget()
|
||||
{
|
||||
MOZ_ASSERT(mIsLocked, "Calling TextureClient::BorrowDrawTarget without locking :(");
|
||||
|
||||
if (!mTexture) {
|
||||
if (!mTexture && !mTexture10) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -296,7 +319,15 @@ TextureClientD3D11::BorrowDrawTarget()
|
||||
}
|
||||
|
||||
// This may return a null DrawTarget
|
||||
mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, mFormat);
|
||||
#if USE_D2D1_1
|
||||
if (mTexture) {
|
||||
mDrawTarget = Factory::CreateDrawTargetForD3D11Texture(mTexture, mFormat);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(mTexture10);
|
||||
mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture10, mFormat);
|
||||
}
|
||||
return mDrawTarget;
|
||||
}
|
||||
|
||||
@ -304,15 +335,38 @@ bool
|
||||
TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
|
||||
{
|
||||
mSize = aSize;
|
||||
ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
|
||||
HRESULT hr;
|
||||
|
||||
CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
aSize.width, aSize.height, 1, 1,
|
||||
D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE);
|
||||
if (mFormat == SurfaceFormat::A8) {
|
||||
// Currently TextureClientD3D11 does not support A8 surfaces. Fallback.
|
||||
return false;
|
||||
}
|
||||
|
||||
newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
||||
#ifdef USE_D2D1_1
|
||||
ID3D11Device* d3d11device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
|
||||
|
||||
HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
|
||||
if (gfxPrefs::Direct2DUse1_1() && d3d11device) {
|
||||
|
||||
CD3D11_TEXTURE2D_DESC newDesc(mFormat == SurfaceFormat::A8 ? DXGI_FORMAT_A8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
aSize.width, aSize.height, 1, 1,
|
||||
D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
|
||||
|
||||
newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
||||
|
||||
hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
|
||||
|
||||
CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
aSize.width, aSize.height, 1, 1,
|
||||
D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE);
|
||||
|
||||
newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
||||
|
||||
hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture10));
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOGD3D11("Error creating texture for client!");
|
||||
@ -332,9 +386,13 @@ TextureClientD3D11::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
|
||||
if (!IsAllocated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<IDXGIResource> resource;
|
||||
mTexture->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
if (mTexture) {
|
||||
mTexture->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
} else {
|
||||
mTexture10->QueryInterface((IDXGIResource**)byRef(resource));
|
||||
}
|
||||
|
||||
HANDLE sharedHandle;
|
||||
HRESULT hr = resource->GetSharedHandle(&sharedHandle);
|
||||
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
|
||||
// TextureClient
|
||||
|
||||
virtual bool IsAllocated() const MOZ_OVERRIDE { return !!mTexture; }
|
||||
virtual bool IsAllocated() const MOZ_OVERRIDE { return mTexture || mTexture10; }
|
||||
|
||||
virtual bool Lock(OpenMode aOpenMode) MOZ_OVERRIDE;
|
||||
|
||||
@ -65,7 +65,8 @@ public:
|
||||
|
||||
protected:
|
||||
gfx::IntSize mSize;
|
||||
RefPtr<ID3D10Texture2D> mTexture;
|
||||
RefPtr<ID3D10Texture2D> mTexture10;
|
||||
RefPtr<ID3D11Texture2D> mTexture;
|
||||
RefPtr<gfx::DrawTarget> mDrawTarget;
|
||||
gfx::SurfaceFormat mFormat;
|
||||
bool mIsLocked;
|
||||
|
@ -98,7 +98,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
SOURCES += [
|
||||
'd3d11/CompositorD3D11.cpp',
|
||||
'd3d11/ReadbackManagerD3D11.cpp',
|
||||
]
|
||||
]
|
||||
if CONFIG['MOZ_ENABLE_DIRECT2D1_1']:
|
||||
DEFINES['USE_D2D1_1'] = True
|
||||
|
||||
EXPORTS.gfxipc += [
|
||||
'ipc/ShadowLayerUtils.h',
|
||||
|
@ -673,13 +673,24 @@ FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescriptio
|
||||
filter->SetInput(IN_COMPOSITE_IN_START + 1, aSources[0]);
|
||||
} else {
|
||||
filter = aDT->CreateFilter(FilterType::BLEND);
|
||||
static const uint8_t blendModes[SVG_FEBLEND_MODE_LIGHTEN + 1] = {
|
||||
static const uint8_t blendModes[SVG_FEBLEND_MODE_LUMINOSITY + 1] = {
|
||||
0,
|
||||
0,
|
||||
BLEND_MODE_MULTIPLY,
|
||||
BLEND_MODE_SCREEN,
|
||||
BLEND_MODE_DARKEN,
|
||||
BLEND_MODE_LIGHTEN
|
||||
BLEND_MODE_LIGHTEN,
|
||||
BLEND_MODE_OVERLAY,
|
||||
BLEND_MODE_COLOR_DODGE,
|
||||
BLEND_MODE_COLOR_BURN,
|
||||
BLEND_MODE_HARD_LIGHT,
|
||||
BLEND_MODE_SOFT_LIGHT,
|
||||
BLEND_MODE_DIFFERENCE,
|
||||
BLEND_MODE_EXCLUSION,
|
||||
BLEND_MODE_HUE,
|
||||
BLEND_MODE_SATURATION,
|
||||
BLEND_MODE_COLOR,
|
||||
BLEND_MODE_LUMINOSITY
|
||||
};
|
||||
filter->SetAttribute(ATT_BLEND_BLENDMODE, (uint32_t)blendModes[mode]);
|
||||
filter->SetInput(IN_BLEND_IN, aSources[0]);
|
||||
|
@ -47,6 +47,17 @@ const unsigned short SVG_FEBLEND_MODE_MULTIPLY = 2;
|
||||
const unsigned short SVG_FEBLEND_MODE_SCREEN = 3;
|
||||
const unsigned short SVG_FEBLEND_MODE_DARKEN = 4;
|
||||
const unsigned short SVG_FEBLEND_MODE_LIGHTEN = 5;
|
||||
const unsigned short SVG_FEBLEND_MODE_OVERLAY = 6;
|
||||
const unsigned short SVG_FEBLEND_MODE_COLOR_DODGE = 7;
|
||||
const unsigned short SVG_FEBLEND_MODE_COLOR_BURN = 8;
|
||||
const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
|
||||
const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
|
||||
const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
|
||||
const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
|
||||
const unsigned short SVG_FEBLEND_MODE_HUE = 13;
|
||||
const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
|
||||
const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
|
||||
const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
|
||||
|
||||
// Edge Mode Values
|
||||
const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
|
||||
|
@ -83,6 +83,10 @@ public:
|
||||
|
||||
virtual int GetScreenDepth() const;
|
||||
|
||||
virtual bool CanRenderContentToDataSurface() const MOZ_OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool UseAcceleratedSkiaCanvas() MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
@ -244,7 +244,8 @@ TemporaryRef<Path> gfxContext::GetPath()
|
||||
|
||||
void gfxContext::SetPath(Path* path)
|
||||
{
|
||||
MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType());
|
||||
MOZ_ASSERT(path->GetBackendType() == mDT->GetBackendType() ||
|
||||
(mDT->GetBackendType() == BackendType::DIRECT2D1_1 && path->GetBackendType() == BackendType::DIRECT2D));
|
||||
mPath = path;
|
||||
mPathBuilder = nullptr;
|
||||
mPathIsRect = false;
|
||||
|
@ -993,6 +993,8 @@ gfxPlatform::BackendTypeForName(const nsCString& aName)
|
||||
return BackendType::SKIA;
|
||||
if (aName.EqualsLiteral("direct2d"))
|
||||
return BackendType::DIRECT2D;
|
||||
if (aName.EqualsLiteral("direct2d1.1"))
|
||||
return BackendType::DIRECT2D1_1;
|
||||
if (aName.EqualsLiteral("cg"))
|
||||
return BackendType::COREGRAPHICS;
|
||||
return BackendType::NONE;
|
||||
@ -1463,8 +1465,18 @@ gfxPlatform::InitBackendPrefs(uint32_t aCanvasBitmask, BackendType aCanvasDefaul
|
||||
if (mPreferredCanvasBackend == BackendType::NONE) {
|
||||
mPreferredCanvasBackend = aCanvasDefault;
|
||||
}
|
||||
mFallbackCanvasBackend =
|
||||
GetCanvasBackendPref(aCanvasBitmask & ~BackendTypeBit(mPreferredCanvasBackend));
|
||||
|
||||
if (mPreferredCanvasBackend == BackendType::DIRECT2D1_1) {
|
||||
// Falling back to D2D 1.0 won't help us here. When D2D 1.1 DT creation
|
||||
// fails it means the surface was too big or there's something wrong with
|
||||
// the device. D2D 1.0 will encounter a similar situation.
|
||||
mFallbackCanvasBackend =
|
||||
GetCanvasBackendPref(aCanvasBitmask &
|
||||
~(BackendTypeBit(mPreferredCanvasBackend) | BackendTypeBit(BackendType::DIRECT2D)));
|
||||
} else {
|
||||
mFallbackCanvasBackend =
|
||||
GetCanvasBackendPref(aCanvasBitmask & ~BackendTypeBit(mPreferredCanvasBackend));
|
||||
}
|
||||
|
||||
mContentBackendBitmask = aContentBitmask;
|
||||
mContentBackend = GetContentBackendPref(mContentBackendBitmask);
|
||||
|
@ -235,6 +235,16 @@ public:
|
||||
CreateDrawTargetForData(unsigned char* aData, const mozilla::gfx::IntSize& aSize,
|
||||
int32_t aStride, mozilla::gfx::SurfaceFormat aFormat);
|
||||
|
||||
/**
|
||||
* Returns true if rendering to data surfaces produces the same results as
|
||||
* rendering to offscreen surfaces on this platform, making it safe to
|
||||
* render content to data surfaces. This is generally false on platforms
|
||||
* which use different backends for each type of DrawTarget.
|
||||
*/
|
||||
virtual bool CanRenderContentToDataSurface() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should use Azure to render content with aTarget. For
|
||||
* example, it is possible that we are using Direct2D for rendering and thus
|
||||
|
@ -64,6 +64,10 @@ public:
|
||||
int32_t aRunScript,
|
||||
nsTArray<const char*>& aFontList);
|
||||
|
||||
virtual bool CanRenderContentToDataSurface() const MOZ_OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UseAcceleratedCanvas();
|
||||
|
||||
virtual bool UseTiling() MOZ_OVERRIDE;
|
||||
|
@ -192,6 +192,7 @@ private:
|
||||
|
||||
DECL_GFX_PREF(Once, "gfx.direct2d.disabled", Direct2DDisabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.direct2d.use1_1", Direct2DUse1_1, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.layerscope.enabled", LayerScopeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.layerscope.port", LayerScopePort, int32_t, 23456);
|
||||
|
@ -709,6 +709,12 @@ static void
|
||||
ClipToRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion,
|
||||
bool aSnap)
|
||||
{
|
||||
if (!aRegion.IsComplex()) {
|
||||
nsIntRect rect = aRegion.GetBounds();
|
||||
aTarget->PushClipRect(Rect(rect.x, rect.y, rect.width, rect.height));
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Path> path = PathFromRegionInternal(aTarget, aRegion, aSnap);
|
||||
aTarget->PushClip(path);
|
||||
}
|
||||
|
@ -312,6 +312,10 @@ gfxWindowsPlatform::gfxWindowsPlatform()
|
||||
|
||||
UpdateRenderMode();
|
||||
|
||||
if (gfxPrefs::Direct2DUse1_1()) {
|
||||
InitD3D11Devices();
|
||||
}
|
||||
|
||||
RegisterStrongMemoryReporter(new GPUAdapterReporter());
|
||||
}
|
||||
|
||||
@ -438,7 +442,12 @@ gfxWindowsPlatform::UpdateRenderMode()
|
||||
if (mRenderMode == RENDER_DIRECT2D) {
|
||||
canvasMask |= BackendTypeBit(BackendType::DIRECT2D);
|
||||
contentMask |= BackendTypeBit(BackendType::DIRECT2D);
|
||||
defaultBackend = BackendType::DIRECT2D;
|
||||
if (gfxPrefs::Direct2DUse1_1() && Factory::SupportsD2D1()) {
|
||||
contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
|
||||
defaultBackend = BackendType::DIRECT2D1_1;
|
||||
} else {
|
||||
defaultBackend = BackendType::DIRECT2D;
|
||||
}
|
||||
} else {
|
||||
canvasMask |= BackendTypeBit(BackendType::SKIA);
|
||||
}
|
||||
@ -1366,43 +1375,23 @@ gfxWindowsPlatform::GetD3D11Device()
|
||||
return mD3D11Device;
|
||||
}
|
||||
|
||||
mD3D11DeviceInitialized = true;
|
||||
|
||||
nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
|
||||
decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
|
||||
GetProcAddress(d3d11Module, "D3D11CreateDevice");
|
||||
|
||||
if (!d3d11CreateDevice) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsTArray<D3D_FEATURE_LEVEL> featureLevels;
|
||||
if (IsWin8OrLater()) {
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
|
||||
}
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
|
||||
|
||||
RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
|
||||
|
||||
if (!adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HRESULT hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
featureLevels.Elements(), featureLevels.Length(),
|
||||
D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
|
||||
|
||||
// We leak these everywhere and we need them our entire runtime anyway, let's
|
||||
// leak it here as well.
|
||||
d3d11Module.disown();
|
||||
InitD3D11Devices();
|
||||
|
||||
return mD3D11Device;
|
||||
}
|
||||
|
||||
ID3D11Device*
|
||||
gfxWindowsPlatform::GetD3D11ContentDevice()
|
||||
{
|
||||
if (mD3D11DeviceInitialized) {
|
||||
return mD3D11ContentDevice;
|
||||
}
|
||||
|
||||
InitD3D11Devices();
|
||||
|
||||
return mD3D11ContentDevice;
|
||||
}
|
||||
|
||||
ReadbackManagerD3D11*
|
||||
gfxWindowsPlatform::GetReadbackManager()
|
||||
{
|
||||
@ -1487,3 +1476,63 @@ gfxWindowsPlatform::GetDXGIAdapter()
|
||||
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
void
|
||||
gfxWindowsPlatform::InitD3D11Devices()
|
||||
{
|
||||
mD3D11DeviceInitialized = true;
|
||||
|
||||
nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
|
||||
decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
|
||||
GetProcAddress(d3d11Module, "D3D11CreateDevice");
|
||||
|
||||
if (!d3d11CreateDevice) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<D3D_FEATURE_LEVEL> featureLevels;
|
||||
if (IsWin8OrLater()) {
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
|
||||
}
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
|
||||
featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
|
||||
|
||||
RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
|
||||
|
||||
if (!adapter) {
|
||||
return;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
featureLevels.Elements(), featureLevels.Length(),
|
||||
D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_D2D1_1
|
||||
if (Factory::SupportsD2D1()) {
|
||||
hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
featureLevels.Elements(), featureLevels.Length(),
|
||||
D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
mD3D11Device = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Factory::SetDirect3D11Device(mD3D11ContentDevice);
|
||||
}
|
||||
#endif
|
||||
|
||||
// We leak these everywhere and we need them our entire runtime anyway, let's
|
||||
// leak it here as well.
|
||||
d3d11Module.disown();
|
||||
}
|
@ -261,6 +261,7 @@ public:
|
||||
ID3D10Device1 *GetD3D10Device() { return mD2DDevice ? cairo_d2d_device_get_device(mD2DDevice) : nullptr; }
|
||||
#endif
|
||||
ID3D11Device *GetD3D11Device();
|
||||
ID3D11Device *GetD3D11ContentDevice();
|
||||
|
||||
mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();
|
||||
|
||||
@ -274,6 +275,7 @@ protected:
|
||||
|
||||
private:
|
||||
void Init();
|
||||
void InitD3D11Devices();
|
||||
IDXGIAdapter1 *GetDXGIAdapter();
|
||||
|
||||
bool mUseDirectWrite;
|
||||
@ -291,6 +293,7 @@ private:
|
||||
mozilla::RefPtr<IDXGIAdapter1> mAdapter;
|
||||
nsRefPtr<mozilla::layers::DeviceManagerD3D9> mDeviceManager;
|
||||
mozilla::RefPtr<ID3D11Device> mD3D11Device;
|
||||
mozilla::RefPtr<ID3D11Device> mD3D11ContentDevice;
|
||||
bool mD3D11DeviceInitialized;
|
||||
mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
|
||||
|
||||
|
@ -184,6 +184,8 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
'gfxDWriteFontList.cpp',
|
||||
'gfxDWriteFonts.cpp',
|
||||
]
|
||||
if CONFIG['MOZ_ENABLE_DIRECT2D1_1']:
|
||||
DEFINES['USE_D2D1_1'] = True
|
||||
|
||||
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp.
|
||||
if CONFIG['INTEL_ARCHITECTURE']:
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "pixman.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
namespace image {
|
||||
|
||||
FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
|
||||
@ -258,8 +261,8 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
|
||||
// Create the Compositing Frame
|
||||
if (!mAnim->compositingFrame) {
|
||||
mAnim->compositingFrame.SetFrame(new imgFrame());
|
||||
nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
nsresult rv =
|
||||
mAnim->compositingFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
mAnim->compositingFrame.SetFrame(nullptr);
|
||||
return false;
|
||||
@ -388,8 +391,9 @@ FrameBlender::DoBlend(nsIntRect* aDirtyRect,
|
||||
// overwrite.
|
||||
if (!mAnim->compositingPrevFrame) {
|
||||
mAnim->compositingPrevFrame.SetFrame(new imgFrame());
|
||||
nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
nsresult rv =
|
||||
mAnim->compositingPrevFrame->InitForDecoder(mSize,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
mAnim->compositingPrevFrame.SetFrame(nullptr);
|
||||
return false;
|
||||
|
@ -223,8 +223,7 @@ public:
|
||||
// that's what the scaler outputs.
|
||||
nsRefPtr<imgFrame> tentativeDstFrame = new imgFrame();
|
||||
nsresult rv =
|
||||
tentativeDstFrame->Init(0, 0, dstSize.width, dstSize.height,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
tentativeDstFrame->InitForDecoder(dstSize, SurfaceFormat::B8G8R8A8);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
@ -648,60 +647,68 @@ RasterImage::GetType()
|
||||
}
|
||||
|
||||
already_AddRefed<imgFrame>
|
||||
RasterImage::GetImgFrameNoDecode(uint32_t framenum)
|
||||
RasterImage::GetFrameNoDecode(uint32_t aFrameNum)
|
||||
{
|
||||
if (!mAnim) {
|
||||
NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
|
||||
NS_ASSERTION(aFrameNum == 0, "Don't ask for a frame > 0 if we're not animated!");
|
||||
return mFrameBlender.GetFrame(0);
|
||||
}
|
||||
return mFrameBlender.GetFrame(framenum);
|
||||
return mFrameBlender.GetFrame(aFrameNum);
|
||||
}
|
||||
|
||||
already_AddRefed<imgFrame>
|
||||
RasterImage::GetImgFrame(uint32_t framenum)
|
||||
DrawableFrameRef
|
||||
RasterImage::GetFrame(uint32_t aFrameNum)
|
||||
{
|
||||
nsresult rv = WantDecodedFrames();
|
||||
CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
|
||||
return GetImgFrameNoDecode(framenum);
|
||||
}
|
||||
|
||||
already_AddRefed<imgFrame>
|
||||
RasterImage::GetDrawableImgFrame(uint32_t framenum)
|
||||
{
|
||||
nsRefPtr<imgFrame> frame;
|
||||
|
||||
if (mMultipart && framenum == GetCurrentImgFrameIndex()) {
|
||||
if (mMultipart &&
|
||||
aFrameNum == GetCurrentFrameIndex() &&
|
||||
mMultipartDecodedFrame) {
|
||||
// In the multipart case we prefer to use mMultipartDecodedFrame, which is
|
||||
// the most recent one we completely decoded, rather than display the real
|
||||
// current frame and risk severe tearing.
|
||||
frame = mMultipartDecodedFrame;
|
||||
return mMultipartDecodedFrame->DrawableRef();
|
||||
}
|
||||
|
||||
// Try our best to start decoding if it's necessary.
|
||||
nsresult rv = WantDecodedFrames();
|
||||
CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), DrawableFrameRef());
|
||||
|
||||
nsRefPtr<imgFrame> frame = GetFrameNoDecode(aFrameNum);
|
||||
if (!frame) {
|
||||
frame = GetImgFrame(framenum);
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
DrawableFrameRef ref = frame->DrawableRef();
|
||||
if (!ref) {
|
||||
// The OS threw this frame away. We need to discard and redecode.
|
||||
MOZ_ASSERT(!mAnim, "Animated frames should be locked");
|
||||
ForceDiscard();
|
||||
WantDecodedFrames();
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
// We will return a paletted frame if it's not marked as compositing failed
|
||||
// so we can catch crashes for reasons we haven't investigated.
|
||||
if (frame && frame->GetCompositingFailed())
|
||||
return nullptr;
|
||||
if (ref->GetCompositingFailed()) {
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
return frame.forget();
|
||||
return ref;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
RasterImage::GetCurrentImgFrameIndex() const
|
||||
RasterImage::GetCurrentFrameIndex() const
|
||||
{
|
||||
if (mAnim)
|
||||
if (mAnim) {
|
||||
return mAnim->GetCurrentAnimationFrameIndex();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
already_AddRefed<imgFrame>
|
||||
RasterImage::GetCurrentImgFrame()
|
||||
uint32_t
|
||||
RasterImage::GetRequestedFrameIndex(uint32_t aWhichFrame) const
|
||||
{
|
||||
return GetImgFrame(GetCurrentImgFrameIndex());
|
||||
return aWhichFrame == FRAME_FIRST ? 0 : GetCurrentFrameIndex();
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
@ -718,9 +725,8 @@ RasterImage::FrameIsOpaque(uint32_t aWhichFrame)
|
||||
return false;
|
||||
|
||||
// See if we can get an image frame.
|
||||
nsRefPtr<imgFrame> frame = aWhichFrame == FRAME_FIRST
|
||||
? GetImgFrameNoDecode(0)
|
||||
: GetImgFrameNoDecode(GetCurrentImgFrameIndex());
|
||||
nsRefPtr<imgFrame> frame =
|
||||
GetFrameNoDecode(GetRequestedFrameIndex(aWhichFrame));
|
||||
|
||||
// If we don't get a frame, the safe answer is "not opaque".
|
||||
if (!frame)
|
||||
@ -743,9 +749,8 @@ RasterImage::FrameRect(uint32_t aWhichFrame)
|
||||
}
|
||||
|
||||
// Get the requested frame.
|
||||
nsRefPtr<imgFrame> frame = aWhichFrame == FRAME_FIRST
|
||||
? GetImgFrameNoDecode(0)
|
||||
: GetImgFrameNoDecode(GetCurrentImgFrameIndex());
|
||||
nsRefPtr<imgFrame> frame =
|
||||
GetFrameNoDecode(GetRequestedFrameIndex(aWhichFrame));
|
||||
|
||||
// If we have the frame, use that rectangle.
|
||||
if (frame) {
|
||||
@ -760,12 +765,6 @@ RasterImage::FrameRect(uint32_t aWhichFrame)
|
||||
return nsIntRect();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
RasterImage::GetCurrentFrameIndex()
|
||||
{
|
||||
return GetCurrentImgFrameIndex();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
RasterImage::GetNumFrames() const
|
||||
{
|
||||
@ -842,10 +841,13 @@ RasterImage::CopyFrame(uint32_t aWhichFrame,
|
||||
// Get the frame. If it's not there, it's probably the caller's fault for
|
||||
// not waiting for the data to be loaded from the network or not passing
|
||||
// FLAG_SYNC_DECODE
|
||||
uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
|
||||
0 : GetCurrentImgFrameIndex();
|
||||
nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
|
||||
if (!frame) {
|
||||
DrawableFrameRef frameRef = GetFrame(GetRequestedFrameIndex(aWhichFrame));
|
||||
if (!frameRef) {
|
||||
// The OS threw this frame away.
|
||||
if (aFlags & FLAG_SYNC_DECODE) {
|
||||
ForceDiscard();
|
||||
return CopyFrame(aWhichFrame, aFlags);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -872,16 +874,16 @@ RasterImage::CopyFrame(uint32_t aWhichFrame,
|
||||
mapping.mStride,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
|
||||
nsIntRect intframerect = frame->GetRect();
|
||||
Rect rect(intframerect.x, intframerect.y,
|
||||
intframerect.width, intframerect.height);
|
||||
if (frame->IsSinglePixel()) {
|
||||
target->FillRect(rect, ColorPattern(frame->SinglePixelColor()),
|
||||
nsIntRect intFrameRect = frameRef->GetRect();
|
||||
Rect rect(intFrameRect.x, intFrameRect.y,
|
||||
intFrameRect.width, intFrameRect.height);
|
||||
if (frameRef->IsSinglePixel()) {
|
||||
target->FillRect(rect, ColorPattern(frameRef->SinglePixelColor()),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
} else {
|
||||
RefPtr<SourceSurface> srcsurf = frame->GetSurface();
|
||||
Rect srcrect(0, 0, intframerect.width, intframerect.height);
|
||||
target->DrawSurface(srcsurf, srcrect, rect);
|
||||
RefPtr<SourceSurface> srcSurf = frameRef->GetSurface();
|
||||
Rect srcRect(0, 0, intFrameRect.width, intFrameRect.height);
|
||||
target->DrawSurface(srcSurf, srcRect, rect);
|
||||
}
|
||||
|
||||
target->Flush();
|
||||
@ -921,46 +923,34 @@ RasterImage::GetFrame(uint32_t aWhichFrame,
|
||||
// Get the frame. If it's not there, it's probably the caller's fault for
|
||||
// not waiting for the data to be loaded from the network or not passing
|
||||
// FLAG_SYNC_DECODE
|
||||
uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
|
||||
0 : GetCurrentImgFrameIndex();
|
||||
nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
|
||||
if (!frame) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> framesurf;
|
||||
|
||||
// If this frame covers the entire image, we can just reuse its existing
|
||||
// surface.
|
||||
nsIntRect framerect = frame->GetRect();
|
||||
if (framerect.x == 0 && framerect.y == 0 &&
|
||||
framerect.width == mSize.width &&
|
||||
framerect.height == mSize.height) {
|
||||
framesurf = frame->GetSurface();
|
||||
if (!framesurf && !frame->IsSinglePixel()) {
|
||||
// No reason to be optimized away here - the OS threw out the data
|
||||
if (!(aFlags & FLAG_SYNC_DECODE))
|
||||
return nullptr;
|
||||
|
||||
// Unconditionally call ForceDiscard() here because GetSurface can only
|
||||
// return null when we can forcibly discard and redecode. There are two
|
||||
// other cases where GetSurface() can return null - when it is a single
|
||||
// pixel image, which we check before getting here, or when this is an
|
||||
// indexed image, in which case we shouldn't be in this function at all.
|
||||
// The only remaining possibility is that SetDiscardable() was called on
|
||||
// this imgFrame, which implies the image can be redecoded.
|
||||
DrawableFrameRef frameRef = GetFrame(GetRequestedFrameIndex(aWhichFrame));
|
||||
if (!frameRef) {
|
||||
// The OS threw this frame away. We'll request a redecode.
|
||||
if (aFlags & FLAG_SYNC_DECODE) {
|
||||
ForceDiscard();
|
||||
return GetFrame(aWhichFrame, aFlags);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The image doesn't have a surface because it's been optimized away. Create
|
||||
// one.
|
||||
if (!framesurf) {
|
||||
framesurf = CopyFrame(aWhichFrame, aFlags);
|
||||
|
||||
// If this frame covers the entire image, we can just reuse its existing
|
||||
// surface.
|
||||
RefPtr<SourceSurface> frameSurf;
|
||||
nsIntRect frameRect = frameRef->GetRect();
|
||||
if (frameRect.x == 0 && frameRect.y == 0 &&
|
||||
frameRect.width == mSize.width &&
|
||||
frameRect.height == mSize.height) {
|
||||
frameSurf = frameRef->GetSurface();
|
||||
}
|
||||
|
||||
return framesurf;
|
||||
// The image doesn't have a usable surface because it's been optimized away or
|
||||
// because it's a partial update frame from an animation. Create one.
|
||||
if (!frameSurf) {
|
||||
frameSurf = CopyFrame(aWhichFrame, aFlags);
|
||||
}
|
||||
|
||||
return frameSurf;
|
||||
}
|
||||
|
||||
already_AddRefed<layers::Image>
|
||||
@ -1183,7 +1173,8 @@ RasterImage::InternalAddFrame(uint32_t framenum,
|
||||
|
||||
nsRefPtr<imgFrame> frame(new imgFrame());
|
||||
|
||||
nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
|
||||
nsIntRect frameRect(aX, aY, aWidth, aHeight);
|
||||
nsresult rv = frame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
|
||||
if (!(mSize.width > 0 && mSize.height > 0))
|
||||
NS_WARNING("Shouldn't call InternalAddFrame with zero size");
|
||||
if (!NS_SUCCEEDED(rv))
|
||||
@ -1367,7 +1358,8 @@ RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
|
||||
|
||||
mFrameBlender.RemoveFrame(aFrameNum);
|
||||
nsRefPtr<imgFrame> newFrame(new imgFrame());
|
||||
nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
|
||||
nsIntRect frameRect(aX, aY, aWidth, aHeight);
|
||||
nsresult rv = newFrame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return InternalAddFrameHelper(aFrameNum, newFrame, imageData, imageLength,
|
||||
paletteData, paletteLength, aRetFrame);
|
||||
@ -1490,9 +1482,10 @@ RasterImage::StartAnimation()
|
||||
|
||||
EnsureAnimExists();
|
||||
|
||||
nsRefPtr<imgFrame> currentFrame = GetCurrentImgFrame();
|
||||
nsRefPtr<imgFrame> currentFrame = GetFrameNoDecode(GetCurrentFrameIndex());
|
||||
// A timeout of -1 means we should display this frame forever.
|
||||
if (currentFrame && mFrameBlender.GetTimeoutForFrame(GetCurrentImgFrameIndex()) < 0) {
|
||||
if (currentFrame &&
|
||||
mFrameBlender.GetTimeoutForFrame(GetCurrentFrameIndex()) < 0) {
|
||||
mAnimationFinished = true;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
@ -2636,67 +2629,56 @@ RasterImage::RequestScale(imgFrame* aFrame, nsIntSize aSize)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
void
|
||||
RasterImage::DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
||||
gfxContext *aContext,
|
||||
const nsIntSize& aSize,
|
||||
const ImageRegion& aRegion,
|
||||
GraphicsFilter aFilter,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
nsRefPtr<imgFrame> frame = aFrame;
|
||||
nsIntRect framerect = frame->GetRect();
|
||||
|
||||
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
|
||||
RefPtr<SourceSurface> surf;
|
||||
DrawableFrameRef frameRef;
|
||||
gfx::Size scale(double(aSize.width) / mSize.width,
|
||||
double(aSize.height) / mSize.height);
|
||||
|
||||
if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
|
||||
// If scale factor is still the same that we scaled for and
|
||||
// ScaleWorker isn't still working, then we can use pre-downscaled frame.
|
||||
// If scale factor has changed, order new request.
|
||||
if (CanScale(aFilter, scale, aFlags) && !aFrameRef->IsSinglePixel()) {
|
||||
// FIXME: Current implementation doesn't support pre-downscale
|
||||
// mechanism for multiple sizes from same src, since we cache
|
||||
// pre-downscaled frame only for the latest requested scale.
|
||||
// The solution is to cache more than one scaled image frame
|
||||
// for each RasterImage.
|
||||
bool needScaleReq;
|
||||
if (mScaleResult.status == SCALE_DONE && mScaleResult.scaledSize == aSize) {
|
||||
// Grab and hold the surface to make sure the OS didn't destroy it
|
||||
surf = mScaleResult.frame->GetSurface();
|
||||
needScaleReq = !surf;
|
||||
if (surf) {
|
||||
frame = mScaleResult.frame;
|
||||
}
|
||||
} else {
|
||||
needScaleReq = !(mScaleResult.status == SCALE_PENDING &&
|
||||
mScaleResult.scaledSize == aSize);
|
||||
frameRef = mScaleResult.frame->DrawableRef();
|
||||
}
|
||||
|
||||
// If we're not waiting for exactly this result, and there's only one
|
||||
// instance of this image on this page, ask for a scale.
|
||||
if (needScaleReq) {
|
||||
RequestScale(frame, aSize);
|
||||
if (!frameRef &&
|
||||
(mScaleResult.status != SCALE_PENDING || mScaleResult.scaledSize != aSize)) {
|
||||
// We either didn't have a complete scaled frame, it didn't match, or the
|
||||
// OS threw it away. Fall back to aFrame, and request a new scaled frame
|
||||
// if we're not already working on the one we need.
|
||||
RequestScale(aFrameRef.get(), aSize);
|
||||
}
|
||||
}
|
||||
|
||||
gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
|
||||
ImageRegion region(aRegion);
|
||||
if (!frameRef) {
|
||||
frameRef = Move(aFrameRef);
|
||||
}
|
||||
|
||||
// By now we may have a frame with the requested size. If not, we need to
|
||||
// adjust the drawing parameters accordingly.
|
||||
nsIntSize finalFrameSize(frame->GetRect().Size());
|
||||
if (finalFrameSize != aSize) {
|
||||
nsIntRect finalFrameRect = frameRef->GetRect();
|
||||
if (finalFrameRect.Size() != aSize) {
|
||||
aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
|
||||
region.Scale(1.0 / scale.width, 1.0 / scale.height);
|
||||
}
|
||||
|
||||
nsIntMargin padding(framerect.y,
|
||||
mSize.width - framerect.XMost(),
|
||||
mSize.height - framerect.YMost(),
|
||||
framerect.x);
|
||||
nsIntMargin padding(finalFrameRect.y,
|
||||
mSize.width - finalFrameRect.XMost(),
|
||||
mSize.height - finalFrameRect.YMost(),
|
||||
finalFrameRect.x);
|
||||
|
||||
return frame->Draw(aContext, region, padding, aFilter, aFlags);
|
||||
frameRef->Draw(aContext, region, padding, aFilter, aFlags);
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
@ -2779,21 +2761,12 @@ RasterImage::Draw(gfxContext* aContext,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
|
||||
: GetCurrentImgFrameIndex();
|
||||
nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
|
||||
if (!frame) {
|
||||
DrawableFrameRef ref = GetFrame(GetRequestedFrameIndex(aWhichFrame));
|
||||
if (!ref) {
|
||||
return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
|
||||
}
|
||||
|
||||
bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aSize,
|
||||
aRegion, aFilter, aFlags);
|
||||
if (!drawn) {
|
||||
// The OS threw out some or all of our buffer. Start decoding again.
|
||||
ForceDiscard();
|
||||
WantDecodedFrames();
|
||||
return NS_OK;
|
||||
}
|
||||
DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize, aRegion, aFilter, aFlags);
|
||||
|
||||
if (mDecoded && !mDrawStartTime.IsNull()) {
|
||||
TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
|
||||
@ -3709,12 +3682,9 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
||||
}
|
||||
|
||||
// If there's only one instance of this image on this page, ask for a scale.
|
||||
uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
|
||||
: GetCurrentImgFrameIndex();
|
||||
|
||||
nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
|
||||
if (frame) {
|
||||
RequestScale(frame, destSize);
|
||||
DrawableFrameRef frameRef = GetFrame(GetRequestedFrameIndex(aWhichFrame));
|
||||
if (frameRef) {
|
||||
RequestScale(frameRef.get(), destSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,10 +169,6 @@ public:
|
||||
uint32_t aToOffset, uint32_t aCount,
|
||||
uint32_t* aWriteCount);
|
||||
|
||||
/* The index of the current frame that would be drawn if the image was to be
|
||||
* drawn now. */
|
||||
uint32_t GetCurrentFrameIndex();
|
||||
|
||||
/* The total number of frames in this image. */
|
||||
uint32_t GetNumFrames() const;
|
||||
|
||||
@ -557,8 +553,8 @@ private:
|
||||
nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
|
||||
DecodeRequest* request = nullptr);
|
||||
|
||||
bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
gfxContext *aContext,
|
||||
void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
|
||||
gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
const ImageRegion& aRegion,
|
||||
GraphicsFilter aFilter,
|
||||
@ -567,21 +563,10 @@ private:
|
||||
TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
|
||||
uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* Deletes and nulls out the frame in mFrames[framenum].
|
||||
*
|
||||
* Does not change the size of mFrames.
|
||||
*
|
||||
* @param framenum The index of the frame to be deleted.
|
||||
* Must lie in [0, mFrames.Length() )
|
||||
*/
|
||||
void DeleteImgFrame(uint32_t framenum);
|
||||
|
||||
already_AddRefed<imgFrame> GetImgFrameNoDecode(uint32_t framenum);
|
||||
already_AddRefed<imgFrame> GetImgFrame(uint32_t framenum);
|
||||
already_AddRefed<imgFrame> GetDrawableImgFrame(uint32_t framenum);
|
||||
already_AddRefed<imgFrame> GetCurrentImgFrame();
|
||||
uint32_t GetCurrentImgFrameIndex() const;
|
||||
already_AddRefed<imgFrame> GetFrameNoDecode(uint32_t aFrameNum);
|
||||
DrawableFrameRef GetFrame(uint32_t aFrameNum);
|
||||
uint32_t GetCurrentFrameIndex() const;
|
||||
uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
|
||||
|
||||
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
|
||||
MallocSizeOf aMallocSizeOf) const;
|
||||
|
@ -12,14 +12,15 @@
|
||||
#include <algorithm>
|
||||
#include "mozilla/Attributes.h" // for MOZ_THIS_IN_INITIALIZER_LIST
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxPattern.h" // Workaround for flaw in bug 921753 part 2.
|
||||
#include "gfxDrawable.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "imgFrame.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsExpirationTracker.h"
|
||||
#include "nsHashKeys.h"
|
||||
@ -117,7 +118,7 @@ class CachedSurface
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(CachedSurface)
|
||||
|
||||
CachedSurface(SourceSurface* aSurface,
|
||||
CachedSurface(imgFrame* aSurface,
|
||||
const IntSize aTargetSize,
|
||||
const Cost aCost,
|
||||
const ImageKey aImageKey,
|
||||
@ -132,11 +133,9 @@ public:
|
||||
MOZ_ASSERT(mImageKey, "Must have a valid image key");
|
||||
}
|
||||
|
||||
already_AddRefed<gfxDrawable> Drawable() const
|
||||
DrawableFrameRef DrawableRef() const
|
||||
{
|
||||
nsRefPtr<gfxDrawable> drawable =
|
||||
new gfxSurfaceDrawable(mSurface, ThebesIntSize(mTargetSize));
|
||||
return drawable.forget();
|
||||
return mSurface->DrawableRef();
|
||||
}
|
||||
|
||||
ImageKey GetImageKey() const { return mImageKey; }
|
||||
@ -145,12 +144,12 @@ public:
|
||||
nsExpirationState* GetExpirationState() { return &mExpirationState; }
|
||||
|
||||
private:
|
||||
nsExpirationState mExpirationState;
|
||||
nsRefPtr<SourceSurface> mSurface;
|
||||
const IntSize mTargetSize;
|
||||
const Cost mCost;
|
||||
const ImageKey mImageKey;
|
||||
const SurfaceKey mSurfaceKey;
|
||||
nsExpirationState mExpirationState;
|
||||
nsRefPtr<imgFrame> mSurface;
|
||||
const IntSize mTargetSize;
|
||||
const Cost mCost;
|
||||
const ImageKey mImageKey;
|
||||
const SurfaceKey mSurfaceKey;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -240,14 +239,14 @@ public:
|
||||
RegisterWeakMemoryReporter(this);
|
||||
}
|
||||
|
||||
void Insert(SourceSurface* aSurface,
|
||||
void Insert(imgFrame* aSurface,
|
||||
IntSize aTargetSize,
|
||||
const Cost aCost,
|
||||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey).take(),
|
||||
"Inserting a duplicate drawable into the SurfaceCache");
|
||||
MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey),
|
||||
"Inserting a duplicate surface into the SurfaceCache");
|
||||
|
||||
// If this is bigger than the maximum cache size, refuse to cache it.
|
||||
if (!CanHold(aCost))
|
||||
@ -317,19 +316,27 @@ public:
|
||||
MOZ_ASSERT(mAvailableCost <= mMaxCost, "More available cost than we started with");
|
||||
}
|
||||
|
||||
already_AddRefed<gfxDrawable> Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
DrawableFrameRef Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache)
|
||||
return nullptr; // No cached surfaces for this image.
|
||||
|
||||
return DrawableFrameRef(); // No cached surfaces for this image.
|
||||
|
||||
nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
|
||||
if (!surface)
|
||||
return nullptr; // Lookup in the per-image cache missed.
|
||||
|
||||
return DrawableFrameRef(); // Lookup in the per-image cache missed.
|
||||
|
||||
DrawableFrameRef ref = surface->DrawableRef();
|
||||
if (!ref) {
|
||||
// The surface was released by the operating system. Remove the cache
|
||||
// entry as well.
|
||||
Remove(surface);
|
||||
return DrawableFrameRef();
|
||||
}
|
||||
|
||||
mExpirationTracker.MarkUsed(surface);
|
||||
return surface->Drawable();
|
||||
return ref;
|
||||
}
|
||||
|
||||
bool CanHold(const Cost aCost) const
|
||||
@ -497,7 +504,7 @@ SurfaceCache::Shutdown()
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<gfxDrawable>
|
||||
/* static */ DrawableFrameRef
|
||||
SurfaceCache::Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
@ -508,7 +515,7 @@ SurfaceCache::Lookup(const ImageKey aImageKey,
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SurfaceCache::Insert(SourceSurface* aSurface,
|
||||
SurfaceCache::Insert(imgFrame* aSurface,
|
||||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
|
@ -4,7 +4,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* SurfaceCache is a service for caching temporary surfaces in imagelib.
|
||||
* SurfaceCache is a service for caching temporary surfaces and decoded image
|
||||
* data in imagelib.
|
||||
*/
|
||||
|
||||
#ifndef MOZILLA_IMAGELIB_SURFACECACHE_H_
|
||||
@ -15,19 +16,15 @@
|
||||
#include "gfxPoint.h" // for gfxSize
|
||||
#include "nsCOMPtr.h" // for already_AddRefed
|
||||
#include "mozilla/gfx/Point.h" // for mozilla::gfx::IntSize
|
||||
#include "mozilla/gfx/2D.h" // for SourceSurface
|
||||
#include "SVGImageContext.h" // for SVGImageContext
|
||||
|
||||
class gfxDrawable;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class DrawTarget;
|
||||
} // namespace gfx
|
||||
|
||||
namespace image {
|
||||
|
||||
class DrawableFrameRef;
|
||||
class Image;
|
||||
class imgFrame;
|
||||
|
||||
/*
|
||||
* ImageKey contains the information we need to look up all cached surfaces for
|
||||
@ -90,6 +87,11 @@ private:
|
||||
* surfaces. Surfaces expire from the cache automatically if they go too long
|
||||
* without being accessed.
|
||||
*
|
||||
* SurfaceCache does not hold surfaces directly; instead, it holds imgFrame
|
||||
* objects, which hold surfaces but also layer on additional features specific
|
||||
* to imagelib's needs like animation, padding support, and transparent support
|
||||
* for volatile buffers.
|
||||
*
|
||||
* SurfaceCache is not thread-safe; it should only be accessed from the main
|
||||
* thread.
|
||||
*/
|
||||
@ -108,27 +110,33 @@ struct SurfaceCache
|
||||
static void Shutdown();
|
||||
|
||||
/*
|
||||
* Look up a surface in the cache.
|
||||
* Look up the imgFrame containing a surface in the cache and returns a
|
||||
* drawable reference to that imgFrame.
|
||||
*
|
||||
* If the imgFrame was found in the cache, but had stored its surface in a
|
||||
* volatile buffer which was discarded by the OS, then it is automatically
|
||||
* removed from the cache and an empty DrawableFrameRef is returned.
|
||||
*
|
||||
* @param aImageKey Key data identifying which image the surface belongs to.
|
||||
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
|
||||
*
|
||||
* @return the requested surface, or nullptr if not found.
|
||||
* @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
|
||||
* or an empty DrawableFrameRef if not found.
|
||||
*/
|
||||
static already_AddRefed<gfxDrawable> Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
static DrawableFrameRef Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
|
||||
/*
|
||||
* Insert a surface into the cache. It is an error to call this function
|
||||
* without first calling Lookup to verify that the surface is not already in
|
||||
* the cache.
|
||||
*
|
||||
* @param aTarget The new surface (in the form of a DrawTarget) to insert
|
||||
* into the cache.
|
||||
* @param aTarget The new surface (wrapped in an imgFrame) to insert into
|
||||
* the cache.
|
||||
* @param aImageKey Key data identifying which image the surface belongs to.
|
||||
* @param aSurfaceKey Key data which uniquely identifies the requested surface.
|
||||
*/
|
||||
static void Insert(gfx::SourceSurface* aSurface,
|
||||
static void Insert(imgFrame* aSurface,
|
||||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "imgDecoderObserver.h"
|
||||
#include "imgFrame.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/SVGSVGElement.h"
|
||||
@ -842,27 +843,30 @@ VectorImage::Draw(gfxContext* aContext,
|
||||
aSVGContext, animTime, aFlags);
|
||||
|
||||
if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
|
||||
CreateDrawableAndShow(params);
|
||||
CreateSurfaceAndShow(params);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxDrawable> drawable =
|
||||
DrawableFrameRef frameRef =
|
||||
SurfaceCache::Lookup(ImageKey(this),
|
||||
SurfaceKey(params.size, aSVGContext,
|
||||
animTime, aFlags));
|
||||
|
||||
// Draw.
|
||||
if (drawable) {
|
||||
Show(drawable, params);
|
||||
if (frameRef) {
|
||||
RefPtr<SourceSurface> surface = frameRef->GetSurface();
|
||||
nsRefPtr<gfxDrawable> svgDrawable =
|
||||
new gfxSurfaceDrawable(surface, ThebesIntSize(frameRef->GetSize()));
|
||||
Show(svgDrawable, params);
|
||||
} else {
|
||||
CreateDrawableAndShow(params);
|
||||
CreateSurfaceAndShow(params);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
|
||||
VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams)
|
||||
{
|
||||
mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
|
||||
mSVGDocumentWrapper->FlushImageTransformInvalidation();
|
||||
@ -885,38 +889,32 @@ VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams)
|
||||
if (bypassCache)
|
||||
return Show(svgDrawable, aParams);
|
||||
|
||||
// Try to create an offscreen surface.
|
||||
RefPtr<gfx::DrawTarget> target =
|
||||
gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(aParams.size,
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
// Try to create an imgFrame, initializing the surface it contains by drawing
|
||||
// our gfxDrawable into it. (We use FILTER_NEAREST since we never scale here.)
|
||||
nsRefPtr<imgFrame> frame = new imgFrame;
|
||||
nsresult rv =
|
||||
frame->InitWithDrawable(svgDrawable, ThebesIntSize(aParams.size),
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
GraphicsFilter::FILTER_NEAREST, aParams.flags);
|
||||
|
||||
// If we couldn't create the draw target, it was probably because it would end
|
||||
// If we couldn't create the frame, it was probably because it would end
|
||||
// up way too big. Generally it also wouldn't fit in the cache, but the prefs
|
||||
// could be set such that the cache isn't the limiting factor.
|
||||
if (!target)
|
||||
if (NS_FAILED(rv))
|
||||
return Show(svgDrawable, aParams);
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(target);
|
||||
// Take a strong reference to the frame's surface and make sure it hasn't
|
||||
// already been purged by the operating system.
|
||||
RefPtr<SourceSurface> surface = frame->GetSurface();
|
||||
if (!surface)
|
||||
return Show(svgDrawable, aParams);
|
||||
|
||||
// Actually draw. (We use FILTER_NEAREST since we never scale here.)
|
||||
nsIntRect imageRect(ThebesIntRect(aParams.imageRect));
|
||||
gfxUtils::DrawPixelSnapped(ctx, svgDrawable,
|
||||
ThebesIntSize(aParams.size),
|
||||
ImageRegion::Create(imageRect),
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
GraphicsFilter::FILTER_NEAREST, aParams.flags);
|
||||
|
||||
RefPtr<SourceSurface> surface = target->Snapshot();
|
||||
|
||||
// Attempt to cache the resulting surface.
|
||||
SurfaceCache::Insert(surface, ImageKey(this),
|
||||
// Attempt to cache the frame.
|
||||
SurfaceCache::Insert(frame, ImageKey(this),
|
||||
SurfaceKey(aParams.size, aParams.svgContext,
|
||||
aParams.animationTime, aParams.flags));
|
||||
|
||||
// Draw. Note that if SurfaceCache::Insert failed for whatever reason,
|
||||
// then |target| is all that is keeping the pixel data alive, so we have
|
||||
// to draw before returning from this function.
|
||||
// Draw.
|
||||
nsRefPtr<gfxDrawable> drawable =
|
||||
new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.size));
|
||||
Show(drawable, aParams);
|
||||
|
@ -86,7 +86,7 @@ protected:
|
||||
virtual nsresult StopAnimation();
|
||||
virtual bool ShouldAnimate();
|
||||
|
||||
void CreateDrawableAndShow(const SVGDrawingParameters& aParams);
|
||||
void CreateSurfaceAndShow(const SVGDrawingParameters& aParams);
|
||||
void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
|
||||
|
||||
private:
|
||||
|
@ -143,17 +143,20 @@ imgFrame::~imgFrame()
|
||||
}
|
||||
}
|
||||
|
||||
nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
|
||||
SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
|
||||
nsresult
|
||||
imgFrame::InitForDecoder(const nsIntRect& aRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth /* = 0 */)
|
||||
{
|
||||
// assert for properties that should be verified by decoders, warn for properties related to bad content
|
||||
if (!AllowedImageSize(aWidth, aHeight)) {
|
||||
// Assert for properties that should be verified by decoders,
|
||||
// warn for properties related to bad content.
|
||||
if (!AllowedImageSize(aRect.width, aRect.height)) {
|
||||
NS_WARNING("Should have legal image size");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mOffset.MoveTo(aX, aY);
|
||||
mSize.SizeTo(aWidth, aHeight);
|
||||
mOffset.MoveTo(aRect.x, aRect.y);
|
||||
mSize.SizeTo(aRect.width, aRect.height);
|
||||
|
||||
mFormat = aFormat;
|
||||
mPaletteDepth = aPaletteDepth;
|
||||
@ -172,32 +175,121 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
|
||||
NS_WARNING("moz_malloc for paletted image data should succeed");
|
||||
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
|
||||
} else {
|
||||
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitForDecoder() twice?");
|
||||
|
||||
// Inform the discard tracker that we are going to allocate some memory.
|
||||
if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) {
|
||||
NS_WARNING("Exceed the hard limit of decode image size");
|
||||
mInformedDiscardTracker =
|
||||
DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
|
||||
if (!mInformedDiscardTracker) {
|
||||
NS_WARNING("Exceeded the image decode size hard limit");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!mImageSurface) {
|
||||
mVBuf = AllocateBufferForImage(mSize, mFormat);
|
||||
if (!mVBuf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (mVBuf->OnHeap()) {
|
||||
int32_t stride = VolatileSurfaceStride(mSize, mFormat);
|
||||
VolatileBufferPtr<uint8_t> ptr(mVBuf);
|
||||
memset(ptr, 0, stride * mSize.height);
|
||||
}
|
||||
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
|
||||
mVBuf = AllocateBufferForImage(mSize, mFormat);
|
||||
if (!mVBuf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (mVBuf->OnHeap()) {
|
||||
int32_t stride = VolatileSurfaceStride(mSize, mFormat);
|
||||
VolatileBufferPtr<uint8_t> ptr(mVBuf);
|
||||
memset(ptr, 0, stride * mSize.height);
|
||||
}
|
||||
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
|
||||
|
||||
if (!mImageSurface) {
|
||||
NS_WARNING("Failed to create VolatileDataSourceSurface");
|
||||
// Image surface allocation is failed, need to return
|
||||
// the booked buffer size.
|
||||
DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mInformedDiscardTracker = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
imgFrame::InitWithDrawable(gfxDrawable* aDrawable,
|
||||
const nsIntSize& aSize,
|
||||
const SurfaceFormat aFormat,
|
||||
GraphicsFilter aFilter,
|
||||
uint32_t aImageFlags)
|
||||
{
|
||||
// Assert for properties that should be verified by decoders,
|
||||
// warn for properties related to bad content.
|
||||
if (!AllowedImageSize(aSize.width, aSize.height)) {
|
||||
NS_WARNING("Should have legal image size");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mOffset.MoveTo(0, 0);
|
||||
mSize.SizeTo(aSize.width, aSize.height);
|
||||
|
||||
mFormat = aFormat;
|
||||
mPaletteDepth = 0;
|
||||
|
||||
// Inform the discard tracker that we are going to allocate some memory.
|
||||
mInformedDiscardTracker =
|
||||
DiscardTracker::TryAllocation(4 * mSize.width * mSize.height);
|
||||
if (!mInformedDiscardTracker) {
|
||||
NS_WARNING("Exceed the image decode size hard limit");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> target;
|
||||
|
||||
bool canUseDataSurface =
|
||||
gfxPlatform::GetPlatform()->CanRenderContentToDataSurface();
|
||||
|
||||
if (canUseDataSurface) {
|
||||
// It's safe to use data surfaces for content on this platform, so we can
|
||||
// get away with using volatile buffers.
|
||||
MOZ_ASSERT(!mImageSurface, "Called imgFrame::InitWithDrawable() twice?");
|
||||
|
||||
mVBuf = AllocateBufferForImage(mSize, mFormat);
|
||||
if (!mVBuf) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
int32_t stride = VolatileSurfaceStride(mSize, mFormat);
|
||||
VolatileBufferPtr<uint8_t> ptr(mVBuf);
|
||||
if (!ptr) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (mVBuf->OnHeap()) {
|
||||
memset(ptr, 0, stride * mSize.height);
|
||||
}
|
||||
mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
|
||||
|
||||
target = gfxPlatform::GetPlatform()->
|
||||
CreateDrawTargetForData(ptr, mSize, stride, mFormat);
|
||||
} else {
|
||||
// We can't use data surfaces for content, so we'll create an offscreen
|
||||
// surface instead. This means if someone later calls RawAccessRef(), we
|
||||
// may have to do an expensive readback, but we warned callers about that in
|
||||
// the documentation for this method.
|
||||
MOZ_ASSERT(!mOptSurface, "Called imgFrame::InitWithDrawable() twice?");
|
||||
|
||||
target = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(mSize, mFormat);
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Draw using the drawable the caller provided.
|
||||
nsIntRect imageRect(0, 0, mSize.width, mSize.height);
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(target);
|
||||
gfxUtils::DrawPixelSnapped(ctx, aDrawable, ThebesIntSize(mSize),
|
||||
ImageRegion::Create(imageRect),
|
||||
mFormat, aFilter, aImageFlags);
|
||||
|
||||
if (canUseDataSurface && !mImageSurface) {
|
||||
NS_WARNING("Failed to create VolatileDataSourceSurface");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!canUseDataSurface) {
|
||||
// We used an offscreen surface, which is an "optimized" surface from
|
||||
// imgFrame's perspective.
|
||||
mOptSurface = target->Snapshot();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -36,7 +36,44 @@ public:
|
||||
|
||||
imgFrame();
|
||||
|
||||
nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
|
||||
/**
|
||||
* Initialize this imgFrame with an empty surface and prepare it for being
|
||||
* written to by a decoder.
|
||||
*
|
||||
* This is appropriate for use with decoded images, but it should not be used
|
||||
* when drawing content into an imgFrame, as it may use a different graphics
|
||||
* backend than normal content drawing.
|
||||
*/
|
||||
nsresult InitForDecoder(const nsIntRect& aRect,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0);
|
||||
|
||||
nsresult InitForDecoder(const nsIntSize& aSize,
|
||||
SurfaceFormat aFormat,
|
||||
uint8_t aPaletteDepth = 0)
|
||||
{
|
||||
return InitForDecoder(nsIntRect(0, 0, aSize.width, aSize.height),
|
||||
aFormat, aPaletteDepth);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize this imgFrame with a new surface and draw the provided
|
||||
* gfxDrawable into it.
|
||||
*
|
||||
* This is appropriate to use when drawing content into an imgFrame, as it
|
||||
* uses the same graphics backend as normal content drawing. The downside is
|
||||
* that the underlying surface may not be stored in a volatile buffer on all
|
||||
* platforms, and raw access to the surface (using RawAccessRef() or
|
||||
* LockImageData()) may be much more expensive than in the InitForDecoder()
|
||||
* case.
|
||||
*/
|
||||
nsresult InitWithDrawable(gfxDrawable* aDrawable,
|
||||
const nsIntSize& aSize,
|
||||
const SurfaceFormat aFormat,
|
||||
GraphicsFilter aFilter,
|
||||
uint32_t aImageFlags);
|
||||
|
||||
nsresult Optimize();
|
||||
|
||||
DrawableFrameRef DrawableRef();
|
||||
|
@ -75,6 +75,7 @@ class LIRGeneratorNone : public LIRGeneratorShared
|
||||
LTableSwitchV *newLTableSwitchV(MTableSwitch *) { MOZ_CRASH(); }
|
||||
bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins) { MOZ_CRASH(); }
|
||||
bool visitSimdSplatX4(MSimdSplatX4 *ins) { MOZ_CRASH(); }
|
||||
bool visitSimdValueX4(MSimdValueX4 *lir) { MOZ_CRASH(); }
|
||||
};
|
||||
|
||||
typedef LIRGeneratorNone LIRGeneratorSpecific;
|
||||
|
@ -1,9 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<rect x="10" y="10" width="80" height="80" fill="#00ff00"/>
|
||||
<rect x="110" y="10" width="80" height="80" fill="#000000"/>
|
||||
<rect x="210" y="10" width="80" height="80" fill="#ffff00"/>
|
||||
<rect x="310" y="10" width="80" height="80" fill="#000000"/>
|
||||
<rect x="410" y="10" width="80" height="80" fill="#ffff00"/>
|
||||
|
||||
<rect x="0" y="0" width="50" height="50" fill="#ACCC10"/>
|
||||
<rect x="50" y="0" width="50" height="50" fill="#B4B43F"/>
|
||||
<rect x="100" y="0" width="50" height="50" fill="#DFDF3F"/>
|
||||
<rect x="150" y="0" width="50" height="50" fill="#B4B43F"/>
|
||||
<rect x="200" y="0" width="50" height="50" fill="#DFDF3F"/>
|
||||
<rect x="250" y="0" width="50" height="50" fill="#DFB43F"/>
|
||||
<rect x="300" y="0" width="50" height="50" fill="#DFB43F"/>
|
||||
<rect x="350" y="0" width="50" height="50" fill="#DFB43F"/>
|
||||
<rect x="0" y="50" width="50" height="50" fill="#E0B440"/>
|
||||
<rect x="50" y="50" width="50" height="50" fill="#DFB43F"/>
|
||||
<rect x="100" y="50" width="50" height="50" fill="#DFDF3F"/>
|
||||
<rect x="150" y="50" width="50" height="50" fill="#DFDF3F"/>
|
||||
<rect x="200" y="50" width="50" height="50" fill="#B4CC3F"/>
|
||||
<rect x="250" y="50" width="50" height="50" fill="#DFB43F"/>
|
||||
<rect x="300" y="50" width="50" height="50" fill="#B4CC3F"/>
|
||||
<rect x="350" y="50" width="50" height="50" fill="#DFC88D"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 1011 B |
@ -1,38 +1,104 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
|
||||
x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood flood-color="#ff0000" result="flood"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="flood" x="10%" y="10%" width="80%" height="80%"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
|
||||
<filter id="f0" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="normal"/>
|
||||
</filter>
|
||||
<rect x="0" y="0" width="100" height="100" fill="#00ff00" filter="url(#f1)"/>
|
||||
|
||||
<filter id="f2" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
|
||||
x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood flood-color="#ff0000" result="flood"/>
|
||||
<feBlend mode="multiply" in="SourceGraphic" in2="flood" x="10%" y="10%" width="80%" height="80%"/>
|
||||
<rect x="0" y="0" width="50" height="50" filter="url(#f0)"/>
|
||||
<filter id="f1" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="multiply"/>
|
||||
</filter>
|
||||
<rect x="100" y="0" width="100" height="100" fill="#00ff00" filter="url(#f2)"/>
|
||||
|
||||
<filter id="f3" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
|
||||
x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood flood-color="#ff0000" result="flood"/>
|
||||
<feBlend mode="screen" in="SourceGraphic" in2="flood" x="10%" y="10%" width="80%" height="80%"/>
|
||||
<rect x="50" y="0" width="50" height="50" filter="url(#f1)"/>
|
||||
<filter id="f2" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="screen"/>
|
||||
</filter>
|
||||
<rect x="200" y="0" width="100" height="100" fill="#00ff00" filter="url(#f3)"/>
|
||||
|
||||
<filter id="f4" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
|
||||
x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood flood-color="#ff0000" result="flood"/>
|
||||
<feBlend mode="darken" in="SourceGraphic" in2="flood" x="10%" y="10%" width="80%" height="80%"/>
|
||||
<rect x="100" y="0" width="50" height="50" filter="url(#f2)"/>
|
||||
<filter id="f3" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="darken"/>
|
||||
</filter>
|
||||
<rect x="300" y="0" width="100" height="100" fill="#00ff00" filter="url(#f4)"/>
|
||||
|
||||
<filter id="f5" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
|
||||
x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood flood-color="#ff0000" result="flood"/>
|
||||
<feBlend mode="lighten" in="SourceGraphic" in2="flood" x="10%" y="10%" width="80%" height="80%"/>
|
||||
<rect x="150" y="0" width="50" height="50" filter="url(#f3)"/>
|
||||
<filter id="f4" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="lighten"/>
|
||||
</filter>
|
||||
<rect x="400" y="0" width="100" height="100" fill="#00ff00" filter="url(#f5)"/>
|
||||
|
||||
</svg>
|
||||
<rect x="200" y="0" width="50" height="50" filter="url(#f4)"/>
|
||||
<filter id="f5" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="overlay"/>
|
||||
</filter>
|
||||
<rect x="250" y="0" width="50" height="50" filter="url(#f5)"/>
|
||||
<filter id="f6" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="color-dodge"/>
|
||||
</filter>
|
||||
<rect x="300" y="0" width="50" height="50" filter="url(#f6)"/>
|
||||
<filter id="f7" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="color-burn"/>
|
||||
</filter>
|
||||
<rect x="350" y="0" width="50" height="50" filter="url(#f7)"/>
|
||||
<filter id="f8" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="hard-light"/>
|
||||
</filter>
|
||||
<rect x="0" y="0" width="50" height="50" filter="url(#f8)"/>
|
||||
<filter id="f9" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="soft-light"/>
|
||||
</filter>
|
||||
<rect x="50" y="50" width="50" height="50" filter="url(#f9)"/>
|
||||
<filter id="f10" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="difference"/>
|
||||
</filter>
|
||||
<rect x="100" y="50" width="50" height="50" filter="url(#f10)"/>
|
||||
<filter id="f11" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="exclusion"/>
|
||||
</filter>
|
||||
<rect x="150" y="50" width="50" height="50" filter="url(#f11)"/>
|
||||
<filter id="f12" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="hue"/>
|
||||
</filter>
|
||||
<rect x="200" y="50" width="50" height="50" filter="url(#f12)"/>
|
||||
<filter id="f13" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="saturation"/>
|
||||
</filter>
|
||||
<rect x="250" y="50" width="50" height="50" filter="url(#f13)"/>
|
||||
<filter id="f14" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="color"/>
|
||||
</filter>
|
||||
<rect x="300" y="50" width="50" height="50" filter="url(#f14)"/>
|
||||
<filter id="f15" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="luminosity"/>
|
||||
</filter>
|
||||
<rect x="350" y="50" width="50" height="50" filter="url(#f15)"/>
|
||||
<filter id="f16" x="0%" y="0%" width="100%" height="100%">
|
||||
<feFlood result="a" flood-color="rgb(255,0,0)" flood-opacity="0.5"/>
|
||||
<feFlood result="b" flood-color="rgb(0,255,0)" flood-opacity="0.5"/>
|
||||
<feBlend in="a" in2="b" mode="undefined"/>
|
||||
</filter>
|
||||
<rect x="0" y="50" width="50" height="50" filter="url(#f16)"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 5.4 KiB |
@ -18,7 +18,7 @@ include svg-filter-chains/reftest.list
|
||||
== dynamic-filter-invalidation-01.svg pass.svg
|
||||
== dynamic-filter-invalidation-02.svg pass.svg
|
||||
|
||||
== feBlend-1.svg feBlend-1-ref.svg
|
||||
fuzzy(1,40000) == feBlend-1.svg feBlend-1-ref.svg
|
||||
== feBlend-2.svg feBlend-2-ref.svg
|
||||
|
||||
fuzzy-if(d2d,1,6400) == feColorMatrix-1.svg feColorMatrix-1-ref.svg
|
||||
|
@ -84,8 +84,10 @@ CSS_KEY(-moz-isolate, _moz_isolate)
|
||||
CSS_KEY(-moz-isolate-override, _moz_isolate_override)
|
||||
CSS_KEY(-moz-left, _moz_left)
|
||||
CSS_KEY(-moz-list, _moz_list)
|
||||
CSS_KEY(-moz-mac-buttonactivetext, _moz_mac_buttonactivetext)
|
||||
CSS_KEY(-moz-mac-chrome-active, _moz_mac_chrome_active)
|
||||
CSS_KEY(-moz-mac-chrome-inactive, _moz_mac_chrome_inactive)
|
||||
CSS_KEY(-moz-mac-defaultbuttontext, _moz_mac_defaultbuttontext)
|
||||
CSS_KEY(-moz-mac-focusring, _moz_mac_focusring)
|
||||
CSS_KEY(-moz-mac-fullscreen-button, _moz_mac_fullscreen_button)
|
||||
CSS_KEY(-moz-mac-menuselect, _moz_mac_menuselect)
|
||||
|
@ -948,8 +948,10 @@ const KTableValue nsCSSProps::kColorKTable[] = {
|
||||
eCSSKeyword__moz_hyperlinktext, NS_COLOR_MOZ_HYPERLINKTEXT,
|
||||
eCSSKeyword__moz_html_cellhighlight, LookAndFeel::eColorID__moz_html_cellhighlight,
|
||||
eCSSKeyword__moz_html_cellhighlighttext, LookAndFeel::eColorID__moz_html_cellhighlighttext,
|
||||
eCSSKeyword__moz_mac_buttonactivetext, LookAndFeel::eColorID__moz_mac_buttonactivetext,
|
||||
eCSSKeyword__moz_mac_chrome_active, LookAndFeel::eColorID__moz_mac_chrome_active,
|
||||
eCSSKeyword__moz_mac_chrome_inactive, LookAndFeel::eColorID__moz_mac_chrome_inactive,
|
||||
eCSSKeyword__moz_mac_defaultbuttontext, LookAndFeel::eColorID__moz_mac_defaultbuttontext,
|
||||
eCSSKeyword__moz_mac_focusring, LookAndFeel::eColorID__moz_mac_focusring,
|
||||
eCSSKeyword__moz_mac_menuselect, LookAndFeel::eColorID__moz_mac_menuselect,
|
||||
eCSSKeyword__moz_mac_menushadow, LookAndFeel::eColorID__moz_mac_menushadow,
|
||||
|
@ -102,13 +102,17 @@ MP4Demuxer::Init()
|
||||
|
||||
if (!mPrivate->mAudio.get() && !strncmp(mimeType, "audio/", 6)) {
|
||||
mPrivate->mAudio = e->getTrack(i);
|
||||
mPrivate->mAudio->start();
|
||||
if (mPrivate->mAudio->start() != OK) {
|
||||
return false;
|
||||
}
|
||||
mAudioConfig.Update(metaData, mimeType);
|
||||
mPrivate->mIndexes.AppendElement(new Index(
|
||||
mPrivate->mAudio->exportIndex(), mSource, mAudioConfig.mTrackId));
|
||||
} else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) {
|
||||
mPrivate->mVideo = e->getTrack(i);
|
||||
mPrivate->mVideo->start();
|
||||
if (mPrivate->mVideo->start() != OK) {
|
||||
return false;
|
||||
}
|
||||
mVideoConfig.Update(metaData, mimeType);
|
||||
mPrivate->mIndexes.AppendElement(new Index(
|
||||
mPrivate->mVideo->exportIndex(), mSource, mVideoConfig.mTrackId));
|
||||
|
@ -48,7 +48,6 @@ public:
|
||||
int32_t timeScale,
|
||||
const sp<SampleTable> &sampleTable,
|
||||
Vector<SidxEntry> &sidx,
|
||||
off64_t firstMoofOffset,
|
||||
MPEG4Extractor::TrackExtends &trackExtends);
|
||||
|
||||
virtual status_t start(MetaData *params = NULL);
|
||||
@ -73,6 +72,7 @@ private:
|
||||
uint32_t mCurrentSampleIndex;
|
||||
uint32_t mCurrentFragmentIndex;
|
||||
Vector<SidxEntry> &mSegments;
|
||||
bool mLookedForMoof;
|
||||
off64_t mFirstMoofOffset;
|
||||
off64_t mCurrentMoofOffset;
|
||||
off64_t mNextMoofOffset;
|
||||
@ -111,6 +111,7 @@ private:
|
||||
status_t parseTrackFragmentRun(off64_t offset, off64_t size);
|
||||
status_t parseSampleAuxiliaryInformationSizes(off64_t offset, off64_t size);
|
||||
status_t parseSampleAuxiliaryInformationOffsets(off64_t offset, off64_t size);
|
||||
void lookForMoof();
|
||||
|
||||
struct TrackFragmentData {
|
||||
TrackFragmentData(): mPresent(false), mFlags(0), mBaseMediaDecodeTime(0) {}
|
||||
@ -349,7 +350,6 @@ static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t
|
||||
|
||||
MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
|
||||
: mSidxDuration(0),
|
||||
mMoofOffset(0),
|
||||
mDataSource(source),
|
||||
mInitCheck(NO_INIT),
|
||||
mHasVideo(false),
|
||||
@ -386,9 +386,7 @@ MPEG4Extractor::~MPEG4Extractor() {
|
||||
}
|
||||
|
||||
uint32_t MPEG4Extractor::flags() const {
|
||||
return CAN_PAUSE |
|
||||
((mMoofOffset == 0 || mSidxEntries.size() != 0) ?
|
||||
(CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK) : 0);
|
||||
return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
|
||||
}
|
||||
|
||||
sp<MetaData> MPEG4Extractor::getMetaData() {
|
||||
@ -439,38 +437,6 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((flags & kIncludeExtensiveMetaData)
|
||||
&& !track->includes_expensive_metadata) {
|
||||
track->includes_expensive_metadata = true;
|
||||
|
||||
const char *mime;
|
||||
CHECK(track->meta->findCString(kKeyMIMEType, &mime));
|
||||
if (!strncasecmp("video/", mime, 6)) {
|
||||
if (mMoofOffset > 0) {
|
||||
int64_t duration;
|
||||
if (track->meta->findInt64(kKeyDuration, &duration)) {
|
||||
// nothing fancy, just pick a frame near 1/4th of the duration
|
||||
track->meta->setInt64(
|
||||
kKeyThumbnailTime, duration / 4);
|
||||
}
|
||||
} else {
|
||||
uint32_t sampleIndex;
|
||||
uint32_t sampleTime;
|
||||
if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
|
||||
&& track->sampleTable->getMetaDataForSample(
|
||||
sampleIndex, NULL /* offset */, NULL /* size */,
|
||||
&sampleTime) == OK) {
|
||||
if (!track->timescale) {
|
||||
return NULL;
|
||||
}
|
||||
track->meta->setInt64(
|
||||
kKeyThumbnailTime,
|
||||
((int64_t)sampleTime * 1000000) / track->timescale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return track->meta;
|
||||
}
|
||||
|
||||
@ -488,27 +454,9 @@ status_t MPEG4Extractor::readMetaData() {
|
||||
}
|
||||
|
||||
off64_t offset = 0;
|
||||
status_t err = OK;
|
||||
while (true) {
|
||||
uint32_t hdr[2];
|
||||
if (mDataSource->readAt(offset, hdr, 8) < 8) {
|
||||
break;
|
||||
}
|
||||
uint32_t chunk_type = ntohl(hdr[1]);
|
||||
if (chunk_type == FOURCC('m', 'd', 'a', 't') && mFirstTrack) {
|
||||
break;
|
||||
}
|
||||
if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
|
||||
// store the offset of the first segment
|
||||
mMoofOffset = offset;
|
||||
break;
|
||||
}
|
||||
status_t err;
|
||||
while (!mFirstTrack) {
|
||||
err = parseChunk(&offset, 0);
|
||||
if (err != OK &&
|
||||
chunk_type != FOURCC('s', 'i', 'd', 'x') &&
|
||||
chunk_type != FOURCC('m', 'o', 'o', 'v')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mInitCheck == OK) {
|
||||
@ -2253,7 +2201,7 @@ sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
|
||||
|
||||
return new MPEG4Source(
|
||||
track->meta, mDataSource, track->timescale, track->sampleTable,
|
||||
mSidxEntries, mMoofOffset, mTrackExtends);
|
||||
mSidxEntries, mTrackExtends);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -2412,7 +2360,6 @@ MPEG4Source::MPEG4Source(
|
||||
int32_t timeScale,
|
||||
const sp<SampleTable> &sampleTable,
|
||||
Vector<SidxEntry> &sidx,
|
||||
off64_t firstMoofOffset,
|
||||
MPEG4Extractor::TrackExtends &trackExtends)
|
||||
: mFormat(format),
|
||||
mDataSource(dataSource),
|
||||
@ -2421,8 +2368,9 @@ MPEG4Source::MPEG4Source(
|
||||
mCurrentSampleIndex(0),
|
||||
mCurrentFragmentIndex(0),
|
||||
mSegments(sidx),
|
||||
mFirstMoofOffset(firstMoofOffset),
|
||||
mCurrentMoofOffset(firstMoofOffset),
|
||||
mLookedForMoof(false),
|
||||
mFirstMoofOffset(0),
|
||||
mCurrentMoofOffset(0),
|
||||
mCurrentTime(0),
|
||||
mCurrentSampleInfoAllocSize(0),
|
||||
mCurrentSampleInfoSizes(NULL),
|
||||
@ -2470,11 +2418,6 @@ MPEG4Source::MPEG4Source(
|
||||
}
|
||||
|
||||
CHECK(format->findInt32(kKeyTrackID, &mTrackId));
|
||||
|
||||
if (mFirstMoofOffset != 0) {
|
||||
off64_t offset = mFirstMoofOffset;
|
||||
parseChunk(&offset);
|
||||
}
|
||||
}
|
||||
|
||||
MPEG4Source::~MPEG4Source() {
|
||||
@ -2485,6 +2428,13 @@ MPEG4Source::~MPEG4Source() {
|
||||
free(mCurrentSampleInfoOffsets);
|
||||
}
|
||||
|
||||
static bool ValidInputSize(int32_t size) {
|
||||
// Reject compressed samples larger than an uncompressed UHD
|
||||
// frame. This is a reasonable cut-off for a lossy codec,
|
||||
// combined with the current Firefox limit to 5k video.
|
||||
return (size > 0 && size < 4 * (1920 * 1080) * 3 / 2);
|
||||
}
|
||||
|
||||
status_t MPEG4Source::start(MetaData *params) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
@ -2500,6 +2450,10 @@ status_t MPEG4Source::start(MetaData *params) {
|
||||
|
||||
int32_t max_size;
|
||||
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
|
||||
if (!ValidInputSize(max_size)) {
|
||||
ALOGE("Invalid max input size %d", max_size);
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
|
||||
mSrcBuffer = new uint8_t[max_size];
|
||||
|
||||
@ -3131,12 +3085,43 @@ size_t MPEG4Source::parseNALSize(const uint8_t *data) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MPEG4Source::lookForMoof() {
|
||||
off64_t offset = 0;
|
||||
off64_t size;
|
||||
while (true) {
|
||||
uint32_t hdr[2];
|
||||
auto x = mDataSource->readAt(offset, hdr, 8);
|
||||
if (x < 8) {
|
||||
break;
|
||||
}
|
||||
uint32_t chunk_size = ntohl(hdr[0]);
|
||||
uint32_t chunk_type = ntohl(hdr[1]);
|
||||
char chunk[5];
|
||||
MakeFourCCString(chunk_type, chunk);
|
||||
if (chunk_type == FOURCC('m', 'o', 'o', 'f')) {
|
||||
mFirstMoofOffset = mCurrentMoofOffset = offset;
|
||||
parseChunk(&offset);
|
||||
break;
|
||||
}
|
||||
if (chunk_type == FOURCC('m', 'd', 'a', 't')) {
|
||||
break;
|
||||
}
|
||||
offset += chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t MPEG4Source::read(
|
||||
MediaBuffer **out, const ReadOptions *options) {
|
||||
Mutex::Autolock autoLock(mLock);
|
||||
|
||||
CHECK(mStarted);
|
||||
|
||||
if (!mLookedForMoof) {
|
||||
mLookedForMoof = true;
|
||||
lookForMoof();
|
||||
}
|
||||
|
||||
if (mFirstMoofOffset > 0) {
|
||||
return fragmentedRead(out, options);
|
||||
}
|
||||
@ -3251,6 +3236,10 @@ status_t MPEG4Source::read(
|
||||
|
||||
int32_t max_size;
|
||||
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
|
||||
if (!ValidInputSize(max_size)) {
|
||||
ALOGE("Invalid max input size %d", max_size);
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
mBuffer = new MediaBuffer(max_size);
|
||||
assert(mBuffer);
|
||||
}
|
||||
@ -3533,6 +3522,10 @@ status_t MPEG4Source::fragmentedRead(
|
||||
|
||||
int32_t max_size;
|
||||
CHECK(mFormat->findInt32(kKeyMaxInputSize, &max_size));
|
||||
if (!ValidInputSize(max_size)) {
|
||||
ALOGE("Invalid max input size %d", max_size);
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
mBuffer = new MediaBuffer(max_size);
|
||||
assert(mBuffer);
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ private:
|
||||
|
||||
Vector<SidxEntry> mSidxEntries;
|
||||
uint64_t mSidxDuration;
|
||||
off64_t mMoofOffset;
|
||||
|
||||
Vector<PsshInfo> mPssh;
|
||||
|
||||
|
@ -200,8 +200,11 @@ public class BrowserDB {
|
||||
return sDb.getFaviconForUrl(cr, faviconURL);
|
||||
}
|
||||
|
||||
public static String getFaviconUrlForHistoryUrl(ContentResolver cr, String url) {
|
||||
return sDb.getFaviconUrlForHistoryUrl(cr, url);
|
||||
/**
|
||||
* Try to find a usable favicon URL in the history or bookmarks table.
|
||||
*/
|
||||
public static String getFaviconURLFromPageURL(ContentResolver cr, String url) {
|
||||
return sDb.getFaviconURLFromPageURL(cr, url);
|
||||
}
|
||||
|
||||
public static void updateFaviconForUrl(ContentResolver cr, String pageUri, byte[] encodedFavicon, String faviconUri) {
|
||||
|
@ -58,6 +58,8 @@ public class LocalBrowserDB {
|
||||
// Calculate these once, at initialization. isLoggable is too expensive to
|
||||
// have in-line in each log call.
|
||||
private static final String LOGTAG = "GeckoLocalBrowserDB";
|
||||
private static final Integer FAVICON_ID_NOT_FOUND = Integer.MIN_VALUE;
|
||||
|
||||
private static final boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG);
|
||||
protected static void debug(String message) {
|
||||
if (logDebug) {
|
||||
@ -998,19 +1000,38 @@ public class LocalBrowserDB {
|
||||
return FaviconDecoder.decodeFavicon(b);
|
||||
}
|
||||
|
||||
public String getFaviconUrlForHistoryUrl(ContentResolver cr, String uri) {
|
||||
final Cursor c = cr.query(mHistoryUriWithProfile,
|
||||
new String[] { History.FAVICON_URL },
|
||||
Combined.URL + " = ?",
|
||||
new String[] { uri },
|
||||
null);
|
||||
/**
|
||||
* Try to find a usable favicon URL in the history or bookmarks table.
|
||||
*/
|
||||
public String getFaviconURLFromPageURL(ContentResolver cr, String uri) {
|
||||
// Check first in the history table.
|
||||
Cursor c = cr.query(mHistoryUriWithProfile,
|
||||
new String[] { History.FAVICON_URL },
|
||||
Combined.URL + " = ?",
|
||||
new String[] { uri },
|
||||
null);
|
||||
|
||||
try {
|
||||
if (!c.moveToFirst()) {
|
||||
return null;
|
||||
if (c.moveToFirst()) {
|
||||
return c.getString(c.getColumnIndexOrThrow(History.FAVICON_URL));
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
|
||||
// If that fails, check in the bookmarks table.
|
||||
c = cr.query(mBookmarksUriWithProfile,
|
||||
new String[] { Bookmarks.FAVICON_URL },
|
||||
Bookmarks.URL + " = ?",
|
||||
new String[] { uri },
|
||||
null);
|
||||
|
||||
try {
|
||||
if (c.moveToFirst()) {
|
||||
return c.getString(c.getColumnIndexOrThrow(Bookmarks.FAVICON_URL));
|
||||
}
|
||||
|
||||
return c.getString(c.getColumnIndexOrThrow(History.FAVICON_URL));
|
||||
return null;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
@ -1027,10 +1048,66 @@ public class LocalBrowserDB {
|
||||
Uri faviconsUri = getAllFaviconsUri().buildUpon().
|
||||
appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
|
||||
|
||||
cr.update(faviconsUri,
|
||||
values,
|
||||
Favicons.URL + " = ?",
|
||||
new String[] { faviconUri });
|
||||
final int updated = cr.update(faviconsUri,
|
||||
values,
|
||||
Favicons.URL + " = ?",
|
||||
new String[] { faviconUri });
|
||||
|
||||
if (updated == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// After writing the encodedFavicon, ensure that the favicon_id in both the bookmark and
|
||||
// history tables are also up-to-date.
|
||||
final Integer id = getIDForFaviconURL(cr, faviconUri);
|
||||
if (id == FAVICON_ID_NOT_FOUND) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateHistoryAndBookmarksFaviconID(cr, pageUri, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and returns the favicon ID of a target URL as an Integer.
|
||||
*/
|
||||
private Integer getIDForFaviconURL(ContentResolver cr, String faviconURL) {
|
||||
final Cursor c = cr.query(mFaviconsUriWithProfile,
|
||||
new String[] { Favicons._ID },
|
||||
Favicons.URL + " = ? AND " + Favicons.DATA + " IS NOT NULL",
|
||||
new String[] { faviconURL },
|
||||
null);
|
||||
|
||||
try {
|
||||
final int col = c.getColumnIndexOrThrow(Favicons._ID);
|
||||
if (c.moveToFirst() && !c.isNull(col)) {
|
||||
return c.getInt(col);
|
||||
}
|
||||
|
||||
// IDs can be negative, so we return a sentinel value indicating "not found".
|
||||
return FAVICON_ID_NOT_FOUND;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the favicon ID in the history and bookmark tables after a new
|
||||
* favicon table entry is added.
|
||||
*/
|
||||
private void updateHistoryAndBookmarksFaviconID(ContentResolver cr, String pageURL, int id) {
|
||||
final ContentValues bookmarkValues = new ContentValues();
|
||||
bookmarkValues.put(Bookmarks.FAVICON_ID, id);
|
||||
cr.update(mBookmarksUriWithProfile,
|
||||
bookmarkValues,
|
||||
Bookmarks.URL + " = ?",
|
||||
new String[] { pageURL });
|
||||
|
||||
final ContentValues historyValues = new ContentValues();
|
||||
historyValues.put(History.FAVICON_ID, id);
|
||||
cr.update(mHistoryUriWithProfile,
|
||||
historyValues,
|
||||
History.URL + " = ?",
|
||||
new String[] { pageURL });
|
||||
}
|
||||
|
||||
public void updateThumbnailForUrl(ContentResolver cr, String uri,
|
||||
|
@ -17,6 +17,7 @@ import org.mozilla.gecko.util.GeckoJarReader;
|
||||
import org.mozilla.gecko.util.NonEvictingLruCache;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
@ -53,8 +54,6 @@ public class Favicons {
|
||||
|
||||
public static final int NOT_LOADING = 0;
|
||||
public static final int LOADED = 1;
|
||||
public static final int FLAG_PERSIST = 2;
|
||||
public static final int FLAG_SCALE = 4;
|
||||
|
||||
// The default Favicon to show if no other can be found.
|
||||
public static Bitmap defaultFavicon;
|
||||
@ -260,12 +259,15 @@ public class Favicons {
|
||||
}
|
||||
}
|
||||
|
||||
targetURL = BrowserDB.getFaviconUrlForHistoryUrl(context.getContentResolver(), pageURL);
|
||||
if (targetURL == null) {
|
||||
// Nothing in the history database. Fall back to the default URL and hope for the best.
|
||||
targetURL = guessDefaultFaviconURL(pageURL);
|
||||
// Try to find the faviconURL in the history and/or bookmarks table.
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
targetURL = BrowserDB.getFaviconURLFromPageURL(resolver, pageURL);
|
||||
if (targetURL != null) {
|
||||
return targetURL;
|
||||
}
|
||||
return targetURL;
|
||||
|
||||
// If we still can't find it, fall back to the default URL and hope for the best.
|
||||
return guessDefaultFaviconURL(pageURL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,6 @@ public class LoadFaviconTask {
|
||||
private static final HashMap<String, LoadFaviconTask> loadsInFlight = new HashMap<>();
|
||||
|
||||
public static final int FLAG_PERSIST = 1;
|
||||
public static final int FLAG_SCALE = 2;
|
||||
private static final int MAX_REDIRECTS_TO_FOLLOW = 5;
|
||||
// The default size of the buffer to use for downloading Favicons in the event no size is given
|
||||
// by the server.
|
||||
|
@ -37,7 +37,7 @@ interface nsIDOMWindow;
|
||||
interface nsIPermission;
|
||||
interface nsISimpleEnumerator;
|
||||
|
||||
[scriptable, uuid(c9fec678-f194-43c9-96b0-7bd9dbdd6bb0)]
|
||||
[scriptable, uuid(620d9b61-8997-4d13-aa64-ec03341dd75b)]
|
||||
interface nsIPermissionManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -132,6 +132,11 @@ interface nsIPermissionManager : nsISupports
|
||||
*/
|
||||
void removeAll();
|
||||
|
||||
/**
|
||||
* Clear all permission information added since the specified time.
|
||||
*/
|
||||
void removeAllSince(in int64_t since);
|
||||
|
||||
/**
|
||||
* Test whether a website has permission to perform the given action.
|
||||
* @param uri the uri to be tested
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user