Merge m-c to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-09-15 14:38:50 +02:00
commit 2ea70b91fa
113 changed files with 10559 additions and 8499 deletions

View File

@ -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];
}
},

View File

@ -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

View File

@ -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(() => {

View File

@ -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),
};

View File

@ -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);
}
}
};

View File

@ -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);

View File

@ -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");
});

View File

@ -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");
}

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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)
{

View File

@ -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")

View File

@ -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

View 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>

View File

@ -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)
{

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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}
};

View File

@ -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

View File

@ -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
*/

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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.

View File

@ -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) {

View File

@ -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:

View File

@ -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,
};
})();

View File

@ -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) {

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
});

View File

@ -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"]

View 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);
}

View File

@ -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]

View File

@ -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>

View File

@ -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),

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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!");
}

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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));

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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);
};

View File

@ -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();

View File

@ -57,7 +57,7 @@ private:
SurfaceFormat mFormat;
IntSize mSize;
RefPtr<DrawTargetD2D1> mDrawTarget;
DrawTargetD2D1* mDrawTarget;
};
class DataSourceSurfaceD2D1 : public DataSourceSurface

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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',

View File

@ -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]);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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']:

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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));

View File

@ -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);
}

View File

@ -93,7 +93,6 @@ private:
Vector<SidxEntry> mSidxEntries;
uint64_t mSidxDuration;
off64_t mMoofOffset;
Vector<PsshInfo> mPssh;

View File

@ -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) {

View File

@ -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,

View File

@ -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);
}
/**

View File

@ -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.

View File

@ -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