merge autoland to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-11-09 16:36:48 +01:00
Родитель 426cccbbc7 dc2b64ae9a
Коммит 61fea2322c
185 изменённых файлов: 3765 добавлений и 1288 удалений

Просмотреть файл

@ -28,10 +28,14 @@ static const mozilla::mscom::ArrayData sPlatformChildArrayData[] = {
{IID_IEnumVARIANT, 3, 1, VT_DISPATCH, IID_IDispatch, 2},
{IID_IAccessible2, 30, 1, VT_UNKNOWN | VT_BYREF, IID_IAccessibleRelation, 2},
{IID_IAccessibleRelation, 7, 1, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 2},
{IID_IAccessible2_2, 48, 2, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 3},
{IID_IAccessibleTableCell, 4, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1},
{IID_IAccessibleTableCell, 7, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1},
{IID_IAccessibleHypertext2, 25, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1}
{IID_IAccessible2_2, 48, 2, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 3,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
{IID_IAccessibleTableCell, 4, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
{IID_IAccessibleTableCell, 7, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
{IID_IAccessibleHypertext2, 25, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer}
};
// Type libraries are thread-neutral, so we can register those from any

Просмотреть файл

@ -324,11 +324,11 @@ function openLinkIn(url, where, params) {
} catch (e) {}
}
// NB: we avoid using |w| here because in the 'popup window' case,
// We avoid using |w| here because in the 'popup window' case,
// if we pass a currentBrowser param |w.gBrowser| might not be the
// tabbrowser that contains |aCurrentBrowser|. We really only care
// about the tab linked to |aCurrentBrowser|.
let tab = aCurrentBrowser.ownerGlobal.gBrowser.getTabForBrowser(aCurrentBrowser);
let tab = aCurrentBrowser.getTabBrowser().getTabForBrowser(aCurrentBrowser);
if (where == "current" && tab.pinned &&
!aAllowPinnedTabHostChange) {
try {
@ -348,6 +348,7 @@ function openLinkIn(url, where, params) {
// result in a new frontmost window (e.g. "javascript:window.open('');").
w.focus();
let browserUsedForLoad = null;
switch (where) {
case "current":
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
@ -383,12 +384,13 @@ function openLinkIn(url, where, params) {
postData: aPostData,
userContextId: aUserContextId
});
browserUsedForLoad = aCurrentBrowser;
break;
case "tabshifted":
loadInBackground = !loadInBackground;
// fall through
case "tab":
w.gBrowser.loadOneTab(url, {
let tabUsedForLoad = w.gBrowser.loadOneTab(url, {
referrerURI: aReferrerURI,
referrerPolicy: aReferrerPolicy,
charset: aCharset,
@ -402,10 +404,15 @@ function openLinkIn(url, where, params) {
userContextId: aUserContextId,
originPrincipal: aPrincipal,
});
browserUsedForLoad = tabUsedForLoad.linkedBrowser;
break;
}
aCurrentBrowser.focus();
// Focus the content, but only if the browser used for the load is selected.
if (browserUsedForLoad &&
browserUsedForLoad == browserUsedForLoad.getTabBrowser().selectedBrowser) {
browserUsedForLoad.focus();
}
if (!loadInBackground && w.isBlankPageURL(url)) {
w.focusAndSelectUrlBar();

Просмотреть файл

@ -5,12 +5,19 @@
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
SingletonEventManager,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
let listenerCount = 0;
function getTree(rootGuid, onlyChildren) {
function convert(node, parent) {
let treenode = {
@ -77,6 +84,103 @@ function convert(result) {
return node;
}
let observer = {
skipTags: true,
skipDescendantsOnItemRemoval: true,
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemAdded(id, parentId, index, itemType, uri, title, dateAdded, guid, parentGuid, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
return;
}
let bookmark = {
id: guid,
parentId: parentGuid,
index,
title,
dateAdded: dateAdded / 1000,
};
if (itemType == PlacesUtils.bookmarks.TYPE_BOOKMARK) {
bookmark.url = uri.spec;
} else {
bookmark.dateGroupModified = bookmark.dateAdded;
}
this.emit("created", bookmark);
},
onItemVisited() {},
onItemMoved(id, oldParentId, oldIndex, newParentId, newIndex, itemType, guid, oldParentGuid, newParentGuid, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
return;
}
let info = {
parentId: newParentGuid,
index: newIndex,
oldParentId: oldParentGuid,
oldIndex,
};
this.emit("moved", {guid, info});
},
onItemRemoved(id, parentId, index, itemType, uri, guid, parentGuid, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
return;
}
let node = {
id: guid,
parentId: parentGuid,
index,
};
if (itemType == PlacesUtils.bookmarks.TYPE_BOOKMARK) {
node.url = uri.spec;
}
this.emit("removed", {guid, info: {parentId: parentGuid, index, node}});
},
onItemChanged(id, prop, isAnno, val, lastMod, itemType, parentId, guid, parentGuid, oldVal, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
return;
}
let info = {};
if (prop == "title") {
info.title = val;
} else if (prop == "uri") {
info.url = val;
} else {
// Not defined yet.
return;
}
this.emit("changed", {guid, info});
},
};
EventEmitter.decorate(observer);
function decrementListeners() {
listenerCount -= 1;
if (!listenerCount) {
PlacesUtils.bookmarks.removeObserver(observer);
}
}
function incrementListeners() {
listenerCount++;
if (listenerCount == 1) {
PlacesUtils.bookmarks.addObserver(observer, false);
}
}
extensions.registerSchemaAPI("bookmarks", "addon_parent", context => {
return {
bookmarks: {
@ -213,6 +317,58 @@ extensions.registerSchemaAPI("bookmarks", "addon_parent", context => {
return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
}
},
onCreated: new SingletonEventManager(context, "bookmarks.onCreated", fire => {
let listener = (event, bookmark) => {
context.runSafe(fire, bookmark.id, bookmark);
};
observer.on("created", listener);
incrementListeners();
return () => {
observer.off("created", listener);
decrementListeners();
};
}).api(),
onRemoved: new SingletonEventManager(context, "bookmarks.onRemoved", fire => {
let listener = (event, data) => {
context.runSafe(fire, data.guid, data.info);
};
observer.on("removed", listener);
incrementListeners();
return () => {
observer.off("removed", listener);
decrementListeners();
};
}).api(),
onChanged: new SingletonEventManager(context, "bookmarks.onChanged", fire => {
let listener = (event, data) => {
context.runSafe(fire, data.guid, data.info);
};
observer.on("changed", listener);
incrementListeners();
return () => {
observer.off("changed", listener);
decrementListeners();
};
}).api(),
onMoved: new SingletonEventManager(context, "bookmarks.onMoved", fire => {
let listener = (event, data) => {
context.runSafe(fire, data.guid, data.info);
};
observer.on("moved", listener);
incrementListeners();
return () => {
observer.off("moved", listener);
decrementListeners();
};
}).api(),
},
};
});

Просмотреть файл

@ -2,6 +2,11 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
promiseObserved,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
@ -31,6 +36,21 @@ function getRecentlyClosed(maxResults, extension) {
return recentlyClosed.slice(0, maxResults);
}
function createSession(restored, extension, sessionId) {
if (!restored) {
return Promise.reject({message: `Could not restore object using sessionId ${sessionId}.`});
}
let sessionObj = {lastModified: Date.now()};
if (restored instanceof Ci.nsIDOMChromeWindow) {
return promiseObserved("sessionstore-single-window-restored", subject => subject == restored).then(() => {
sessionObj.window = WindowManager.convert(extension, restored, {populate: true});
return Promise.resolve([sessionObj]);
});
}
sessionObj.tab = TabManager.for(extension).convert(restored);
return Promise.resolve([sessionObj]);
}
extensions.registerSchemaAPI("sessions", "addon_parent", context => {
let {extension} = context;
return {
@ -39,6 +59,34 @@ extensions.registerSchemaAPI("sessions", "addon_parent", context => {
let maxResults = filter.maxResults == undefined ? this.MAX_SESSION_RESULTS : filter.maxResults;
return Promise.resolve(getRecentlyClosed(maxResults, extension));
},
restore: function(sessionId) {
let session, closedId;
if (sessionId) {
closedId = sessionId;
session = SessionStore.undoCloseById(closedId);
} else if (SessionStore.lastClosedObjectType == "window") {
// If the most recently closed object is a window, just undo closing the most recent window.
session = SessionStore.undoCloseWindow(0);
} else {
// It is a tab, and we cannot call SessionStore.undoCloseTab without a window,
// so we must find the tab in which case we can just use its closedId.
let recentlyClosedTabs = [];
for (let window of WindowListManager.browserWindows()) {
let closedTabData = SessionStore.getClosedTabData(window, false);
for (let tab of closedTabData) {
recentlyClosedTabs.push(tab);
}
}
// Sort the tabs.
recentlyClosedTabs.sort((a, b) => b.closedAt - a.closedAt);
// Use the closedId of the most recently closed tab to restore it.
closedId = recentlyClosedTabs[0].closedId;
session = SessionStore.undoCloseById(closedId);
}
return createSession(session, extension, closedId);
},
},
};
});

Просмотреть файл

@ -451,7 +451,6 @@
"events": [
{
"name": "onCreated",
"unsupported": true,
"type": "function",
"description": "Fired when a bookmark or folder is created.",
"parameters": [
@ -467,7 +466,6 @@
},
{
"name": "onRemoved",
"unsupported": true,
"type": "function",
"description": "Fired when a bookmark or folder is removed. When a folder is removed recursively, a single notification is fired for the folder, and none for its contents.",
"parameters": [
@ -488,7 +486,6 @@
},
{
"name": "onChanged",
"unsupported": true,
"type": "function",
"description": "Fired when a bookmark or folder changes. <b>Note:</b> Currently, only title and url changes trigger this.",
"parameters": [
@ -511,7 +508,6 @@
},
{
"name": "onMoved",
"unsupported": true,
"type": "function",
"description": "Fired when a bookmark or folder is moved to a different parent folder.",
"parameters": [

Просмотреть файл

@ -103,7 +103,6 @@
},
{
"name": "restore",
"unsupported": true,
"type": "function",
"description": "Reopens a $(ref:windows.Window) or $(ref:tabs.Tab), with an optional callback to run when the entry has been restored.",
"async": "callback",

Просмотреть файл

@ -61,6 +61,7 @@ tags = webextensions
[browser_ext_runtime_setUninstallURL.js]
[browser_ext_sessions_getRecentlyClosed.js]
[browser_ext_sessions_getRecentlyClosed_private.js]
[browser_ext_sessions_restore.js]
[browser_ext_simple.js]
[browser_ext_tab_runtimeConnect.js]
[browser_ext_tabs_audio.js]

Просмотреть файл

@ -0,0 +1,134 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
SimpleTest.requestCompleteLog();
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
add_task(function* test_sessions_restore() {
function background() {
browser.test.onMessage.addListener((msg, data) => {
if (msg == "check-sessions") {
browser.sessions.getRecentlyClosed().then(recentlyClosed => {
browser.test.sendMessage("recentlyClosed", recentlyClosed);
});
} else if (msg == "restore") {
browser.sessions.restore(data).then(sessions => {
browser.test.sendMessage("restored", sessions);
});
} else if (msg == "restore-reject") {
browser.sessions.restore("not-a-valid-session-id").then(
sessions => {
browser.test.fail("restore rejected with an invalid sessionId");
},
error => {
browser.test.assertTrue(
error.message.includes("Could not restore object using sessionId not-a-valid-session-id."));
browser.test.sendMessage("restore-rejected");
}
);
}
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["sessions", "tabs"],
},
background,
});
yield extension.startup();
let {Management: {global: {WindowManager, TabManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
function checkLocalTab(tab, expectedUrl) {
let realTab = TabManager.getTab(tab.id);
let tabState = JSON.parse(SessionStore.getTabState(realTab));
is(tabState.entries[0].url, expectedUrl, "restored tab has the expected url");
}
let win = yield BrowserTestUtils.openNewBrowserWindow();
yield BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:config");
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
for (let url of ["about:robots", "about:mozilla"]) {
yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
}
yield BrowserTestUtils.closeWindow(win);
extension.sendMessage("check-sessions");
let recentlyClosed = yield extension.awaitMessage("recentlyClosed");
// Check that our expected window is the most recently closed.
is(recentlyClosed[0].window.tabs.length, 3, "most recently closed window has the expected number of tabs");
// Restore the window.
extension.sendMessage("restore");
let restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored[0].window.tabs[0], "about:config");
checkLocalTab(restored[0].window.tabs[1], "about:robots");
checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
// Close the window again.
let window = WindowManager.getWindow(restored[0].window.id);
yield BrowserTestUtils.closeWindow(window);
// Restore the window using the sessionId.
extension.sendMessage("check-sessions");
recentlyClosed = yield extension.awaitMessage("recentlyClosed");
extension.sendMessage("restore", recentlyClosed[0].window.sessionId);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored[0].window.tabs[0], "about:config");
checkLocalTab(restored[0].window.tabs[1], "about:robots");
checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
// Close the window again.
window = WindowManager.getWindow(restored[0].window.id);
yield BrowserTestUtils.closeWindow(window);
// Open and close a tab.
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
yield BrowserTestUtils.removeTab(tab);
// Restore the most recently closed item.
extension.sendMessage("restore");
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
tab = restored[0].tab;
ok(tab, "restore returned a tab");
checkLocalTab(tab, "about:robots");
// Close the tab again.
let realTab = TabManager.getTab(tab.id);
yield BrowserTestUtils.removeTab(realTab);
// Restore the tab using the sessionId.
extension.sendMessage("check-sessions");
recentlyClosed = yield extension.awaitMessage("recentlyClosed");
extension.sendMessage("restore", recentlyClosed[0].tab.sessionId);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
tab = restored[0].tab;
ok(tab, "restore returned a tab");
checkLocalTab(tab, "about:robots");
// Close the tab again.
realTab = TabManager.getTab(tab.id);
yield BrowserTestUtils.removeTab(realTab);
// Try to restore something with an invalid sessionId.
extension.sendMessage("restore-reject");
restored = yield extension.awaitMessage("restore-rejected");
yield extension.unload();
});

Просмотреть файл

@ -6,6 +6,8 @@ function backgroundScript() {
let unsortedId, ourId;
let initialBookmarkCount = 0;
let createdBookmarks = new Set();
let createdFolderId;
let collectedEvents = [];
const nonExistentId = "000000000000";
const bookmarkGuids = {
menuGuid: "menu________",
@ -37,6 +39,73 @@ function backgroundScript() {
browser.test.fail("Did not get expected error");
}
function checkOnCreated(id, parentId, index, title, url, dateAdded) {
let createdData = collectedEvents.pop();
browser.test.assertEq("onCreated", createdData.event, "onCreated was the last event received");
browser.test.assertEq(id, createdData.id, "onCreated event received the expected id");
let bookmark = createdData.bookmark;
browser.test.assertEq(id, bookmark.id, "onCreated event received the expected bookmark id");
browser.test.assertEq(parentId, bookmark.parentId, "onCreated event received the expected bookmark parentId");
browser.test.assertEq(index, bookmark.index, "onCreated event received the expected bookmark index");
browser.test.assertEq(title, bookmark.title, "onCreated event received the expected bookmark title");
browser.test.assertEq(url, bookmark.url, "onCreated event received the expected bookmark url");
browser.test.assertEq(dateAdded, bookmark.dateAdded, "onCreated event received the expected bookmark dateAdded");
}
function checkOnChanged(id, url, title) {
// If both url and title are changed, then url is fired last.
let changedData = collectedEvents.pop();
browser.test.assertEq("onChanged", changedData.event, "onChanged was the last event received");
browser.test.assertEq(id, changedData.id, "onChanged event received the expected id");
browser.test.assertEq(url, changedData.info.url, "onChanged event received the expected url");
// title is fired first.
changedData = collectedEvents.pop();
browser.test.assertEq("onChanged", changedData.event, "onChanged was the last event received");
browser.test.assertEq(id, changedData.id, "onChanged event received the expected id");
browser.test.assertEq(title, changedData.info.title, "onChanged event received the expected title");
}
function checkOnMoved(id, parentId, oldParentId, index, oldIndex) {
let movedData = collectedEvents.pop();
browser.test.assertEq("onMoved", movedData.event, "onMoved was the last event received");
browser.test.assertEq(id, movedData.id, "onMoved event received the expected id");
let info = movedData.info;
browser.test.assertEq(parentId, info.parentId, "onMoved event received the expected parentId");
browser.test.assertEq(oldParentId, info.oldParentId, "onMoved event received the expected oldParentId");
browser.test.assertEq(index, info.index, "onMoved event received the expected index");
browser.test.assertEq(oldIndex, info.oldIndex, "onMoved event received the expected oldIndex");
}
function checkOnRemoved(id, parentId, index, url) {
let removedData = collectedEvents.pop();
browser.test.assertEq("onRemoved", removedData.event, "onRemoved was the last event received");
browser.test.assertEq(id, removedData.id, "onRemoved event received the expected id");
let info = removedData.info;
browser.test.assertEq(parentId, removedData.info.parentId, "onRemoved event received the expected parentId");
browser.test.assertEq(index, removedData.info.index, "onRemoved event received the expected index");
let node = info.node;
browser.test.assertEq(id, node.id, "onRemoved event received the expected node id");
browser.test.assertEq(parentId, node.parentId, "onRemoved event received the expected node parentId");
browser.test.assertEq(index, node.index, "onRemoved event received the expected node index");
browser.test.assertEq(url, node.url, "onRemoved event received the expected node url");
}
browser.bookmarks.onChanged.addListener((id, info) => {
collectedEvents.push({event: "onChanged", id, info});
});
browser.bookmarks.onCreated.addListener((id, bookmark) => {
collectedEvents.push({event: "onCreated", id, bookmark});
});
browser.bookmarks.onMoved.addListener((id, info) => {
collectedEvents.push({event: "onMoved", id, info});
});
browser.bookmarks.onRemoved.addListener((id, info) => {
collectedEvents.push({event: "onRemoved", id, info});
});
browser.bookmarks.get(["not-a-bookmark-guid"]).then(expectedError, error => {
browser.test.assertTrue(
error.message.includes("Invalid value for property 'guid': not-a-bookmark-guid"),
@ -57,6 +126,8 @@ function backgroundScript() {
}).then(result => {
ourId = result.id;
checkOurBookmark(result);
browser.test.assertEq(1, collectedEvents.length, "1 expected event received");
checkOnCreated(ourId, bookmarkGuids.unfiledGuid, 0, "test bookmark", "http://example.org/", result.dateAdded);
return browser.bookmarks.get(ourId);
}).then(results => {
@ -96,6 +167,9 @@ function backgroundScript() {
browser.test.assertEq("http://example.com/", result.url, "Updated bookmark has the expected URL");
browser.test.assertEq(ourId, result.id, "Updated bookmark has the expected id");
browser.test.assertEq(2, collectedEvents.length, "2 expected events received");
checkOnChanged(ourId, "http://example.com/", "new test title");
return Promise.resolve().then(() => {
return browser.bookmarks.update(ourId, {url: "this is not a valid url"});
}).then(expectedError, error => {
@ -129,6 +203,9 @@ function backgroundScript() {
}).then(result => {
browser.test.assertEq(undefined, result, "Removing a bookmark returns undefined");
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnRemoved(ourId, bookmarkGuids.unfiledGuid, 0, "http://example.com/");
return browser.bookmarks.get(ourId).then(expectedError, error => {
browser.test.assertTrue(
error.message.includes("Bookmark not found"),
@ -145,32 +222,49 @@ function backgroundScript() {
}).then(() => {
// test bookmarks.search
return Promise.all([
browser.bookmarks.create({title: "MØzillä", url: "http://møzîllä.örg"}),
browser.bookmarks.create({title: "Example", url: "http://example.org"}),
browser.bookmarks.create({title: "MØzillä", url: "http://møzîllä.örg/"}),
browser.bookmarks.create({title: "Example", url: "http://example.org/"}),
browser.bookmarks.create({title: "Mozilla Folder"}),
browser.bookmarks.create({title: "EFF", url: "http://eff.org"}),
browser.bookmarks.create({title: "Menu Item", url: "http://menu.org", parentId: bookmarkGuids.menuGuid}),
browser.bookmarks.create({title: "Toolbar Item", url: "http://toolbar.org", parentId: bookmarkGuids.toolbarGuid}),
browser.bookmarks.create({title: "EFF", url: "http://eff.org/"}),
browser.bookmarks.create({title: "Menu Item", url: "http://menu.org/", parentId: bookmarkGuids.menuGuid}),
browser.bookmarks.create({title: "Toolbar Item", url: "http://toolbar.org/", parentId: bookmarkGuids.toolbarGuid}),
]);
}).then(results => {
browser.test.assertEq(6, collectedEvents.length, "6 expected events received");
checkOnCreated(results[5].id, bookmarkGuids.toolbarGuid, 0, "Toolbar Item", "http://toolbar.org/", results[5].dateAdded);
checkOnCreated(results[4].id, bookmarkGuids.menuGuid, 0, "Menu Item", "http://menu.org/", results[4].dateAdded);
checkOnCreated(results[3].id, bookmarkGuids.unfiledGuid, 0, "EFF", "http://eff.org/", results[3].dateAdded);
checkOnCreated(results[2].id, bookmarkGuids.unfiledGuid, 0, "Mozilla Folder", undefined, results[2].dateAdded);
checkOnCreated(results[1].id, bookmarkGuids.unfiledGuid, 0, "Example", "http://example.org/", results[1].dateAdded);
checkOnCreated(results[0].id, bookmarkGuids.unfiledGuid, 0, "MØzillä", "http://møzîllä.örg/", results[0].dateAdded);
for (let result of results) {
if (result.title !== "Mozilla Folder") {
createdBookmarks.add(result.id);
}
}
let createdFolderId = results[2].id;
let folderResult = results[2];
createdFolderId = folderResult.id;
return Promise.all([
browser.bookmarks.create({title: "Mozilla", url: "http://allizom.org", parentId: createdFolderId}),
browser.bookmarks.create({title: "Mozilla Corporation", url: "http://allizom.com", parentId: createdFolderId}),
browser.bookmarks.create({title: "Firefox", url: "http://allizom.org/firefox", parentId: createdFolderId}),
browser.bookmarks.create({title: "Mozilla", url: "http://allizom.org/", parentId: createdFolderId}),
browser.bookmarks.create({title: "Mozilla Corporation", url: "http://allizom.com/", parentId: createdFolderId}),
browser.bookmarks.create({title: "Firefox", url: "http://allizom.org/firefox/", parentId: createdFolderId}),
]).then(results => {
browser.test.assertEq(3, collectedEvents.length, "3 expected events received");
checkOnCreated(results[2].id, createdFolderId, 0, "Firefox", "http://allizom.org/firefox/", results[2].dateAdded);
checkOnCreated(results[1].id, createdFolderId, 0, "Mozilla Corporation", "http://allizom.com/", results[1].dateAdded);
checkOnCreated(results[0].id, createdFolderId, 0, "Mozilla", "http://allizom.org/", results[0].dateAdded);
return browser.bookmarks.create({
title: "About Mozilla",
url: "http://allizom.org/about",
url: "http://allizom.org/about/",
parentId: createdFolderId,
index: 1,
});
}).then(() => {
}).then(result => {
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnCreated(result.id, createdFolderId, 1, "About Mozilla", "http://allizom.org/about/", result.dateAdded);
// returns all items on empty object
return browser.bookmarks.search({});
}).then(results => {
@ -375,20 +469,33 @@ function backgroundScript() {
return browser.bookmarks.move(corporationBookmark.id, {index: 0}).then(result => {
browser.test.assertEq(0, result.index, "Bookmark has the expected index");
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnMoved(corporationBookmark.id, createdFolderId, createdFolderId, 0, 2);
return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.menuGuid});
}).then(result => {
browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
browser.test.assertEq(childCount, result.index, "Bookmark has the expected index");
return browser.bookmarks.move(corporationBookmark.id, {index: 1});
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnMoved(corporationBookmark.id, bookmarkGuids.menuGuid, createdFolderId, 1, 0);
return browser.bookmarks.move(corporationBookmark.id, {index: 0});
}).then(result => {
browser.test.assertEq(bookmarkGuids.menuGuid, result.parentId, "Bookmark has the expected parent");
browser.test.assertEq(1, result.index, "Bookmark has the expected index");
browser.test.assertEq(0, result.index, "Bookmark has the expected index");
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnMoved(corporationBookmark.id, bookmarkGuids.menuGuid, bookmarkGuids.menuGuid, 0, 1);
return browser.bookmarks.move(corporationBookmark.id, {parentId: bookmarkGuids.toolbarGuid, index: 1});
}).then(result => {
browser.test.assertEq(bookmarkGuids.toolbarGuid, result.parentId, "Bookmark has the expected parent");
browser.test.assertEq(1, result.index, "Bookmark has the expected index");
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnMoved(corporationBookmark.id, bookmarkGuids.toolbarGuid, bookmarkGuids.menuGuid, 1, 0);
createdBookmarks.add(corporationBookmark.id);
});
}).then(() => {
@ -415,6 +522,9 @@ function backgroundScript() {
return browser.bookmarks.search({title: "Mozilla Folder"}).then(result => {
return browser.bookmarks.removeTree(result[0].id);
}).then(() => {
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnRemoved(createdFolderId, bookmarkGuids.unfiledGuid, 1);
return browser.bookmarks.search({}).then(results => {
browser.test.assertEq(
startBookmarkCount - 4,
@ -426,8 +536,15 @@ function backgroundScript() {
return browser.bookmarks.create({title: "Empty Folder"});
}).then(result => {
let emptyFolderId = result.id;
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnCreated(emptyFolderId, bookmarkGuids.unfiledGuid, 3, "Empty Folder", undefined, result.dateAdded);
browser.test.assertEq("Empty Folder", result.title, "Folder has the expected title");
return browser.bookmarks.remove(emptyFolderId).then(() => {
browser.test.assertEq(1, collectedEvents.length, "1 expected events received");
checkOnRemoved(emptyFolderId, bookmarkGuids.unfiledGuid, 3);
return browser.bookmarks.get(emptyFolderId).then(expectedError, error => {
browser.test.assertTrue(
error.message.includes("Bookmark not found"),
@ -456,9 +573,12 @@ function backgroundScript() {
let promises = Array.from(createdBookmarks, guid => browser.bookmarks.remove(guid));
return Promise.all(promises);
}).then(() => {
browser.test.assertEq(createdBookmarks.size, collectedEvents.length, "expected number of events received");
return browser.bookmarks.search({});
}).then(results => {
browser.test.assertEq(initialBookmarkCount, results.length, "All created bookmarks have been removed");
return browser.test.notifyPass("bookmarks");
}).catch(error => {
browser.test.fail(`Error: ${String(error)} :: ${error.stack}`);

Просмотреть файл

@ -70,6 +70,12 @@ function* doTest() {
EventUtils.synthesizeMouseAtCenter(searchInNewTabMenuItem, {});
let tab = yield promise;
// By default the search will open in the background and the popup will stay open:
promise = promiseEvent(searchPopup, "popuphidden");
info("Closing search panel");
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield promise;
// Check the loaded tab.
Assert.equal(tab.linkedBrowser.currentURI.spec,
"http://mochi.test:8888/browser/browser/components/search/test/",

Просмотреть файл

@ -0,0 +1,173 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Implements a service used to access storage and communicate with content.
*
* A "fields" array is used to communicate with FormAutofillContent. Each item
* represents a single input field in the content page as well as its
* @autocomplete properties. The schema is as below. Please refer to
* FormAutofillContent.jsm for more details.
*
* [
* {
* section,
* addressType,
* contactType,
* fieldName,
* value,
* index
* },
* {
* // ...
* }
* ]
*/
/* exported FormAutofillParent */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
"resource://formautofill/ProfileStorage.jsm");
const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
let FormAutofillParent = {
_profileStore: null,
/**
* Initializes ProfileStorage and registers the message handler.
*/
init: function() {
let storePath =
OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
this._profileStore = new ProfileStorage(storePath);
this._profileStore.initialize();
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
mm.addMessageListener("FormAutofill:PopulateFieldValues", this);
},
/**
* Handles the message coming from FormAutofillContent.
*
* @param {string} message.name The name of the message.
* @param {object} message.data The data of the message.
* @param {nsIFrameMessageManager} message.target Caller's message manager.
*/
receiveMessage: function({name, data, target}) {
switch (name) {
case "FormAutofill:PopulateFieldValues":
this._populateFieldValues(data, target);
break;
}
},
/**
* Returns the instance of ProfileStorage. To avoid syncing issues, anyone
* who needs to access the profile should request the instance by this instead
* of creating a new one.
*
* @returns {ProfileStorage}
*/
getProfileStore: function() {
return this._profileStore;
},
/**
* Uninitializes FormAutofillParent. This is for testing only.
*
* @private
*/
_uninit: function() {
if (this._profileStore) {
this._profileStore._saveImmediately();
this._profileStore = null;
}
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
},
/**
* Populates the field values and notifies content to fill in. Exception will
* be thrown if there's no matching profile.
*
* @private
* @param {string} data.guid
* Indicates which profile to populate
* @param {Fields} data.fields
* The "fields" array collected from content.
* @param {nsIFrameMessageManager} target
* Content's message manager.
*/
_populateFieldValues({guid, fields}, target) {
this._profileStore.notifyUsed(guid);
this._fillInFields(this._profileStore.get(guid), fields);
target.sendAsyncMessage("FormAutofill:fillForm", {fields});
},
/**
* Transforms a word with hyphen into camel case.
* (e.g. transforms "address-type" into "addressType".)
*
* @private
* @param {string} str The original string with hyphen.
* @returns {string} The camel-cased output string.
*/
_camelCase(str) {
return str.toLowerCase().replace(/-([a-z])/g, s => s[1].toUpperCase());
},
/**
* Get the corresponding value from the specified profile according to a valid
* @autocomplete field name.
*
* Note that the field name doesn't need to match the property name defined in
* Profile object. This method can transform the raw data to fulfill it. (e.g.
* inputting "country-name" as "fieldName" will get a full name transformed
* from the country code that is recorded in "country" field.)
*
* @private
* @param {Profile} profile The specified profile.
* @param {string} fieldName A valid @autocomplete field name.
* @returns {string} The corresponding value. Returns "undefined" if there's
* no matching field.
*/
_getDataByFieldName(profile, fieldName) {
let key = this._camelCase(fieldName);
// TODO: Transform the raw profile data to fulfill "fieldName" here.
return profile[key];
},
/**
* Fills in the "fields" array by the specified profile.
*
* @private
* @param {Profile} profile The specified profile to fill in.
* @param {Fields} fields The "fields" array collected from content.
*/
_fillInFields(profile, fields) {
for (let field of fields) {
let value = this._getDataByFieldName(profile, field.fieldName);
if (value !== undefined) {
field.value = value;
}
}
},
};
this.EXPORTED_SYMBOLS = ["FormAutofillParent"];

Просмотреть файл

@ -12,6 +12,16 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://testing-common/MockDocument.jsm");
// Redirect the path of the resouce in addon to the exact file path.
let defineLazyModuleGetter = XPCOMUtils.defineLazyModuleGetter;
XPCOMUtils.defineLazyModuleGetter = function() {
let result = /^resource\:\/\/formautofill\/(.+)$/.exec(arguments[2]);
if (result) {
arguments[2] = Services.io.newFileURI(do_get_file(result[1])).spec;
}
return defineLazyModuleGetter.apply(this, arguments);
};
// Load the module by Service newFileURI API for running extension's XPCShell test
function importAutofillModule(module) {
return Cu.import(Services.io.newFileURI(do_get_file(module)).spec);

Просмотреть файл

@ -0,0 +1,106 @@
/*
* Test for populating field values in Form Autofill Parent.
*/
/* global FormAutofillParent */
"use strict";
importAutofillModule("FormAutofillParent.jsm");
do_get_profile();
const TEST_FIELDS = [
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "organization"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level1"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "postal-code"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email"},
];
const TEST_GUID = "test-guid";
const TEST_PROFILE = {
guid: TEST_GUID,
organization: "World Wide Web Consortium",
streetAddress: "32 Vassar Street\nMIT Room 32-G524",
addressLevel2: "Cambridge",
addressLevel1: "MA",
postalCode: "02139",
country: "US",
tel: "+1 617 253 5702",
email: "timbl@w3.org",
};
function camelCase(str) {
return str.toLowerCase().replace(/-([a-z])/g, s => s[1].toUpperCase());
}
add_task(function* test_populateFieldValues() {
FormAutofillParent.init();
let store = FormAutofillParent.getProfileStore();
do_check_neq(store, null);
store.get = function(guid) {
do_check_eq(guid, TEST_GUID);
return store._clone(TEST_PROFILE);
};
let notifyUsedCalledCount = 0;
store.notifyUsed = function(guid) {
do_check_eq(guid, TEST_GUID);
notifyUsedCalledCount++;
};
yield new Promise((resolve) => {
FormAutofillParent.receiveMessage({
name: "FormAutofill:PopulateFieldValues",
data: {
guid: TEST_GUID,
fields: TEST_FIELDS,
},
target: {
sendAsyncMessage: function(name, data) {
do_check_eq(name, "FormAutofill:fillForm");
let fields = data.fields;
do_check_eq(fields.length, TEST_FIELDS.length);
for (let i = 0; i < fields.length; i++) {
do_check_eq(fields[i].fieldName, TEST_FIELDS[i].fieldName);
do_check_eq(fields[i].value,
TEST_PROFILE[camelCase(fields[i].fieldName)]);
}
resolve();
},
},
});
});
do_check_eq(notifyUsedCalledCount, 1);
FormAutofillParent._uninit();
do_check_null(FormAutofillParent.getProfileStore());
});
add_task(function* test_populateFieldValues_with_invalid_guid() {
FormAutofillParent.init();
Assert.throws(() => {
FormAutofillParent.receiveMessage({
name: "FormAutofill:PopulateFieldValues",
data: {
guid: "invalid-guid",
fields: TEST_FIELDS,
},
target: {},
});
}, /No matching profile\./);
FormAutofillParent._uninit();
});

Просмотреть файл

@ -3,8 +3,10 @@ head = head.js
tail =
support-files =
../../content/FormAutofillContent.jsm
../../content/FormAutofillParent.jsm
../../content/ProfileStorage.jsm
[test_autofillFormFields.js]
[test_collectFormFields.js]
[test_populateFieldValues.js]
[test_profileStorage.js]

Просмотреть файл

@ -220,3 +220,7 @@ res/table-remove-column.gif
res/table-remove-row-active.gif
res/table-remove-row-hover.gif
res/table-remove-row.gif
# Aurora branding
browser/chrome/browser/content/browser/defaultthemes/devedition.icon.png
browser/chrome/browser/content/branding/icon64.png
browser/chrome/devtools/content/framework/dev-edition-promo/dev-edition-logo.png

Просмотреть файл

@ -172,7 +172,7 @@ ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
mozglue/build/target memory/replace/logalloc/replay/target: memory/replace/dummy/target
endif
endif
ifeq (,$(MOZ_SYSTEM_NSS)$(MOZ_FOLD_LIBS))
ifeq (,$(MOZ_SYSTEM_NSPR)$(MOZ_SYSTEM_NSS)$(MOZ_FOLD_LIBS))
config/external/nss/target: config/external/nspr/pr/target config/external/nspr/ds/target config/external/nspr/libc/target
endif
# Most things are built during compile (target/host), but some things happen during export

Просмотреть файл

@ -921,6 +921,15 @@ cargo_build_flags += --manifest-path $(CARGO_FILE)
cargo_build_flags += --target=$(RUST_TARGET)
cargo_build_flags += --verbose
# Enable color output if original stdout was a TTY and color settings
# aren't already present. This essentially restores the default behavior
# of cargo when running via `mach`.
ifdef MACH_STDOUT_ISATTY
ifeq (,$(findstring --color,$(cargo_build_flags)))
cargo_build_flags += --color=always
endif
endif
# Assume any system libraries rustc links against are already in the target's LIBS.
#
# We need to run cargo unconditionally, because cargo is the only thing that

Просмотреть файл

@ -30,7 +30,11 @@ define(function (require, exports, module) {
},
getLength: function (grip) {
return grip.preview ? grip.preview.length : 0;
if (!grip.preview) {
return 0;
}
return grip.preview.length || grip.preview.childNodesLength || 0;
},
getTitle: function (object, context) {
@ -43,28 +47,37 @@ define(function (require, exports, module) {
return "";
},
getPreviewItems: function (grip) {
if (!grip.preview) {
return null;
}
return grip.preview.items || grip.preview.childNodes || null;
},
arrayIterator: function (grip, max) {
let items = [];
const gripLength = this.getLength(grip);
if (!grip.preview || !grip.preview.length) {
if (!gripLength) {
return items;
}
let array = grip.preview.items;
if (!array) {
const previewItems = this.getPreviewItems(grip);
if (!previewItems) {
return items;
}
let delim;
// number of grip.preview.items is limited to 10, but we may have more
// items in grip-array
let delimMax = grip.preview.length > array.length ?
array.length : array.length - 1;
// number of grip preview items is limited to 10, but we may have more
// items in grip-array.
let delimMax = gripLength > previewItems.length ?
previewItems.length : previewItems.length - 1;
let provider = this.props.provider;
for (let i = 0; i < array.length && i < max; i++) {
for (let i = 0; i < previewItems.length && i < max; i++) {
try {
let itemGrip = array[i];
let itemGrip = previewItems[i];
let value = provider ? provider.getValue(itemGrip) : itemGrip;
delim = (i == delimMax ? "" : ", ");
@ -80,10 +93,10 @@ define(function (require, exports, module) {
})));
}
}
if (array.length > max || grip.preview.length > array.length) {
if (previewItems.length > max || gripLength > previewItems.length) {
let objectLink = this.props.objectLink || span;
let leftItemNum = grip.preview.length - max > 0 ?
grip.preview.length - max : grip.preview.length - array.length;
let leftItemNum = gripLength - max > 0 ?
gripLength - max : gripLength - previewItems.length;
items.push(Caption({
object: objectLink({
object: this.props.object
@ -170,7 +183,11 @@ define(function (require, exports, module) {
return false;
}
return (grip.preview && grip.preview.kind == "ArrayLike");
return (grip.preview && (
grip.preview.kind == "ArrayLike" ||
type === "DocumentFragment"
)
);
}
// Exports from this module

Просмотреть файл

@ -37,6 +37,7 @@ window.onload = Task.async(function* () {
yield testPreviewLimit();
yield testNamedNodeMap();
yield testNodeList();
yield testDocumentFragment();
} catch(e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
@ -272,6 +273,38 @@ window.onload = Task.async(function* () {
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testDocumentFragment() {
const testName = "testDocumentFragment";
const defaultOutput = "DocumentFragment [ li#li-0.list-element, " +
"li#li-1.list-element, li#li-2.list-element, 2 more… ]";
const longOutput = "DocumentFragment [ " +
"li#li-0.list-element, li#li-1.list-element, li#li-2.list-element, " +
"li#li-3.list-element, li#li-4.list-element ]";
const modeTests = [
{
mode: undefined,
expectedOutput: defaultOutput,
},
{
mode: "tiny",
expectedOutput: `[5]`,
},
{
mode: "short",
expectedOutput: defaultOutput,
},
{
mode: "long",
expectedOutput: longOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function getGripStub(functionName) {
switch (functionName) {
case "testBasic":
@ -412,67 +445,68 @@ window.onload = Task.async(function* () {
}
};
case "testNamedNodeMap":
return {
"type": "object",
"class": "NamedNodeMap",
"actor": "server1.conn3.obj42",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 6,
"preview": {
"kind": "ArrayLike",
"length": 3,
"items": [
{
"type": "object",
"class": "Attr",
"actor": "server1.conn3.obj43",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 2,
"nodeName": "class",
"value": "myclass"
}
},
{
"type": "object",
"class": "Attr",
"actor": "server1.conn3.obj44",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 2,
"nodeName": "cellpadding",
"value": "7"
}
},
{
"type": "object",
"class": "Attr",
"actor": "server1.conn3.obj44",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 2,
"nodeName": "border",
"value": "3"
}
case "testNamedNodeMap":
return {
"type": "object",
"class": "NamedNodeMap",
"actor": "server1.conn3.obj42",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 6,
"preview": {
"kind": "ArrayLike",
"length": 3,
"items": [
{
"type": "object",
"class": "Attr",
"actor": "server1.conn3.obj43",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 2,
"nodeName": "class",
"value": "myclass"
}
]
}
};
},
{
"type": "object",
"class": "Attr",
"actor": "server1.conn3.obj44",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 2,
"nodeName": "cellpadding",
"value": "7"
}
},
{
"type": "object",
"class": "Attr",
"actor": "server1.conn3.obj44",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 2,
"nodeName": "border",
"value": "3"
}
}
]
}
};
case "testNodeList":
return {
"type": "object",
@ -549,7 +583,122 @@ window.onload = Task.async(function* () {
]
}
};
case "testDocumentFragment":
return {
"type": "object",
"actor": "server1.conn1.child1/obj45",
"class": "DocumentFragment",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 11,
"nodeName": "#document-fragment",
"childNodesLength": 5,
"childNodes": [
{
"type": "object",
"actor": "server1.conn1.child1/obj46",
"class": "HTMLLIElement",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 1,
"nodeName": "li",
"attributes": {
"id": "li-0",
"class": "list-element"
},
"attributesLength": 2
}
},
{
"type": "object",
"actor": "server1.conn1.child1/obj47",
"class": "HTMLLIElement",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 1,
"nodeName": "li",
"attributes": {
"id": "li-1",
"class": "list-element"
},
"attributesLength": 2
}
},
{
"type": "object",
"actor": "server1.conn1.child1/obj48",
"class": "HTMLLIElement",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 1,
"nodeName": "li",
"attributes": {
"id": "li-2",
"class": "list-element"
},
"attributesLength": 2
}
},
{
"type": "object",
"actor": "server1.conn1.child1/obj49",
"class": "HTMLLIElement",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 1,
"nodeName": "li",
"attributes": {
"id": "li-3",
"class": "list-element"
},
"attributesLength": 2
}
},
{
"type": "object",
"actor": "server1.conn1.child1/obj50",
"class": "HTMLLIElement",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"kind": "DOMNode",
"nodeType": 1,
"nodeName": "li",
"attributes": {
"id": "li-4",
"class": "list-element"
},
"attributesLength": 2
}
}
]
}
};
}
return null;
}
});
</script>

Просмотреть файл

@ -30,6 +30,7 @@
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/RangeBinding.h"
#include "mozilla/dom/DOMRect.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/Telemetry.h"
@ -2897,18 +2898,30 @@ GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
}
static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
mozilla::dom::DOMStringList* aTextList,
nsIContent* aContent, int32_t aStartOffset,
int32_t aEndOffset, bool aClampToEdge,
bool aFlushLayout)
{
nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
if (textFrame) {
// If we'll need it later, collect the full content text now.
nsAutoString textContent;
if (aTextList) {
mozilla::ErrorResult err; // ignored
aContent->GetTextContent(textContent, err);
}
nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
if (fend <= aStartOffset || fstart >= aEndOffset)
continue;
// Calculate the text content offsets we'll need if text is requested.
int32_t textContentStart = fstart;
int32_t textContentEnd = fend;
// overlapping with the offset we want
f->EnsureTextRun(nsTextFrame::eInflated);
NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), NS_ERROR_OUT_OF_MEMORY);
@ -2917,24 +2930,36 @@ static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
if (fstart < aStartOffset) {
// aStartOffset is within this frame
ExtractRectFromOffset(f, aStartOffset, &r, rtl, aClampToEdge);
textContentStart = aStartOffset;
}
if (fend > aEndOffset) {
// aEndOffset is in the middle of this frame
ExtractRectFromOffset(f, aEndOffset, &r, !rtl, aClampToEdge);
textContentEnd = aEndOffset;
}
r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo);
aCallback->AddRect(r);
// Finally capture the text, if requested.
if (aTextList) {
const nsAString& textSubstring =
Substring(textContent,
textContentStart,
(textContentEnd - textContentStart));
aTextList->Add(textSubstring);
}
}
}
return NS_OK;
}
/* static */ void
nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
nsRange* aRange,
nsINode* aStartParent, int32_t aStartOffset,
nsINode* aEndParent, int32_t aEndOffset,
bool aClampToEdge, bool aFlushLayout)
nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
mozilla::dom::DOMStringList* aTextList,
nsRange* aRange,
nsINode* aStartParent, int32_t aStartOffset,
nsINode* aEndParent, int32_t aEndOffset,
bool aClampToEdge, bool aFlushLayout)
{
// Hold strong pointers across the flush
nsCOMPtr<nsINode> startContainer = aStartParent;
@ -2992,11 +3017,11 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
if (node == startContainer) {
int32_t offset = startContainer == endContainer ?
aEndOffset : content->GetText()->GetLength();
GetPartialTextRect(aCollector, content, aStartOffset, offset,
GetPartialTextRect(aCollector, aTextList, content, aStartOffset, offset,
aClampToEdge, aFlushLayout);
continue;
} else if (node == endContainer) {
GetPartialTextRect(aCollector, content, 0, aEndOffset,
GetPartialTextRect(aCollector, aTextList, content, 0, aEndOffset,
aClampToEdge, aFlushLayout);
continue;
}
@ -3004,8 +3029,9 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
nsIFrame* frame = content->GetPrimaryFrame();
if (frame) {
nsLayoutUtils::GetAllInFlowRects(frame,
nsLayoutUtils::GetAllInFlowRectsAndTexts(frame,
nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector,
aTextList,
nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
}
} while (!iter.IsDone());
@ -3027,8 +3053,8 @@ nsRange::GetBoundingClientRect(bool aClampToEdge, bool aFlushLayout)
}
nsLayoutUtils::RectAccumulator accumulator;
CollectClientRects(&accumulator, this, mStartParent, mStartOffset,
mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
CollectClientRectsAndText(&accumulator, nullptr, this, mStartParent,
mStartOffset, mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
accumulator.mResultRect;
@ -3055,11 +3081,29 @@ nsRange::GetClientRects(bool aClampToEdge, bool aFlushLayout)
nsLayoutUtils::RectListBuilder builder(rectList);
CollectClientRects(&builder, this, mStartParent, mStartOffset,
mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
CollectClientRectsAndText(&builder, nullptr, this, mStartParent,
mStartOffset, mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
return rectList.forget();
}
void
nsRange::GetClientRectsAndTexts(
mozilla::dom::ClientRectsAndTexts& aResult,
ErrorResult& aErr)
{
if (!mStartParent) {
return;
}
aResult.mRectList = new DOMRectList(static_cast<nsIDOMRange*>(this));
aResult.mTextList = new DOMStringList();
nsLayoutUtils::RectListBuilder builder(aResult.mRectList);
CollectClientRectsAndText(&builder, aResult.mTextList, this,
mStartParent, mStartOffset, mEndParent, mEndOffset, true, true);
}
NS_IMETHODIMP
nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
{

Просмотреть файл

@ -25,6 +25,7 @@
namespace mozilla {
class ErrorResult;
namespace dom {
struct ClientRectsAndTexts;
class DocumentFragment;
class DOMRect;
class DOMRectList;
@ -212,6 +213,9 @@ public:
bool aFlushLayout = true);
already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
bool aFlushLayout = true);
void GetClientRectsAndTexts(
mozilla::dom::ClientRectsAndTexts& aResult,
ErrorResult& aErr);
static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
mozilla::ErrorResult& aError,
nsIContent* aStartParent,
@ -263,11 +267,16 @@ public:
static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
uint32_t aEndOffset);
static void CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
nsRange* aRange,
nsINode* aStartParent, int32_t aStartOffset,
nsINode* aEndParent, int32_t aEndOffset,
bool aClampToEdge, bool aFlushLayout);
/**
* This helper function gets rects and correlated text for the given range.
* @param aTextList optional where nullptr = don't retrieve text
*/
static void CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
mozilla::dom::DOMStringList* aTextList,
nsRange* aRange,
nsINode* aStartParent, int32_t aStartOffset,
nsINode* aEndParent, int32_t aEndOffset,
bool aClampToEdge, bool aFlushLayout);
/**
* Scan this range for -moz-user-select:none nodes and split it up into

Просмотреть файл

@ -68,6 +68,7 @@ support-files = ../file_bug357450.js
[test_fileconstructor.xul]
[test_fileconstructor_tempfile.xul]
[test_nsITextInputProcessor.xul]
[test_range_getClientRectsAndTexts.html]
[test_title.xul]
[test_windowroot.xul]
[test_swapFrameLoaders.xul]

Просмотреть файл

@ -0,0 +1,60 @@
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script>
'use strict';
SimpleTest.waitForExplicitFinish();
function runTests()
{
let range = document.createRange();
let attempts = [
{startNode: "one", start:0, endNode:"one", end:0, textList:[], message:"Empty rect"},
{startNode: "one", start:2, endNode:"one", end:6, textList:["l on"], message:"Single line"},
{startNode: "implicit", start:6, endNode:"implicit", end:12, textList:["it\nbre"], message:"Implicit break"},
{startNode: "two.a", start:1, endNode:"two.b", end:2, textList:["wo", "", "li"], message:"Two lines"},
{startNode: "embed.a", start:7, endNode:"embed.b", end:2, textList:["th ", "simple nested", " ", "te"], message:"Simple nested"},
{startNode: "deep.a", start:2, endNode:"deep.b", end:2, textList:["ne with ", "complex, more deeply nested", " ", "te"], message:"Complex nested"},
{startNode: "image.a", start:7, endNode:"image.b", end:2, textList:["th inline ", "", " ", "im"], message:"Inline image"},
];
for (let attempt of attempts) {
range.setStart(document.getElementById(attempt.startNode).childNodes[0], attempt.start);
range.setEnd(document.getElementById(attempt.endNode).childNodes[0], attempt.end);
let result = range.getClientRectsAndTexts();
is(result.textList.length, attempt.textList.length, attempt.message + " range has expected number of texts.");
let i = 0;
for (let text of result.textList) {
is(text, attempt.textList[i], attempt.message + " matched text index " + i + ".");
i++;
}
}
SimpleTest.finish();
}
</script>
</head>
<body onLoad="runTests()">
<div id="one">All on one line</div>
<div id="implicit">Implicit
break in one line</div>
<div id="two.a">Two<br/
><span id="two.b">lines</span></div>
<div id="embed.a">Line with <span>simple nested</span> <span id="embed.b">text</span></div>
<div id="deep.a">Line with <span>complex, <span>more <span>deeply <span>nested</span></span></span></span> <span id="deep.b">text</span></div>
<div id="image.a">Line with inline <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC" width="20" height="20"/> <span id="image.b">image</span></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<script>
window.onmessage = event => {
document.body.textContent = `${event.origin}|${event.data}`;
};
</script>
</head>
<body></body>
</html>

Просмотреть файл

@ -741,6 +741,8 @@ skip-if = toolkit == 'android' # Plugins don't work on Android
[test_postMessage_solidus.html]
[test_postMessages.html]
support-files = worker_postMessages.js
[test_postMessage_originAttributes.html]
support-files = file_receiveMessage.html
[test_processing_instruction_update_stylesheet.xhtml]
[test_progress_events_for_gzip_data.html]
[test_range_bounds.html]

Просмотреть файл

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Test window.postMessages from system principal to window with non-default originAttributes</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<iframe id="target-iframe"></iframe>
<script type="application/javascript">
add_task(function*() {
let iframe = document.querySelector("#target-iframe");
let win = SpecialPowers.wrap(iframe).contentWindow;
let docShell = win.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIDocShell);
// Add private browsing ID to docShell origin and load document.
docShell.setOriginAttributes({privateBrowsingId: 1});
yield new Promise(resolve => {
iframe.addEventListener("load", resolve, true);
iframe.src = SimpleTest.getTestFileURL("file_receiveMessage.html");
});
// Set up console monitor to wait for warning.
const expectedMessage = (
'Attempting to post a message to window with url ' +
'"http://mochi.test:8888/tests/dom/base/test/file_receiveMessage.html" ' +
'and origin "http://mochi.test:8888^privateBrowsingId=1" from a system ' +
'principal scope with mismatched origin "[System Principal]".');
let consolePromise = new Promise(resolve => {
SimpleTest.monitorConsole(resolve, [e => e.message == expectedMessage]);
});
// Post to the content window via SpecialPowers' system principal scope.
win.postMessage("Hello. o/", "http://mochi.test:8888");
yield new Promise(resolve => setTimeout(resolve, 0));
SimpleTest.endMonitorConsole();
yield consolePromise;
// Check that the window received and handled the message.
is(win.document.body.textContent, "|Hello. o/",
"Content window received the expected message");
});
</script>
</body>
</html>

Просмотреть файл

@ -33,14 +33,6 @@ const int kLeftStickYAxis = 1;
const int kRightStickXAxis = 2;
const int kRightStickYAxis = 3;
// Standard channel is used for managing gamepads that
// are from GamepadPlatformService. VR channel
// is for gamepads that are from VRManagerChild.
enum class GamepadServiceType : uint16_t {
Standard,
VR,
NumGamepadServiceType
};
class Gamepad final : public nsISupports,
public nsWrapperCache

Просмотреть файл

@ -590,7 +590,8 @@ GamepadManager::Update(const GamepadChangeEvent& aEvent)
if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
const GamepadAdded& a = aEvent.get_GamepadAdded();
AddGamepad(a.index(), a.id(),
a.mapping(), a.service_type(),
static_cast<GamepadMappingType>(a.mapping()),
a.service_type(),
a.num_buttons(), a.num_axes());
return;
}

Просмотреть файл

@ -11,6 +11,7 @@
#include "nsIObserver.h"
// Needed for GamepadMappingType
#include "mozilla/dom/GamepadBinding.h"
#include "mozilla/dom/GamepadServiceType.h"
class nsGlobalWindow;

Просмотреть файл

@ -95,7 +95,7 @@ GamepadPlatformService::AddGamepad(const char* aID,
uint32_t index = ++mGamepadIndex;
GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
aMapping, GamepadServiceType::Standard, aNumButtons, aNumAxes);
static_cast<uint32_t>(aMapping), GamepadServiceType::Standard, aNumButtons, aNumAxes);
NotifyGamepadChange<GamepadAdded>(a);
return index;
}

Просмотреть файл

@ -122,10 +122,8 @@ GamepadServiceTest::AddGamepad(const nsAString& aID,
return nullptr;
}
// Because GamepadServiceTest::AddGamepad() is opened for Web API,
// we need to convert aMapping from uint32_t to GamepadMappingType here.
GamepadAdded a(nsString(aID), 0,
static_cast<GamepadMappingType>(aMapping),
aMapping,
GamepadServiceType::Standard,
aNumButtons, aNumAxes);
GamepadChangeEvent e(a);

Просмотреть файл

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
using mozilla::dom::GamepadMappingType from "mozilla/dom/GamepadMessageUtils.h";
using mozilla::dom::GamepadServiceType from "mozilla/dom/GamepadMessageUtils.h";
@ -12,7 +11,10 @@ namespace dom {
struct GamepadAdded {
nsString id;
uint32_t index;
GamepadMappingType mapping;
// Ideally, mapping should be a GamepadMappingType
// But, we have dependency problems in non MOZ_GAMEPAD
// platforms. Therefore, we make it as an uint32_t here.
uint32_t mapping;
GamepadServiceType service_type;
uint32_t num_buttons;
uint32_t num_axes;

Просмотреть файл

@ -3,16 +3,10 @@
#define mozilla_dom_gamepad_GamepadMessageUtils_h
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/Gamepad.h"
#include "mozilla/dom/GamepadServiceType.h"
namespace IPC {
template<>
struct ParamTraits<mozilla::dom::GamepadMappingType> :
public ContiguousEnumSerializer<mozilla::dom::GamepadMappingType,
mozilla::dom::GamepadMappingType(mozilla::dom::GamepadMappingType::_empty),
mozilla::dom::GamepadMappingType(mozilla::dom::GamepadMappingType::EndGuard_)> {};
template<>
struct ParamTraits<mozilla::dom::GamepadServiceType> :
public ContiguousEnumSerializer<mozilla::dom::GamepadServiceType,

Просмотреть файл

@ -0,0 +1,20 @@
#ifndef mozilla_dom_GamepadServiceType_h_
#define mozilla_dom_GamepadServiceType_h_
namespace mozilla{
namespace dom{
// Standard channel is used for managing gamepads that
// are from GamepadPlatformService. VR channel
// is for gamepads that are from VRManagerChild.
enum class GamepadServiceType : uint16_t {
Standard,
VR,
NumGamepadServiceType
};
}// namespace dom
}// namespace mozilla
#endif // mozilla_dom_GamepadServiceType_h_

Просмотреть файл

@ -23,7 +23,7 @@ GamepadTestChannelParent::RecvGamepadTestEvent(const uint32_t& aID,
nsCString gamepadID;
LossyCopyUTF16toASCII(a.id(), gamepadID);
uint32_t index = service->AddGamepad(gamepadID.get(),
a.mapping(),
static_cast<GamepadMappingType>(a.mapping()),
a.num_buttons(),
a.num_axes());
if (!mShuttingdown) {

Просмотреть файл

@ -10,6 +10,11 @@ IPDL_SOURCES += [
'ipc/PGamepadTestChannel.ipdl'
]
EXPORTS.mozilla.dom += [
'ipc/GamepadMessageUtils.h',
'ipc/GamepadServiceType.h'
]
if CONFIG['MOZ_GAMEPAD']:
EXPORTS.mozilla.dom += [
'Gamepad.h',
@ -20,7 +25,6 @@ if CONFIG['MOZ_GAMEPAD']:
'GamepadServiceTest.h',
'ipc/GamepadEventChannelChild.h',
'ipc/GamepadEventChannelParent.h',
'ipc/GamepadMessageUtils.h',
'ipc/GamepadTestChannelChild.h',
'ipc/GamepadTestChannelParent.h'
]

Просмотреть файл

@ -2413,8 +2413,9 @@ TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
// We send the current layer observer epoch to the compositor so that
// TabParent knows whether a layer update notification corresponds to the
// latest SetDocShellIsActive request that was made.
ClientLayerManager *manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager();
manager->SetLayerObserverEpoch(aLayerObserverEpoch);
if (ClientLayerManager* clm = mPuppetWidget->GetLayerManager()->AsClientLayerManager()) {
clm->SetLayerObserverEpoch(aLayerObserverEpoch);
}
// docshell is consider prerendered only if not active yet
mIsPrerendered &= !aIsActive;
@ -2588,34 +2589,40 @@ TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIden
PRenderFrameChild::Send__delete__(remoteFrame);
return false;
}
nsTArray<LayersBackend> backends;
backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
bool success;
PLayerTransactionChild* shadowManager =
compositorChild->SendPLayerTransactionConstructor(backends,
aLayersId, &mTextureFactoryIdentifier, &success);
if (!success) {
NS_WARNING("failed to properly allocate layer transaction");
PRenderFrameChild::Send__delete__(remoteFrame);
return false;
}
if (!shadowManager) {
NS_WARNING("failed to construct LayersChild");
// This results in |remoteFrame| being deleted.
PRenderFrameChild::Send__delete__(remoteFrame);
return false;
}
ShadowLayerForwarder* lf =
mPuppetWidget->GetLayerManager(
shadowManager, mTextureFactoryIdentifier.mParentBackend)
nullptr, mTextureFactoryIdentifier.mParentBackend)
->AsShadowForwarder();
MOZ_ASSERT(lf && lf->HasShadowManager(),
"PuppetWidget should have shadow manager");
lf->IdentifyTextureHost(mTextureFactoryIdentifier);
ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
// As long as we are creating a ClientLayerManager for the puppet widget,
// lf must be non-null here.
MOZ_ASSERT(lf);
if (lf) {
nsTArray<LayersBackend> backends;
backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
bool success;
PLayerTransactionChild* shadowManager =
compositorChild->SendPLayerTransactionConstructor(backends,
aLayersId, &mTextureFactoryIdentifier, &success);
if (!success) {
NS_WARNING("failed to properly allocate layer transaction");
PRenderFrameChild::Send__delete__(remoteFrame);
return false;
}
if (!shadowManager) {
NS_WARNING("failed to construct LayersChild");
// This results in |remoteFrame| being deleted.
PRenderFrameChild::Send__delete__(remoteFrame);
return false;
}
lf->SetShadowManager(shadowManager);
lf->IdentifyTextureHost(mTextureFactoryIdentifier);
ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
}
mRemoteFrame = remoteFrame;
if (aLayersId != 0) {
@ -3000,6 +3007,7 @@ TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier)
RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
ClientLayerManager* clm = lm->AsClientLayerManager();
MOZ_ASSERT(clm);
mTextureFactoryIdentifier = aNewIdentifier;
clm->UpdateTextureFactoryIdentifier(aNewIdentifier);

Просмотреть файл

@ -102,22 +102,26 @@ AudioCaptureStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
AudioSegment output;
for (uint32_t i = 0; i < inputCount; i++) {
MediaStream* s = mInputs[i]->GetSource();
for (StreamTracks::TrackIter track(s->GetStreamTracks(),
MediaSegment::AUDIO);
!track.IsEnded(); track.Next()) {
StreamTracks::TrackIter track(s->GetStreamTracks(), MediaSegment::AUDIO);
if (track.IsEnded()) {
// No tracks for this input. Still we append data to trigger the mixer.
AudioSegment toMix;
toMix.AppendNullData(aTo - aFrom);
toMix.Mix(mMixer, MONO, Graph()->GraphRate());
}
for (; !track.IsEnded(); track.Next()) {
AudioSegment* inputSegment = track->Get<AudioSegment>();
StreamTime inputStart = s->GraphTimeToStreamTimeWithBlocking(aFrom);
StreamTime inputEnd = s->GraphTimeToStreamTimeWithBlocking(aTo);
if (track->IsEnded() && inputSegment->GetDuration() <= inputEnd) {
// If the input track has ended and we have consumed all its data it
// can be ignored.
continue;
}
AudioSegment toMix;
toMix.AppendSlice(*inputSegment, inputStart, inputEnd);
// Care for streams blocked in the [aTo, aFrom] range.
if (inputEnd - inputStart < aTo - aFrom) {
toMix.AppendNullData((aTo - aFrom) - (inputEnd - inputStart));
if (track->IsEnded() && inputSegment->GetDuration() <= inputStart) {
toMix.AppendNullData(aTo - aFrom);
} else {
toMix.AppendSlice(*inputSegment, inputStart, inputEnd);
// Care for streams blocked in the [aTo, aFrom] range.
if (inputEnd - inputStart < aTo - aFrom) {
toMix.AppendNullData((aTo - aFrom) - (inputEnd - inputStart));
}
}
toMix.Mix(mMixer, MONO, Graph()->GraphRate());
}

Просмотреть файл

@ -1144,14 +1144,12 @@ MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
}
// Ensure logical position is updated after seek.
UpdateLogicalPositionInternal(aVal.mEventVisibility);
UpdateLogicalPositionInternal();
if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
mOwner->SeekCompleted();
AsyncResolveSeekDOMPromiseIfExists();
if (fireEnded) {
mOwner->PlaybackEnded();
}
mOwner->SeekCompleted();
AsyncResolveSeekDOMPromiseIfExists();
if (fireEnded) {
mOwner->PlaybackEnded();
}
}
@ -1193,7 +1191,7 @@ MediaDecoder::ChangeState(PlayState aState)
}
void
MediaDecoder::UpdateLogicalPositionInternal(MediaDecoderEventVisibility aEventVisibility)
MediaDecoder::UpdateLogicalPositionInternal()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsShutdown());
@ -1211,8 +1209,7 @@ MediaDecoder::UpdateLogicalPositionInternal(MediaDecoderEventVisibility aEventVi
// frame has reflowed and the size updated beforehand.
Invalidate();
if (logicalPositionChanged &&
aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
if (logicalPositionChanged) {
FireTimeUpdate();
}
}

Просмотреть файл

@ -59,10 +59,7 @@ class MediaDecoder : public AbstractMediaDecoder
{
public:
struct SeekResolveValue {
SeekResolveValue(bool aAtEnd, MediaDecoderEventVisibility aEventVisibility)
: mAtEnd(aAtEnd), mEventVisibility(aEventVisibility) {}
bool mAtEnd;
MediaDecoderEventVisibility mEventVisibility;
};
// Used to register with MediaResource to receive notifications which will
@ -402,7 +399,7 @@ private:
// Seeking has started. Inform the element on the main thread.
void SeekingStarted();
void UpdateLogicalPositionInternal(MediaDecoderEventVisibility aEventVisibility);
void UpdateLogicalPositionInternal();
void UpdateLogicalPosition()
{
MOZ_ASSERT(NS_IsMainThread());
@ -411,7 +408,7 @@ private:
if (mPlayState == PLAY_STATE_PAUSED || IsSeeking()) {
return;
}
UpdateLogicalPositionInternal(MediaDecoderEventVisibility::Observable);
UpdateLogicalPositionInternal();
}
// Find the end of the cached data starting at the current decoder

Просмотреть файл

@ -200,7 +200,7 @@ public:
virtual void HandleWaitingForData() {}
virtual void HandleAudioCaptured() {}
virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) = 0;
virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget);
virtual RefPtr<ShutdownPromise> HandleShutdown();
@ -217,6 +217,12 @@ private:
auto ReturnTypeHelper(R(S::*)(As...)) -> R;
protected:
enum class EventVisibility : int8_t
{
Observable,
Suppressed
};
using Master = MediaDecoderStateMachine;
explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
TaskQueue* OwnerThread() const { return mMaster->mTaskQueue; }
@ -394,8 +400,7 @@ private:
* Purpose: release decoder resources to save memory and hardware resources.
*
* Transition to:
* DECODING_FIRSTFRAME when play state changes to PLAYING.
* SEEKING if any seek request.
* SEEKING if any seek request or play state changes to PLAYING.
*/
class MediaDecoderStateMachine::DormantState
: public MediaDecoderStateMachine::StateObject
@ -403,12 +408,21 @@ class MediaDecoderStateMachine::DormantState
public:
explicit DormantState(Master* aPtr) : StateObject(aPtr) {}
void Enter(SeekJob aPendingSeek)
void Enter()
{
mPendingSeek = Move(aPendingSeek);
if (mMaster->IsPlaying()) {
mMaster->StopPlayback();
}
// Calculate the position to seek to when exiting dormant.
auto t = mMaster->mMediaSink->IsStarted()
? mMaster->GetClock()
: mMaster->GetMediaTime();
mPendingSeek.mTarget = SeekTarget(t, SeekTarget::Accurate);
// SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
// need to create the promise even it is not used at all.
RefPtr<MediaDecoder::SeekPromise> x = mPendingSeek.mPromise.Ensure(__func__);
mMaster->Reset();
mMaster->mReader->ReleaseResources();
}
@ -425,8 +439,6 @@ public:
return DECODER_STATE_DORMANT;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleVideoSuspendTimeout() override
{
// Do nothing since we've released decoders in Enter().
@ -581,8 +593,6 @@ public:
CheckSlowDecoding(aDecodeStart);
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleEndOfStream() override;
void HandleWaitingForData() override
@ -686,16 +696,7 @@ private:
void EnterDormant()
{
auto t = mMaster->mMediaSink->IsStarted()
? mMaster->GetClock()
: mMaster->GetMediaTime();
SeekJob seekJob;
seekJob.mTarget = SeekTarget(t, SeekTarget::Accurate,
MediaDecoderEventVisibility::Suppressed);
// SeekJob asserts |mTarget.IsValid() == !mPromise.IsEmpty()| so we
// need to create the promise even it is not used at all.
RefPtr<MediaDecoder::SeekPromise> unused = seekJob.mPromise.Ensure(__func__);
SetState<DormantState>(Move(seekJob));
SetState<DormantState>();
}
void StartDormantTimer()
@ -753,7 +754,8 @@ class MediaDecoderStateMachine::SeekingState
public:
explicit SeekingState(Master* aPtr) : StateObject(aPtr) {}
RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob)
RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
EventVisibility aVisibility)
{
mSeekJob = Move(aSeekJob);
@ -796,8 +798,7 @@ public:
mMaster->UpdatePlaybackPositionInternal(
mSeekTask->GetSeekTarget().GetTime().ToMicroseconds());
if (mSeekJob.mTarget.mEventVisibility ==
MediaDecoderEventVisibility::Observable) {
if (aVisibility == EventVisibility::Observable) {
mMaster->mOnPlaybackEvent.Notify(MediaEventType::SeekStarted);
}
@ -848,8 +849,6 @@ public:
MOZ_ASSERT(false);
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleVideoSuspendTimeout() override
{
// Do nothing since we want a valid video frame to show when seek is done.
@ -967,8 +966,6 @@ public:
void HandleEndOfStream() override;
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleVideoSuspendTimeout() override
{
if (mMaster->HasVideo()) {
@ -1059,8 +1056,6 @@ public:
return DECODER_STATE_COMPLETED;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
void HandleAudioCaptured() override
{
// MediaSink is changed. Schedule Step() to check if we can start playback.
@ -1134,6 +1129,16 @@ public:
}
};
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
StateObject::HandleSeek(SeekTarget aTarget)
{
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob), EventVisibility::Observable);
}
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::
StateObject::HandleShutdown()
@ -1204,10 +1209,9 @@ StateObject::HandleResumeVideoDecoding()
seekJob.mTarget = SeekTarget(mMaster->GetMediaTime(),
type,
MediaDecoderEventVisibility::Suppressed,
true /* aVideoOnly */);
SetState<SeekingState>(Move(seekJob))->Then(
SetState<SeekingState>(Move(seekJob), EventVisibility::Suppressed)->Then(
AbstractThread::MainThread(), __func__,
[start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
[](){});
@ -1276,17 +1280,6 @@ DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
}
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
DormantState::HandleSeek(SeekTarget aTarget)
{
// Exit dormant when the user wants to seek.
mPendingSeek.RejectIfExists(__func__);
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob));
}
void
MediaDecoderStateMachine::
DormantState::HandlePlayStateChanged(MediaDecoder::PlayState aPlayState)
@ -1294,7 +1287,8 @@ DormantState::HandlePlayStateChanged(MediaDecoder::PlayState aPlayState)
if (aPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
// Exit dormant when the user wants to play.
MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy);
SetState<DecodingFirstFrameState>(Move(mPendingSeek));
MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent);
SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Suppressed);
}
}
@ -1313,7 +1307,7 @@ DecodingFirstFrameState::Enter(SeekJob aPendingSeek)
if (aPendingSeek.Exists() &&
(mMaster->mSentFirstFrameLoadedEvent ||
Reader()->ForceZeroStartTime())) {
SetState<SeekingState>(Move(aPendingSeek));
SetState<SeekingState>(Move(aPendingSeek), EventVisibility::Observable);
return;
}
@ -1350,10 +1344,7 @@ DecodingFirstFrameState::HandleSeek(SeekTarget aTarget)
// in Enter() if there is any pending seek.
MOZ_ASSERT(!mPendingSeek.Exists());
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob));
return StateObject::HandleSeek(aTarget);
}
void
@ -1370,7 +1361,7 @@ DecodingFirstFrameState::MaybeFinishDecodeFirstFrame()
mMaster->FinishDecodeFirstFrame();
if (mPendingSeek.Exists()) {
SetState<SeekingState>(Move(mPendingSeek));
SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Observable);
} else {
SetState<DecodingState>();
}
@ -1410,16 +1401,6 @@ DecodingState::Enter()
}
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
DecodingState::HandleSeek(SeekTarget aTarget)
{
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob));
}
void
MediaDecoderStateMachine::
DecodingState::HandleEndOfStream()
@ -1465,16 +1446,6 @@ DecodingState::MaybeStartBuffering()
}
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
SeekingState::HandleSeek(SeekTarget aTarget)
{
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob));
}
void
MediaDecoderStateMachine::
SeekingState::SeekCompleted()
@ -1612,26 +1583,6 @@ BufferingState::HandleEndOfStream()
}
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
BufferingState::HandleSeek(SeekTarget aTarget)
{
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob));
}
RefPtr<MediaDecoder::SeekPromise>
MediaDecoderStateMachine::
CompletedState::HandleSeek(SeekTarget aTarget)
{
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return SetState<SeekingState>(Move(seekJob));
}
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::
ShutdownState::Enter()

Просмотреть файл

@ -41,7 +41,8 @@ bool SeekJob::Exists() const
void SeekJob::Resolve(bool aAtEnd, const char* aCallSite)
{
MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
MediaDecoder::SeekResolveValue val;
val.mAtEnd = aAtEnd;
mPromise.Resolve(val, aCallSite);
mTarget.Reset();
}

Просмотреть файл

@ -27,37 +27,29 @@ struct SeekTarget {
NextFrame,
};
SeekTarget()
: mEventVisibility(MediaDecoderEventVisibility::Observable)
, mTime(media::TimeUnit::Invalid())
: mTime(media::TimeUnit::Invalid())
, mType(SeekTarget::Invalid)
, mVideoOnly(false)
{
}
SeekTarget(int64_t aTimeUsecs,
Type aType,
MediaDecoderEventVisibility aEventVisibility =
MediaDecoderEventVisibility::Observable,
bool aVideoOnly = false)
: mEventVisibility(aEventVisibility)
, mTime(media::TimeUnit::FromMicroseconds(aTimeUsecs))
: mTime(media::TimeUnit::FromMicroseconds(aTimeUsecs))
, mType(aType)
, mVideoOnly(aVideoOnly)
{
}
SeekTarget(const media::TimeUnit& aTime,
Type aType,
MediaDecoderEventVisibility aEventVisibility =
MediaDecoderEventVisibility::Observable,
bool aVideoOnly = false)
: mEventVisibility(aEventVisibility)
, mTime(aTime)
: mTime(aTime)
, mType(aType)
, mVideoOnly(aVideoOnly)
{
}
SeekTarget(const SeekTarget& aOther)
: mEventVisibility(aOther.mEventVisibility)
, mTime(aOther.mTime)
: mTime(aOther.mTime)
, mType(aOther.mType)
, mVideoOnly(aOther.mVideoOnly)
{
@ -97,8 +89,6 @@ struct SeekTarget {
return mVideoOnly;
}
MediaDecoderEventVisibility mEventVisibility;
private:
// Seek target time.
media::TimeUnit mTime;

Просмотреть файл

@ -11,7 +11,7 @@ from marionette_driver import Wait
from marionette_driver.errors import TimeoutException
from marionette.marionette_test import SkipTest
from firefox_puppeteer.testcases import BaseFirefoxTestCase
from firefox_puppeteer import PuppeteerMixin
from external_media_tests.utils import (timestamp_now, verbose_until)
from external_media_tests.media_utils.video_puppeteer import (
VideoException,
@ -19,7 +19,7 @@ from external_media_tests.media_utils.video_puppeteer import (
)
class MediaTestCase(BaseFirefoxTestCase, MarionetteTestCase):
class MediaTestCase(PuppeteerMixin, MarionetteTestCase):
"""
Necessary methods for MSE playback

2
dom/media/test/external/requirements.txt поставляемый
Просмотреть файл

@ -17,4 +17,4 @@ mozversion==1.4
wptserve==1.3.0
marionette-client==3.1.0
marionette-driver==2.0.0
firefox-puppeteer >= 52.0.0, <53.0.0
firefox-puppeteer >= 52.1.0, <53.0.0

Просмотреть файл

@ -86,3 +86,13 @@ partial interface Range {
DOMRectList? getClientRects();
DOMRect getBoundingClientRect();
};
dictionary ClientRectsAndTexts {
required DOMRectList rectList;
required DOMStringList textList;
};
partial interface Range {
[ChromeOnly, Throws]
ClientRectsAndTexts getClientRectsAndTexts();
};

Просмотреть файл

@ -2000,26 +2000,31 @@ EditorBase::StopPreservingSelection()
mSavedSel.MakeEmpty();
}
void
bool
EditorBase::EnsureComposition(WidgetCompositionEvent* aCompositionEvent)
{
if (mComposition) {
return;
return true;
}
// The compositionstart event must cause creating new TextComposition
// instance at being dispatched by IMEStateManager.
mComposition = IMEStateManager::GetTextCompositionFor(aCompositionEvent);
if (!mComposition) {
MOZ_CRASH("IMEStateManager doesn't return proper composition");
// However, TextComposition may be committed before the composition
// event comes here.
return false;
}
mComposition->StartHandlingComposition(this);
return true;
}
nsresult
EditorBase::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent)
{
MOZ_ASSERT(!mComposition, "There is composition already");
EnsureComposition(aCompositionEvent);
if (!EnsureComposition(aCompositionEvent)) {
return NS_OK;
}
if (mPhonetic) {
mPhonetic->Truncate(0);
}

Просмотреть файл

@ -441,8 +441,14 @@ protected:
/**
* EnsureComposition() should be called by composition event handlers. This
* tries to get the composition for the event and set it to mComposition.
* However, this may fail because the composition may be committed before
* the event comes to the editor.
*
* @return true if there is a composition. Otherwise, for example,
* a composition event handler in web contents moved focus
* for committing the composition, returns false.
*/
void EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
nsresult GetSelection(SelectionType aSelectionType,
nsISelection** aSelection);

Просмотреть файл

@ -844,7 +844,9 @@ TextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
MOZ_ASSERT(compositionChangeEvent->mMessage == eCompositionChange,
"The internal event should be eCompositionChange");
EnsureComposition(compositionChangeEvent);
if (!EnsureComposition(compositionChangeEvent)) {
return NS_OK;
}
nsCOMPtr<nsIPresShell> ps = GetPresShell();
NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);

Просмотреть файл

@ -199,6 +199,7 @@ skip-if = os == 'android'
[test_bug1181130-1.html]
[test_bug1181130-2.html]
[test_bug1186799.html]
[test_bug1230473.html]
[test_bug1247483.html]
subsuite = clipboard
skip-if = toolkit == 'android'

Просмотреть файл

@ -0,0 +1,124 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1230473
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1230473</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1230473">Mozilla Bug 1230473</a>
<input id="input">
<textarea id="textarea"></textarea>
<div id="div" contenteditable></div>
<script type="application/javascript">
/** Test for Bug 1230473 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(()=>{
function runTest(aEditor) {
function committer() {
aEditor.blur();
aEditor.focus();
}
function isNSEditableElement() {
return aEditor.tagName.toLowerCase() == "input" || aEditor.tagName.toLowerCase() == "textarea";
}
function value() {
return isNSEditableElement() ? aEditor.value : aEditor.textContent;
}
function isComposing() {
return isNSEditableElement() ? SpecialPowers.wrap(aEditor)
.QueryInterface(SpecialPowers.Ci.nsIDOMNSEditableElement)
.editor
.QueryInterface(SpecialPowers.Ci.nsIEditorIMESupport)
.composing :
SpecialPowers.wrap(window)
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
.QueryInterface(SpecialPowers.Ci.nsIDocShell)
.editor
.QueryInterface(SpecialPowers.Ci.nsIEditorIMESupport)
.composing;
}
function clear() {
if (isNSEditableElement()) {
aEditor.value = "";
} else {
aEditor.textContent = "";
}
}
clear();
// Committing at compositionstart
aEditor.focus();
aEditor.addEventListener("compositionstart", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
aEditor.removeEventListener("compositionstart", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionstart event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't insert any text since it's committed at compositionstart");
clear();
// Committing at first compositionupdate
aEditor.focus();
aEditor.addEventListener("compositionupdate", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't have inserted any text since it's committed at first compositionupdate");
clear();
// Committing at first text (eCompositionChange)
aEditor.focus();
aEditor.addEventListener("text", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
is(value(), "", "composition in " + aEditor.id + " should have inserted any text since it's committed at first text");
clear();
// Committing at second compositionupdate
aEditor.focus();
synthesizeComposition({ type: "compositionstart" });
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
ok(isComposing(), "composition should be in " + aEditor.id + " before dispatching second compositionupdate");
is(value(), "a", "composition in " + aEditor.id + " should be 'a' before dispatching second compositionupdate");
aEditor.addEventListener("compositionupdate", committer, true);
synthesizeCompositionChange({ composition: { string: "ab", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 2, length: 0 }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second compositionupdate");
clear();
// Committing at second text (eCompositionChange)
aEditor.focus();
synthesizeComposition({ type: "compositionstart" });
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
ok(isComposing(), "composition should be in " + aEditor.id + " before dispatching second text");
is(value(), "a", "composition in " + aEditor.id + " should be 'a' before dispatching second text");
aEditor.addEventListener("text", committer, true);
synthesizeCompositionChange({ composition: { string: "ab", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 2, length: 0 }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second text");
clear();
}
runTest(document.getElementById("input"));
runTest(document.getElementById("textarea"));
runTest(document.getElementById("div"));
SimpleTest.finish();
});
</script>
</body>
</html>

Просмотреть файл

@ -415,7 +415,7 @@ GPUProcessManager::DestroyProcess()
RefPtr<CompositorSession>
GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,
bool aUseExternalSurfaceSize,
@ -456,7 +456,7 @@ GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
RefPtr<CompositorSession>
GPUProcessManager::CreateRemoteSession(nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
const uint64_t& aRootLayerTreeId,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,

Просмотреть файл

@ -25,7 +25,6 @@ namespace mozilla {
namespace layers {
class IAPZCTreeManager;
class CompositorSession;
class ClientLayerManager;
class CompositorUpdateObserver;
class PCompositorBridgeChild;
class PImageBridgeChild;
@ -57,10 +56,10 @@ class GPUProcessManager final : public GPUProcessHost::Listener
{
friend class layers::RemoteCompositorSession;
typedef layers::ClientLayerManager ClientLayerManager;
typedef layers::CompositorSession CompositorSession;
typedef layers::IAPZCTreeManager IAPZCTreeManager;
typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
typedef layers::IAPZCTreeManager IAPZCTreeManager;
typedef layers::LayerManager LayerManager;
typedef layers::PCompositorBridgeChild PCompositorBridgeChild;
typedef layers::PImageBridgeChild PImageBridgeChild;
typedef layers::RemoteCompositorSession RemoteCompositorSession;
@ -82,7 +81,7 @@ public:
RefPtr<CompositorSession> CreateTopLevelCompositor(
nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,
bool aUseExternalSurfaceSize,
@ -183,7 +182,7 @@ private:
RefPtr<CompositorSession> CreateRemoteSession(
nsBaseWidget* aWidget,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
const uint64_t& aRootLayerTreeId,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,

Просмотреть файл

@ -24,7 +24,7 @@ InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidget*
/* static */ RefPtr<InProcessCompositorSession>
InProcessCompositorSession::Create(nsIWidget* aWidget,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
const uint64_t& aRootLayerTreeId,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,

Просмотреть файл

@ -20,7 +20,7 @@ class InProcessCompositorSession final : public CompositorSession
public:
static RefPtr<InProcessCompositorSession> Create(
nsIWidget* aWidget,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
const uint64_t& aRootLayerTreeId,
CSSToLayoutDeviceScale aScale,
bool aUseAPZ,

Просмотреть файл

@ -74,7 +74,7 @@ static StaticRefPtr<CompositorBridgeChild> sCompositorBridge;
Atomic<int32_t> KnowsCompositor::sSerialCounter(0);
CompositorBridgeChild::CompositorBridgeChild(ClientLayerManager *aLayerManager)
CompositorBridgeChild::CompositorBridgeChild(LayerManager *aLayerManager)
: mLayerManager(aLayerManager)
, mCanSend(false)
, mFwdTransactionId(0)
@ -256,7 +256,7 @@ CompositorBridgeChild::InitSameProcess(widget::CompositorWidget* aWidget,
/* static */ RefPtr<CompositorBridgeChild>
CompositorBridgeChild::CreateRemote(const uint64_t& aProcessToken,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
Endpoint<PCompositorBridgeChild>&& aEndpoint)
{
RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager);
@ -526,7 +526,8 @@ CompositorBridgeChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTr
{
if (mLayerManager) {
MOZ_ASSERT(aId == 0);
RefPtr<ClientLayerManager> m = mLayerManager;
RefPtr<ClientLayerManager> m = mLayerManager->AsClientLayerManager();
MOZ_ASSERT(m);
m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
} else if (aId != 0) {
RefPtr<dom::TabChild> child = dom::TabChild::GetFrom(aId);

Просмотреть файл

@ -50,7 +50,7 @@ class CompositorBridgeChild final : public PCompositorBridgeChild,
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorBridgeChild, override);
explicit CompositorBridgeChild(ClientLayerManager *aLayerManager);
explicit CompositorBridgeChild(LayerManager *aLayerManager);
void Destroy();
@ -69,7 +69,7 @@ public:
static RefPtr<CompositorBridgeChild> CreateRemote(
const uint64_t& aProcessToken,
ClientLayerManager* aLayerManager,
LayerManager* aLayerManager,
Endpoint<PCompositorBridgeChild>&& aEndpoint);
/**
@ -282,7 +282,7 @@ private:
uint32_t mAPZCId;
};
RefPtr<ClientLayerManager> mLayerManager;
RefPtr<LayerManager> mLayerManager;
// When not multi-process, hold a reference to the CompositorBridgeParent to keep it
// alive. This reference should be null in multi-process.
RefPtr<CompositorBridgeParent> mCompositorBridgeParent;

Просмотреть файл

@ -24,7 +24,8 @@ gfxQuartzNativeDrawing::BeginNativeDrawing()
NS_ASSERTION(!mCGContext, "BeginNativeDrawing called when drawing already in progress");
DrawTarget *dt = mDrawTarget;
if (dt->IsDualDrawTarget() || dt->IsTiledDrawTarget()) {
if (dt->IsDualDrawTarget() || dt->IsTiledDrawTarget() ||
dt->GetBackendType() != BackendType::SKIA) {
// We need a DrawTarget that we can get a CGContextRef from:
Matrix transform = dt->GetTransform();
@ -49,6 +50,7 @@ gfxQuartzNativeDrawing::BeginNativeDrawing()
dt = mTempDrawTarget;
}
if (dt) {
MOZ_ASSERT(dt->GetBackendType() == BackendType::SKIA);
mCGContext = mBorrowedContext.Init(dt);
MOZ_ASSERT(mCGContext);
}

Просмотреть файл

@ -173,4 +173,16 @@ uint32_t
VRControllerHost::GetIndex()
{
return mIndex;
}
}
void
VRControllerHost::SetButtonPressed(uint64_t aBit)
{
mButtonPressed = aBit;
}
uint64_t
VRControllerHost::GetButtonPressed()
{
return mButtonPressed;
}

Просмотреть файл

@ -90,6 +90,8 @@ public:
const VRControllerInfo& GetControllerInfo() const;
void SetIndex(uint32_t aIndex);
uint32_t GetIndex();
void SetButtonPressed(uint64_t aBit);
uint64_t GetButtonPressed();
protected:
explicit VRControllerHost(VRDeviceType aType);
@ -98,6 +100,8 @@ protected:
VRControllerInfo mControllerInfo;
// The controller index in VRControllerManager.
uint32_t mIndex;
// The current button pressed bit of button mask.
uint64_t mButtonPressed;
};
} // namespace gfx

Просмотреть файл

@ -6,7 +6,10 @@
#include <math.h>
#include "gfxVR.h"
#include "mozilla/dom/Gamepad.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
#endif
#ifndef M_PI
# define M_PI 3.14159265358979323846
@ -65,8 +68,7 @@ VRControllerManager::AllocateControllerID()
}
void
VRControllerManager::AddGamepad(const char* aID,
dom::GamepadMappingType aMapping,
VRControllerManager::AddGamepad(const char* aID, uint32_t aMapping,
uint32_t aNumButtons, uint32_t aNumAxes)
{
dom::GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), mControllerCount,

Просмотреть файл

@ -15,7 +15,6 @@
#include "mozilla/EnumeratedArray.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/dom/GamepadBinding.h"
namespace mozilla {
namespace layers {
@ -216,14 +215,14 @@ struct VRControllerInfo
VRDeviceType GetType() const { return mType; }
uint32_t GetControllerID() const { return mControllerID; }
const nsCString& GetControllerName() const { return mControllerName; }
dom::GamepadMappingType GetMappingType() const { return mMappingType; }
uint32_t GetMappingType() const { return mMappingType; }
uint32_t GetNumButtons() const { return mNumButtons; }
uint32_t GetNumAxes() const { return mNumAxes; }
uint32_t mControllerID;
VRDeviceType mType;
nsCString mControllerName;
dom::GamepadMappingType mMappingType;
uint32_t mMappingType;
uint32_t mNumButtons;
uint32_t mNumAxes;
@ -253,7 +252,7 @@ public:
virtual void ScanForDevices() = 0;
void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
void NewAxisMove(uint32_t aIndex, uint32_t aAxis, double aValue);
void AddGamepad(const char* aID, dom::GamepadMappingType aMapping,
void AddGamepad(const char* aID, uint32_t aMapping,
uint32_t aNumButtons, uint32_t aNumAxes);
void RemoveGamepad(uint32_t aIndex);

Просмотреть файл

@ -24,7 +24,11 @@
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
#include "openvr/openvr.h"
#include "mozilla/dom/Gamepad.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadEventTypes.h"
#include "mozilla/dom/GamepadBinding.h"
#endif
#ifndef M_PI
# define M_PI 3.14159265358979323846
@ -485,7 +489,11 @@ VRControllerOpenVR::VRControllerOpenVR()
{
MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
mControllerInfo.mControllerName.AssignLiteral("OpenVR HMD");
mControllerInfo.mMappingType = dom::GamepadMappingType::_empty;
#ifdef MOZ_GAMEPAD
mControllerInfo.mMappingType = static_cast<uint32_t>(dom::GamepadMappingType::_empty);
#else
mControllerInfo.mMappingType = 0;
#endif
mControllerInfo.mNumButtons = gNumOpenVRButtonMask;
mControllerInfo.mNumAxes = gNumOpenVRAxis;
}
@ -583,9 +591,7 @@ VRControllerManagerOpenVR::HandleInput()
== vr::TrackedDeviceClass_Controller);
if (mVRSystem->GetControllerState(controller->GetTrackedIndex(), &state)) {
if (state.ulButtonPressed) {
HandleButtonPress(controller->GetIndex(), state.ulButtonPressed);
}
HandleButtonPress(controller->GetIndex(), state.ulButtonPressed);
axis = static_cast<uint32_t>(VRControllerAxisType::TrackpadXAxis);
HandleAxisMove(controller->GetIndex(), axis,
@ -607,11 +613,26 @@ VRControllerManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
uint64_t aButtonPressed)
{
uint64_t buttonMask = 0;
RefPtr<impl::VRControllerOpenVR> controller;
controller = mOpenVRController[aControllerIdx];
uint64_t diff = (controller->GetButtonPressed() ^ aButtonPressed);
if (!diff) {
return;
}
for (uint32_t i = 0; i < gNumOpenVRButtonMask; ++i) {
buttonMask = gOpenVRButtonMask[i];
NewButtonEvent(aControllerIdx, i, aButtonPressed & buttonMask);
if (diff & buttonMask) {
// diff & aButtonPressed would be true while a new button press
// event, otherwise it is an old press event and needs to notify
// the button has been released.
NewButtonEvent(aControllerIdx, i, diff & aButtonPressed);
}
}
controller->SetButtonPressed(aButtonPressed);
}
void
@ -665,9 +686,12 @@ VRControllerManagerOpenVR::ScanForDevices()
openVRController->SetTrackedIndex(trackedDevice);
mOpenVRController.AppendElement(openVRController);
// Only in MOZ_GAMEPAD platform, We add gamepads.
#ifdef MOZ_GAMEPAD
// Not already present, add it.
AddGamepad("OpenVR Gamepad", GamepadMappingType::_empty,
AddGamepad("OpenVR Gamepad", static_cast<uint32_t>(GamepadMappingType::_empty),
gNumOpenVRButtonMask, gNumOpenVRAxis);
++mControllerCount;
#endif
}
}

Просмотреть файл

@ -15,10 +15,13 @@
#include "mozilla/dom/VREventObserver.h"
#include "mozilla/dom/WindowBinding.h" // for FrameRequestCallback
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/GamepadManager.h"
#include "mozilla/layers/TextureClient.h"
#include "nsContentUtils.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadManager.h"
#endif
using layers::TextureClient;
namespace {
@ -470,12 +473,14 @@ VRManagerChild::RecvNotifyVRVSync(const uint32_t& aDisplayID)
bool
VRManagerChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent)
{
#ifdef MOZ_GAMEPAD
// VRManagerChild could be at other processes, but GamepadManager
// only exists at the content process or the parent process
// in non-e10s mode.
if (mGamepadManager) {
mGamepadManager->Update(aGamepadEvent);
}
#endif
return true;
}

Просмотреть файл

@ -256,10 +256,18 @@ MainThreadHandoff::FixArrayElements(ICallFrame* aFrame,
return hr;
}
PVOID stackBase = aFrame->GetStackLocation();
// We dereference because we need to obtain the value of a parameter
// from a stack offset. This pointer is the base of the array.
arrayPtr = *reinterpret_cast<PVOID*>(reinterpret_cast<PBYTE>(stackBase) +
paramInfo.stackOffset);
if (aArrayData.mFlag == ArrayData::Flag::eAllocatedByServer) {
// In order for the server to allocate the array's buffer and store it in
// an outparam, the parameter must be typed as Type***. Since the base
// of the array is Type*, we must dereference twice.
arrayPtr = **reinterpret_cast<PVOID**>(reinterpret_cast<PBYTE>(stackBase) +
paramInfo.stackOffset);
} else {
// We dereference because we need to obtain the value of a parameter
// from a stack offset. This pointer is the base of the array.
arrayPtr = *reinterpret_cast<PVOID*>(reinterpret_cast<PBYTE>(stackBase) +
paramInfo.stackOffset);
}
} else if (FAILED(hr)) {
return hr;
} else {

Просмотреть файл

@ -81,21 +81,30 @@ UniquePtr<RegisteredProxy> RegisterTypelib(const wchar_t* aLeafName,
*/
struct ArrayData
{
enum class Flag
{
eNone = 0,
eAllocatedByServer = 1 // This implies an extra level of indirection
};
ArrayData(REFIID aIid, ULONG aMethodIndex, ULONG aArrayParamIndex,
VARTYPE aArrayParamType, REFIID aArrayParamIid,
ULONG aLengthParamIndex)
ULONG aLengthParamIndex, Flag aFlag = Flag::eNone)
: mIid(aIid)
, mMethodIndex(aMethodIndex)
, mArrayParamIndex(aArrayParamIndex)
, mArrayParamType(aArrayParamType)
, mArrayParamIid(aArrayParamIid)
, mLengthParamIndex(aLengthParamIndex)
, mFlag(aFlag)
{
}
ArrayData(const ArrayData& aOther)
{
*this = aOther;
}
ArrayData& operator=(const ArrayData& aOther)
{
mIid = aOther.mIid;
@ -104,14 +113,17 @@ struct ArrayData
mArrayParamType = aOther.mArrayParamType;
mArrayParamIid = aOther.mArrayParamIid;
mLengthParamIndex = aOther.mLengthParamIndex;
mFlag = aOther.mFlag;
return *this;
}
IID mIid;
ULONG mMethodIndex;
ULONG mArrayParamIndex;
VARTYPE mArrayParamType;
IID mArrayParamIid;
ULONG mLengthParamIndex;
Flag mFlag;
};
void RegisterArrayData(const ArrayData* aArrayData, size_t aLength);

Просмотреть файл

@ -97,6 +97,12 @@ uint8_t gLayerManagerUserData;
* The user data is a MaskLayerUserData.
*/
uint8_t gMaskLayerUserData;
/**
* The address of gCSSMaskLayerUserData is used as the user
* data key for mask layers of css masking managed by FrameLayerBuilder.
* The user data is a CSSMaskLayerUserData.
*/
uint8_t gCSSMaskLayerUserData;
// a global cache of image containers used for mask layers
static MaskLayerImageCache* gMaskLayerImageCache = nullptr;
@ -1252,7 +1258,9 @@ protected:
* index in the layer, if any.
*/
struct MaskLayerKey;
already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey);
already_AddRefed<ImageLayer>
CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
mozilla::function<void(Layer* aLayer)> aSetUserData);
/**
* Grabs all PaintedLayers and ColorLayers from the ContainerLayer and makes them
* available for recycling.
@ -1345,6 +1353,12 @@ protected:
Maybe<size_t> SetupMaskLayerForScrolledClip(Layer* aLayer,
const DisplayItemClip& aClip);
/*
* Create/find a mask layer with suitable size for aMaskItem to paint
* css-positioned-masking onto.
*/
void SetupMaskLayerForCSSMask(Layer* aLayer, nsDisplayMask* aMaskItem);
already_AddRefed<Layer> CreateMaskLayer(
Layer *aLayer, const DisplayItemClip& aClip,
const Maybe<size_t>& aForAncestorMaskLayer,
@ -1504,6 +1518,26 @@ struct MaskLayerUserData : public LayerUserData
, mScaleY(-1.0f)
, mAppUnitsPerDevPixel(-1)
{ }
MaskLayerUserData(const DisplayItemClip& aClip,
uint32_t aRoundedRectClipCount,
int32_t aAppUnitsPerDevPixel,
const ContainerLayerParameters& aParams)
: mScaleX(aParams.mXScale)
, mScaleY(aParams.mYScale)
, mOffset(aParams.mOffset)
, mAppUnitsPerDevPixel(aAppUnitsPerDevPixel)
{
aClip.AppendRoundedRects(&mRoundedClipRects, aRoundedRectClipCount);
}
void operator=(MaskLayerUserData&& aOther)
{
mScaleX = aOther.mScaleX;
mScaleY = aOther.mScaleY;
mOffset = aOther.mOffset;
mAppUnitsPerDevPixel = aOther.mAppUnitsPerDevPixel;
mRoundedClipRects.SwapElements(aOther.mRoundedClipRects);
}
bool
operator== (const MaskLayerUserData& aOther) const
@ -1527,6 +1561,105 @@ struct MaskLayerUserData : public LayerUserData
int32_t mAppUnitsPerDevPixel;
};
/*
* User data for layers which will be used as masks for css positioned mask.
*/
struct CSSMaskLayerUserData : public LayerUserData
{
CSSMaskLayerUserData()
: mImageLayers(nsStyleImageLayers::LayerType::Mask)
{ }
CSSMaskLayerUserData(nsIFrame* aFrame, const nsRect& aBound)
: mImageLayers(aFrame->StyleSVGReset()->mMask),
mContentRect(aFrame->GetContentRectRelativeToSelf()),
mPaddingRect(aFrame->GetPaddingRectRelativeToSelf()),
mBorderRect(aFrame->GetRectRelativeToSelf()),
mMarginRect(aFrame->GetMarginRectRelativeToSelf()),
mBounds(aBound)
{
Hash(aFrame);
}
CSSMaskLayerUserData& operator=(const CSSMaskLayerUserData& aOther)
{
mImageLayers = aOther.mImageLayers;
mContentRect = aOther.mContentRect;
mPaddingRect = aOther.mPaddingRect;
mBorderRect = aOther.mBorderRect;
mMarginRect = aOther.mMarginRect;
mBounds = aOther.mBounds;
mHash = aOther.mHash;
return *this;
}
bool
operator==(const CSSMaskLayerUserData& aOther) const
{
if (mHash != aOther.mHash) {
return false;
}
if (mImageLayers.mLayers != aOther.mImageLayers.mLayers) {
return false;
}
if (!mContentRect.IsEqualEdges(aOther.mContentRect) ||
!mPaddingRect.IsEqualEdges(aOther.mPaddingRect) ||
!mBorderRect.IsEqualEdges(aOther.mBorderRect) ||
!mMarginRect.IsEqualEdges(aOther.mMarginRect)) {
return false;
}
if (!mBounds.IsEqualEdges(aOther.mBounds)) {
return false;
}
return true;
}
private:
void Hash(nsIFrame* aFrame)
{
uint32_t hash = 0;
const nsStyleImageLayers& imageLayers = aFrame->StyleSVGReset()->mMask;
for (uint32_t i = 0; i < imageLayers.mLayers.Length(); i++) {
const nsStyleImageLayers::Layer& newLayer = imageLayers.mLayers[i];
hash = AddToHash(hash, HashBytes(&newLayer, sizeof(newLayer)));
}
hash = AddToHash(hash, HashBytes(&mContentRect, sizeof(mContentRect)));
hash = AddToHash(hash, HashBytes(&mPaddingRect, sizeof(mPaddingRect)));
hash = AddToHash(hash, HashBytes(&mBorderRect, sizeof(mBorderRect)));
hash = AddToHash(hash, HashBytes(&mMarginRect, sizeof(mMarginRect)));
hash = AddToHash(hash, HashBytes(&mBounds, sizeof(mBounds)));
mHash = hash;
}
nsStyleImageLayers mImageLayers;
nsRect mContentRect;
nsRect mPaddingRect;
nsRect mBorderRect;
nsRect mMarginRect;
nsRect mBounds;
uint32_t mHash;
};
/*
* A helper object to create a draw target for painting mask and create a
* image container to hold the drawing result. The caller can then bind this
* image container with a image mask layer via ImageLayer::SetContainer.
*/
class MaskImageData
{
public:
@ -1535,6 +1668,8 @@ public:
, mSize(aSize)
, mLayerManager(aLayerManager)
{
MOZ_ASSERT(!mSize.IsEmpty());
MOZ_ASSERT(mLayerManager);
}
~MaskImageData()
@ -1549,7 +1684,6 @@ public:
gfx::DrawTarget* CreateDrawTarget()
{
MOZ_ASSERT(mLayerManager);
if (mDrawTarget) {
return mDrawTarget;
}
@ -1585,6 +1719,20 @@ public:
return mDrawTarget;
}
already_AddRefed<ImageContainer> CreateImageAndImageContainer()
{
RefPtr<ImageContainer> container = mLayerManager->CreateImageContainer();
RefPtr<Image> image = CreateImage();
if (!image) {
return nullptr;
}
container->SetCurrentImageInTransaction(image);
return container.forget();
}
private:
already_AddRefed<Image> CreateImage()
{
if (mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC &&
@ -1604,10 +1752,10 @@ public:
new TextureWrapperImage(mTextureClient, gfx::IntRect(gfx::IntPoint(0, 0), mSize));
return image.forget();
}
return nullptr;
}
private:
bool mTextureClientLocked;
gfx::IntSize mSize;
LayerManager* mLayerManager;
@ -2107,7 +2255,8 @@ ContainerState::CreateOrRecycleImageLayer(PaintedLayer *aPainted)
}
already_AddRefed<ImageLayer>
ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey)
ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
mozilla::function<void(Layer* aLayer)> aSetUserData)
{
RefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aKey);
if (result) {
@ -2119,7 +2268,7 @@ ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey)
result = mManager->CreateImageLayer();
if (!result)
return nullptr;
result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData());
aSetUserData(result);
}
return result.forget();
@ -3745,6 +3894,80 @@ ContainerState::SetupMaskLayerForScrolledClip(Layer* aLayer,
return Nothing();
}
void
ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
nsDisplayMask* aMaskItem)
{
MOZ_ASSERT(mManager->IsCompositingCheap());
RefPtr<ImageLayer> maskLayer =
CreateOrRecycleMaskImageLayerFor(MaskLayerKey(aLayer, Nothing()),
[](Layer* aMaskLayer)
{
aMaskLayer->SetUserData(&gCSSMaskLayerUserData,
new CSSMaskLayerUserData());
}
);
CSSMaskLayerUserData* oldUserData =
static_cast<CSSMaskLayerUserData*>(maskLayer->GetUserData(&gCSSMaskLayerUserData));
bool snap;
nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap);
CSSMaskLayerUserData newUserData(aMaskItem->Frame(), bounds);
if (*oldUserData == newUserData) {
aLayer->SetMaskLayer(maskLayer);
return;
}
const nsIFrame* frame = aMaskItem->Frame();
int32_t A2D = frame->PresContext()->AppUnitsPerDevPixel();
Rect devBounds = NSRectToRect(bounds, A2D);
uint32_t maxSize = mManager->GetMaxTextureSize();
gfx::Size surfaceSize(std::min<gfx::Float>(devBounds.Width(), maxSize),
std::min<gfx::Float>(devBounds.Height(), maxSize));
IntSize surfaceSizeInt(NSToIntCeil(surfaceSize.width),
NSToIntCeil(surfaceSize.height));
if (surfaceSizeInt.IsEmpty()) {
return;
}
MaskImageData imageData(surfaceSizeInt, mManager);
RefPtr<DrawTarget> dt = imageData.CreateDrawTarget();
if (!dt || !dt->IsValid()) {
NS_WARNING("Could not create DrawTarget for mask layer.");
return;
}
RefPtr<gfxContext> maskCtx = gfxContext::CreateOrNull(dt);
gfxPoint offset = nsLayoutUtils::PointToGfxPoint(bounds.TopLeft(), A2D);
maskCtx->SetMatrix(gfxMatrix::Translation(-offset));
if (!aMaskItem->PaintMask(mBuilder, maskCtx)) {
// Mostly because of mask resource is not ready.
return;
}
// Setup mask layer offset.
Matrix4x4 matrix;
matrix.PreTranslate(offset.x, offset.y, 0);
matrix.PreTranslate(mParameters.mOffset.x, mParameters.mOffset.y, 0);
matrix.PreScale(mParameters.mXScale, mParameters.mYScale, 1.0);
maskLayer->SetBaseTransform(matrix);
RefPtr<ImageContainer> imgContainer =
imageData.CreateImageAndImageContainer();
if (!imgContainer) {
return;
}
maskLayer->SetContainer(imgContainer);
*oldUserData = newUserData;
aLayer->SetMaskLayer(maskLayer);
}
/*
* Iterate through the non-clip items in aList and its descendants.
* For each item we compute the effective clip rect. Each item is assigned
@ -4148,6 +4371,9 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
SetupMaskLayer(ownLayer, layerClip);
}
}
} else if (item->GetType() == nsDisplayItem::TYPE_MASK) {
nsDisplayMask* maskItem = static_cast<nsDisplayMask*>(item);
SetupMaskLayerForCSSMask(ownLayer, maskItem);
}
ContainerLayer* oldContainer = ownLayer->GetParent();
@ -6026,18 +6252,27 @@ ContainerState::CreateMaskLayer(Layer *aLayer,
const Maybe<size_t>& aForAncestorMaskLayer,
uint32_t aRoundedRectClipCount)
{
// aLayer will never be the container layer created by an nsDisplayMask
// because nsDisplayMask propagates the DisplayItemClip to its contents
// and is not clipped itself.
// This assertion will fail if that ever stops being the case.
MOZ_ASSERT(!aLayer->GetUserData(&gCSSMaskLayerUserData),
"A layer contains round clips should not have css-mask on it.");
// check if we can re-use the mask layer
MaskLayerKey recycleKey(aLayer, aForAncestorMaskLayer);
RefPtr<ImageLayer> maskLayer = CreateOrRecycleMaskImageLayerFor(recycleKey);
RefPtr<ImageLayer> maskLayer =
CreateOrRecycleMaskImageLayerFor(recycleKey,
[](Layer* aMaskLayer)
{
aMaskLayer->SetUserData(&gMaskLayerUserData,
new MaskLayerUserData());
}
);
MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer);
MaskLayerUserData newData;
aClip.AppendRoundedRects(&newData.mRoundedClipRects, aRoundedRectClipCount);
newData.mScaleX = mParameters.mXScale;
newData.mScaleY = mParameters.mYScale;
newData.mOffset = mParameters.mOffset;
newData.mAppUnitsPerDevPixel = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
int32_t A2D = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
MaskLayerUserData newData(aClip, aRoundedRectClipCount, A2D, mParameters);
if (*userData == newData) {
return maskLayer.forget();
}
@ -6114,14 +6349,13 @@ ContainerState::CreateMaskLayer(Layer *aLayer,
aRoundedRectClipCount);
// build the image and container
container = aLayer->Manager()->CreateImageContainer();
MOZ_ASSERT(aLayer->Manager() == mManager);
container = imageData.CreateImageAndImageContainer();
NS_ASSERTION(container, "Could not create image container for mask layer.");
RefPtr<Image> image = imageData.CreateImage();
if (!image) {
if (!container) {
return nullptr;
}
container->SetCurrentImageInTransaction(image);
GetMaskLayerImageCache()->PutImage(newKey.forget(), container);
}
@ -6134,11 +6368,7 @@ ContainerState::CreateMaskLayer(Layer *aLayer,
maskLayer->SetBaseTransform(matrix);
// save the details of the clip in user data
userData->mScaleX = newData.mScaleX;
userData->mScaleY = newData.mScaleY;
userData->mOffset = newData.mOffset;
userData->mAppUnitsPerDevPixel = newData.mAppUnitsPerDevPixel;
userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects);
*userData = Move(newData);
userData->mImageKey.Reset(lookupKey);
return maskLayer.forget();

Просмотреть файл

@ -24,6 +24,7 @@ class nsDisplayList;
class nsDisplayItem;
class gfxContext;
class nsDisplayItemGeometry;
class nsDisplayMask;
namespace mozilla {
class DisplayItemScrollClip;

Просмотреть файл

@ -7052,14 +7052,75 @@ nsDisplayMask::BuildLayer(nsDisplayListBuilder* aBuilder,
return container.forget();
}
bool
nsDisplayMask::PaintMask(nsDisplayListBuilder* aBuilder,
gfxContext* aMaskContext)
{
MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
nsSVGIntegrationUtils::PaintFramesParams params(*aMaskContext,
mFrame, mVisibleRect,
borderArea, aBuilder,
nullptr,
mHandleOpacity);
ComputeMaskGeometry(params);
image::DrawResult result = nsSVGIntegrationUtils::PaintMask(params);
nsDisplayMaskGeometry::UpdateDrawResult(this, result);
return (result == image::DrawResult::SUCCESS) ? true : false;
}
LayerState
nsDisplayMask::GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aParameters)
{
if (ShouldPaintOnMaskLayer(aManager)) {
return RequiredLayerStateForChildren(aBuilder, aManager, aParameters,
mList, GetAnimatedGeometryRoot());
}
return LAYER_SVG_EFFECTS;
}
bool nsDisplayMask::ShouldPaintOnMaskLayer(LayerManager* aManager)
{
if (!aManager->IsCompositingCheap()) {
return false;
}
nsSVGIntegrationUtils::MaskUsage maskUsage;
nsSVGIntegrationUtils::DetermineMaskUsage(mFrame, mHandleOpacity, maskUsage);
if (!maskUsage.shouldGenerateMaskLayer ||
maskUsage.opacity != 1.0 || maskUsage.shouldApplyClipPath ||
maskUsage.shouldApplyBasicShape ||
maskUsage.shouldGenerateClipMaskLayer) {
return false;
}
if (!nsSVGIntegrationUtils::IsMaskResourceReady(mFrame)) {
return false;
}
// XXX temporary disable drawing SVG mask onto mask layer before bug 1313877
// been fixed.
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
for (size_t i = 0; i < maskFrames.Length() ; i++) {
nsSVGMaskFrame *maskFrame = maskFrames[i];
if (maskFrame) {
return false; // Found SVG mask.
}
}
return true;
}
bool nsDisplayMask::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion)
{
@ -7113,6 +7174,8 @@ nsDisplayMask::PaintAsLayer(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx,
LayerManager* aManager)
{
MOZ_ASSERT(!ShouldPaintOnMaskLayer(aManager));
nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(),
mFrame, mVisibleRect,

Просмотреть файл

@ -3878,6 +3878,9 @@ protected:
*/
class nsDisplayMask : public nsDisplaySVGEffects {
public:
typedef mozilla::layers::ImageLayer ImageLayer;
typedef class mozilla::gfx::DrawTarget DrawTarget;
nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, bool aHandleOpacity,
const DisplayItemScrollClip* aScrollClip);
@ -3913,11 +3916,20 @@ public:
nsRenderingContext* aCtx,
LayerManager* aManager);
/*
* Paint mask onto aMaskContext in mFrame's coordinate space.
*/
bool PaintMask(nsDisplayListBuilder* aBuilder, gfxContext* aMaskContext);
const nsTArray<nsRect>& GetDestRects()
{
return mDestRects;
}
private:
// According to mask property and the capability of aManager, determine
// whether paint mask onto a dedicate mask layer.
bool ShouldPaintOnMaskLayer(LayerManager* aManager);
nsTArray<nsRect> mDestRects;
};

Просмотреть файл

@ -70,6 +70,7 @@
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/DOMRect.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/KeyframeEffectReadOnly.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "imgIRequest.h"
@ -3938,6 +3939,25 @@ struct BoxToRect : public nsLayoutUtils::BoxCallback {
}
};
struct BoxToRectAndText : public BoxToRect {
mozilla::dom::DOMStringList* mTextList;
BoxToRectAndText(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
mozilla::dom::DOMStringList* aTextList, uint32_t aFlags)
: BoxToRect(aRelativeTo, aCallback, aFlags), mTextList(aTextList) {}
virtual void AddBox(nsIFrame* aFrame) override {
BoxToRect::AddBox(aFrame);
if (mTextList) {
nsIContent* content = aFrame->GetContent();
nsAutoString textContent;
mozilla::ErrorResult err; // ignored
content->GetTextContent(textContent, err);
mTextList->Add(textContent);
}
}
};
void
nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
RectCallback* aCallback, uint32_t aFlags)
@ -3946,6 +3966,16 @@ nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
GetAllInFlowBoxes(aFrame, &converter);
}
void
nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame* aFrame, nsIFrame* aRelativeTo,
RectCallback* aCallback,
mozilla::dom::DOMStringList* aTextList,
uint32_t aFlags)
{
BoxToRectAndText converter(aRelativeTo, aCallback, aTextList, aFlags);
GetAllInFlowBoxes(aFrame, &converter);
}
nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) {
@ -8869,7 +8899,7 @@ nsLayoutUtils::GetSelectionBoundingRect(Selection* aSel)
RectAccumulator accumulator;
for (int32_t idx = 0; idx < rangeCount; ++idx) {
nsRange* range = aSel->GetRangeAt(idx);
nsRange::CollectClientRects(&accumulator, range,
nsRange::CollectClientRectsAndText(&accumulator, nullptr, range,
range->GetStartParent(), range->StartOffset(),
range->GetEndParent(), range->EndOffset(),
true, false);

Просмотреть файл

@ -1198,6 +1198,11 @@ public:
static void GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
RectCallback* aCallback, uint32_t aFlags = 0);
static void GetAllInFlowRectsAndTexts(nsIFrame* aFrame, nsIFrame* aRelativeTo,
RectCallback* aCallback,
mozilla::dom::DOMStringList* aTextList,
uint32_t aFlags = 0);
/**
* Computes the union of all rects returned by GetAllInFlowRects. If
* the union is empty, returns the first rect.

Просмотреть файл

@ -5886,7 +5886,7 @@ Selection::ContainsPoint(const nsPoint& aPoint)
PointInRectChecker checker(aPoint);
for (uint32_t i = 0; i < RangeCount(); i++) {
nsRange* range = GetRangeAt(i);
nsRange::CollectClientRects(&checker, range,
nsRange::CollectClientRectsAndText(&checker, nullptr, range,
range->GetStartParent(), range->StartOffset(),
range->GetEndParent(), range->EndOffset(),
true, false);

Просмотреть файл

@ -2621,6 +2621,41 @@ nsStyleImageLayers::HasLayerWithImage() const
return false;
}
nsStyleImageLayers&
nsStyleImageLayers::operator=(const nsStyleImageLayers& aOther)
{
mAttachmentCount = aOther.mAttachmentCount;
mClipCount = aOther.mClipCount;
mOriginCount = aOther.mOriginCount;
mRepeatCount = aOther.mRepeatCount;
mPositionXCount = aOther.mPositionXCount;
mPositionYCount = aOther.mPositionYCount;
mImageCount = aOther.mImageCount;
mSizeCount = aOther.mSizeCount;
mMaskModeCount = aOther.mMaskModeCount;
mBlendModeCount = aOther.mBlendModeCount;
mCompositeCount = aOther.mCompositeCount;
mLayers = aOther.mLayers;
uint32_t count = mLayers.Length();
if (count != aOther.mLayers.Length()) {
NS_WARNING("truncating counts due to out-of-memory");
mAttachmentCount = std::max(mAttachmentCount, count);
mClipCount = std::max(mClipCount, count);
mOriginCount = std::max(mOriginCount, count);
mRepeatCount = std::max(mRepeatCount, count);
mPositionXCount = std::max(mPositionXCount, count);
mPositionYCount = std::max(mPositionYCount, count);
mImageCount = std::max(mImageCount, count);
mSizeCount = std::max(mSizeCount, count);
mMaskModeCount = std::max(mMaskModeCount, count);
mBlendModeCount = std::max(mBlendModeCount, count);
mCompositeCount = std::max(mCompositeCount, count);
}
return *this;
}
bool
nsStyleImageLayers::IsInitialPositionForLayerType(Position aPosition, LayerType aType)
{

Просмотреть файл

@ -892,6 +892,7 @@ struct nsStyleImageLayers {
nsStyleImageLayers::LayerType aType) const;
bool HasLayerWithImage() const;
nsStyleImageLayers& operator=(const nsStyleImageLayers& aOther);
static const nsCSSPropertyID kBackgroundLayerTable[];
static const nsCSSPropertyID kMaskLayerTable[];

Просмотреть файл

@ -415,7 +415,7 @@ private:
* Returns true if any of the masks is an image mask (and not an SVG mask).
*/
static bool
HasNonSVGMask(const nsTArray<nsSVGMaskFrame *>& aMaskFrames)
HasNonSVGMask(const nsTArray<nsSVGMaskFrame*>& aMaskFrames)
{
for (size_t i = 0; i < aMaskFrames.Length() ; i++) {
nsSVGMaskFrame *maskFrame = aMaskFrames[i];
@ -429,67 +429,34 @@ HasNonSVGMask(const nsTArray<nsSVGMaskFrame *>& aMaskFrames)
typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
/**
* Paint css-positioned-mask onto a given target(aMaskDT).
*/
static DrawResult
GenerateMaskSurface(const PaintFramesParams& aParams,
float aOpacity, nsStyleContext* aSC,
const nsTArray<nsSVGMaskFrame *>& aMaskFrames,
const nsPoint& aOffsetToUserSpace,
Matrix& aOutMaskTransform,
RefPtr<SourceSurface>& aOutMaskSurface,
bool& aOpacityApplied)
PaintMaskSurface(const PaintFramesParams& aParams,
DrawTarget* aMaskDT, float aOpacity, nsStyleContext* aSC,
const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
const gfxMatrix& aMaskSurfaceMatrix,
const nsPoint& aOffsetToUserSpace)
{
const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
MOZ_ASSERT(aMaskFrames.Length() > 0);
MOZ_ASSERT(aMaskDT->GetFormat() == SurfaceFormat::A8);
const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
gfxMatrix cssPxToDevPxMatrix =
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
gfxContext& ctx = aParams.ctx;
// There is only one SVG mask.
if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
aOpacityApplied = true;
aOutMaskSurface =
aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame,
cssPxToDevPxMatrix, aOpacity,
&aOutMaskTransform,
svgReset->mMask.mLayers[0].mMaskMode);
return DrawResult::SUCCESS;
}
const IntRect& maskSurfaceRect = aParams.maskRect;
if (maskSurfaceRect.IsEmpty()) {
return DrawResult::SUCCESS;
}
RefPtr<DrawTarget> maskDT =
ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
SurfaceFormat::A8);
if (!maskDT || !maskDT->IsValid()) {
return DrawResult::TEMPORARY_ERROR;
}
RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(maskDT);
MOZ_ASSERT(maskContext);
nsPresContext* presContext = aParams.frame->PresContext();
gfxPoint devPixelOffsetToUserSpace =
nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
presContext->AppUnitsPerDevPixel());
// Set ctx's matrix on maskContext, offset by the maskSurfaceRect's position.
// This makes sure that we combine the masks in device space.
gfxMatrix maskSurfaceMatrix =
ctx.CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft());
maskContext->SetMatrix(maskSurfaceMatrix);
// Set aAppliedOpacity as true only if all mask layers are svg mask.
// In this case, we will apply opacity into the final mask surface, so the
// caller does not need to apply it again.
aOpacityApplied = !HasNonSVGMask(aMaskFrames);
RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(aMaskDT);
MOZ_ASSERT(maskContext);
maskContext->SetMatrix(aMaskSurfaceMatrix);
// Multiple SVG masks interleave with image mask. Paint each layer onto
// maskDT one at a time.
// aMaskDT one at a time.
for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) {
nsSVGMaskFrame *maskFrame = aMaskFrames[i];
@ -504,7 +471,7 @@ GenerateMaskSurface(const PaintFramesParams& aParams,
RefPtr<SourceSurface> svgMask =
maskFrame->GetMaskForMaskedFrame(maskContext, aParams.frame,
cssPxToDevPxMatrix,
aOpacityApplied ? aOpacity : 1.0,
aOpacity,
&svgMaskMatrix,
svgReset->mMask.mLayers[i].mMaskMode);
if (svgMask) {
@ -512,7 +479,7 @@ GenerateMaskSurface(const PaintFramesParams& aParams,
maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
Rect drawRect = IntRectToRect(IntRect(IntPoint(0, 0), svgMask->GetSize()));
maskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
drawRect.TopLeft(),
DrawOptions(1.0, compositionOp));
}
@ -539,6 +506,67 @@ GenerateMaskSurface(const PaintFramesParams& aParams,
}
}
return DrawResult::SUCCESS;
}
static DrawResult
CreateAndPaintMaskSurface(const PaintFramesParams& aParams,
float aOpacity, nsStyleContext* aSC,
const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
const nsPoint& aOffsetToUserSpace,
Matrix& aOutMaskTransform,
RefPtr<SourceSurface>& aOutMaskSurface,
bool& aOpacityApplied)
{
const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
MOZ_ASSERT(aMaskFrames.Length() > 0);
gfxContext& ctx = aParams.ctx;
// There is only one SVG mask.
if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
gfxMatrix cssPxToDevPxMatrix =
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
aOpacityApplied = true;
aOutMaskSurface =
aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame,
cssPxToDevPxMatrix, aOpacity,
&aOutMaskTransform,
svgReset->mMask.mLayers[0].mMaskMode);
return DrawResult::SUCCESS;
}
const IntRect& maskSurfaceRect = aParams.maskRect;
if (maskSurfaceRect.IsEmpty()) {
return DrawResult::SUCCESS;
}
RefPtr<DrawTarget> maskDT =
ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
SurfaceFormat::A8);
if (!maskDT || !maskDT->IsValid()) {
return DrawResult::TEMPORARY_ERROR;
}
// Set aAppliedOpacity as true only if all mask layers are svg mask.
// In this case, we will apply opacity into the final mask surface, so the
// caller does not need to apply it again.
aOpacityApplied = !HasNonSVGMask(aMaskFrames);
// Set context's matrix on maskContext, offset by the maskSurfaceRect's
// position. This makes sure that we combine the masks in device space.
gfxMatrix maskSurfaceMatrix =
ctx.CurrentMatrix() * gfxMatrix::Translation(-aParams.maskRect.TopLeft());
DrawResult result = PaintMaskSurface(aParams, maskDT,
aOpacityApplied ? aOpacity : 1.0,
aSC, aMaskFrames, maskSurfaceMatrix,
aOffsetToUserSpace);
if (result != DrawResult::SUCCESS) {
return result;
}
aOutMaskTransform = ToMatrix(maskSurfaceMatrix);
if (!aOutMaskTransform.Invert()) {
return DrawResult::SUCCESS;
@ -549,13 +577,12 @@ GenerateMaskSurface(const PaintFramesParams& aParams,
}
static float
ComputeOpacity(const PaintFramesParams& aParams)
ComputeOpacity(nsIFrame* aFrame, bool aHandleOpacity)
{
nsIFrame* frame = aParams.frame;
float opacity = frame->StyleEffects()->mOpacity;
float opacity = aFrame->StyleEffects()->mOpacity;
if (opacity != 1.0f &&
(nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.handleOpacity)) {
(nsSVGUtils::CanOptimizeOpacity(aFrame) || !aHandleOpacity)) {
return 1.0f;
}
@ -658,6 +685,136 @@ SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
}
}
void
nsSVGIntegrationUtils::DetermineMaskUsage(nsIFrame* aFrame,
bool aHandleOpacity,
MaskUsage& aUsage)
{
aUsage.opacity = ComputeOpacity(aFrame, aHandleOpacity);
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
// For a HTML doc:
// According to css-masking spec, always create a mask surface when we
// have any item in maskFrame even if all of those items are
// non-resolvable <mask-sources> or <images>, we still need to create a
// transparent black mask layer under this condition.
// For a SVG doc:
// SVG 1.1 say that if we fail to resolve a mask, we should draw the
// object unmasked.
aUsage.shouldGenerateMaskLayer =
(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)
? maskFrames.Length() == 1 && maskFrames[0]
: maskFrames.Length() > 0;
#else
// Since we do not support image mask so far, we should treat any
// unresolvable mask as no mask. Otherwise, any object with a valid image
// mask, e.g. url("xxx.png"), will become invisible just because we can not
// handle image mask correctly. (See bug 1294171)
aUsage.shouldGenerateMaskLayer = maskFrames.Length() == 1 && maskFrames[0];
#endif
bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
MOZ_ASSERT_IF(clipPathFrame,
svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
switch (svgReset->mClipPath.GetType()) {
case StyleShapeSourceType::URL:
if (clipPathFrame) {
if (clipPathFrame->IsTrivial()) {
aUsage.shouldApplyClipPath = true;
} else {
aUsage.shouldGenerateClipMaskLayer = true;
}
}
break;
case StyleShapeSourceType::Shape:
case StyleShapeSourceType::Box:
aUsage.shouldApplyBasicShape = true;
break;
case StyleShapeSourceType::None:
MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer &&
!aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape);
break;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
break;
}
}
bool
nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
{
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
for (uint32_t i = 0; i < maskFrames.Length(); i++) {
// Refers to a valid SVG mask.
if (maskFrames[i]) {
continue;
}
// Refers to an external resource, which is not ready yet.
if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) {
return false;
}
}
// Either all mask resources are ready, or no mask resource is needed.
return true;
}
DrawResult
nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
{
MaskUsage maskUsage;
DetermineMaskUsage(aParams.frame, aParams.handleOpacity, maskUsage);
MOZ_ASSERT(maskUsage.shouldGenerateMaskLayer);
nsIFrame* frame = aParams.frame;
if (!ValidateSVGFrame(frame)) {
return DrawResult::SUCCESS;
}
if (maskUsage.opacity == 0.0f) {
return DrawResult::SUCCESS;
}
gfxContext& ctx = aParams.ctx;
gfxContextMatrixAutoSaveRestore matSR(&ctx);
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
bool opacityApplied = !HasNonSVGMask(maskFrames);
nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace;
SetupContextMatrix(frame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
return PaintMaskSurface(aParams, ctx.GetDrawTarget(),
opacityApplied ? maskUsage.opacity : 1.0,
firstFrame->StyleContext(), maskFrames,
ctx.CurrentMatrix(), offsetToUserSpace);
}
DrawResult
nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
{
@ -684,8 +841,10 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
return result;
}
float opacity = ComputeOpacity(aParams);
if (opacity == 0.0f) {
MaskUsage maskUsage;
DetermineMaskUsage(aParams.frame, aParams.handleOpacity, maskUsage);
if (maskUsage.opacity == 0.0f) {
return DrawResult::SUCCESS;
}
@ -699,65 +858,18 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
// For a HTML doc:
// According to css-masking spec, always create a mask surface when we
// have any item in maskFrame even if all of those items are
// non-resolvable <mask-sources> or <images>, we still need to create a
// transparent black mask layer under this condition.
// For a SVG doc:
// SVG 1.1 say that if we fail to resolve a mask, we should draw the
// object unmasked.
bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
bool shouldGenerateMaskLayer = hasSVGLayout
? maskFrames.Length() == 1 && maskFrames[0]
: maskFrames.Length() > 0;
#else
// Since we do not support image mask so far, we should treat any
// unresolvable mask as no mask. Otherwise, any object with a valid image
// mask, e.g. url("xxx.png"), will become invisible just because we can not
// handle image mask correctly. (See bug 1294171)
bool shouldGenerateMaskLayer = maskFrames.Length() == 1 && maskFrames[0];
#endif
bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
MOZ_ASSERT_IF(clipPathFrame,
svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
bool shouldGenerateClipMaskLayer = false;
bool shouldApplyClipPath = false;
bool shouldApplyBasicShape = false;
switch (svgReset->mClipPath.GetType()) {
case StyleShapeSourceType::URL:
if (clipPathFrame) {
if (clipPathFrame->IsTrivial()) {
shouldApplyClipPath = true;
} else {
shouldGenerateClipMaskLayer = true;
}
}
break;
case StyleShapeSourceType::Shape:
case StyleShapeSourceType::Box:
shouldApplyBasicShape = true;
break;
case StyleShapeSourceType::None:
break;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
break;
}
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace;
bool shouldGenerateMask = (opacity != 1.0f || shouldGenerateClipMaskLayer ||
shouldGenerateMaskLayer);
bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
maskUsage.shouldGenerateClipMaskLayer ||
maskUsage.shouldGenerateMaskLayer);
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
@ -768,7 +880,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
RefPtr<SourceSurface> maskSurface;
bool opacityApplied = false;
if (shouldGenerateMaskLayer) {
if (maskUsage.shouldGenerateMaskLayer) {
matSR.SetContext(&context);
// For css-mask, we want to generate a mask for each continuation frame,
@ -776,17 +888,18 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
// instead of the first continuation frame.
SetupContextMatrix(frame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
result = GenerateMaskSurface(aParams, opacity,
firstFrame->StyleContext(),
maskFrames, offsetToUserSpace,
maskTransform, maskSurface, opacityApplied);
result = CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
firstFrame->StyleContext(),
maskFrames, offsetToUserSpace,
maskTransform, maskSurface,
opacityApplied);
if (!maskSurface) {
// Entire surface is clipped out.
return result;
}
}
if (shouldGenerateClipMaskLayer) {
if (maskUsage.shouldGenerateClipMaskLayer) {
matSR.Restore();
matSR.SetContext(&context);
@ -809,8 +922,9 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
}
// opacity != 1.0f.
if (!shouldGenerateClipMaskLayer && !shouldGenerateMaskLayer) {
MOZ_ASSERT(opacity != 1.0f);
if (!maskUsage.shouldGenerateClipMaskLayer &&
!maskUsage.shouldGenerateMaskLayer) {
MOZ_ASSERT(maskUsage.opacity != 1.0f);
matSR.SetContext(&context);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
@ -819,11 +933,13 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
if (aParams.layerManager->GetRoot()->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
opacityApplied ? 1.0 : opacity,
opacityApplied
? 1.0
: maskUsage.opacity,
maskSurface, maskTransform);
} else {
context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
opacityApplied ? 1.0 : opacity,
opacityApplied ? 1.0 : maskUsage.opacity,
maskSurface, maskTransform);
}
}
@ -831,14 +947,15 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
* we can just do normal painting and get it clipped appropriately.
*/
if (shouldApplyClipPath || shouldApplyBasicShape) {
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
gfxContextMatrixAutoSaveRestore matSR(&context);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
MOZ_ASSERT(!shouldApplyClipPath || !shouldApplyBasicShape);
if (shouldApplyClipPath) {
MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
!maskUsage.shouldApplyBasicShape);
if (maskUsage.shouldApplyClipPath) {
clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
} else {
nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
@ -854,7 +971,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
aParams.builder);
basic->SetTarget(oldCtx);
if (shouldApplyClipPath || shouldApplyBasicShape) {
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
context.PopClip();
}
@ -878,7 +995,7 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
return DrawResult::SUCCESS;
}
float opacity = ComputeOpacity(aParams);
float opacity = ComputeOpacity(frame, aParams.handleOpacity);
if (opacity == 0.0f) {
return DrawResult::SUCCESS;
}

Просмотреть файл

@ -160,6 +160,35 @@ public:
static DrawResult
PaintMaskAndClipPath(const PaintFramesParams& aParams);
/**
* Paint mask of non-SVG frame onto a given context, aParams.ctx.
* aParams.ctx must contain an A8 surface.
*/
static DrawResult
PaintMask(const PaintFramesParams& aParams);
struct MaskUsage {
bool shouldGenerateMaskLayer;
bool shouldGenerateClipMaskLayer;
bool shouldApplyClipPath;
bool shouldApplyBasicShape;
float opacity;
MaskUsage()
: shouldGenerateMaskLayer(false), shouldGenerateClipMaskLayer(false),
shouldApplyClipPath(false), shouldApplyBasicShape(false), opacity(0.0)
{ }
};
static void
DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity, MaskUsage& aUsage);
/**
* Return true if all the mask resource of aFrame are ready.
*/
static bool
IsMaskResourceReady(nsIFrame* aFrame);
/**
* Paint non-SVG frame with filter and opacity effect.
*/

Просмотреть файл

@ -117,3 +117,5 @@ res/table-remove-column.gif
res/table-remove-row-active.gif
res/table-remove-row-hover.gif
res/table-remove-row.gif
modules/commonjs/index.js
chrome/toolkit/content/global/XPCNativeWrapper.js

Просмотреть файл

@ -542,6 +542,7 @@ public:
nullptr, nullptr,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER);
loadInfo->SetOriginAttributes(mCI->GetOriginAttributes());
RefPtr<nsHttpChannel> chan = new nsHttpChannel();
nsresult rv;

Просмотреть файл

@ -5446,6 +5446,7 @@ library IAccessible2Lib
importlib ("oleacc.dll");
interface IAccessible2;
interface IAccessible2_2;
interface IAccessible2_3;
interface IAccessibleAction;
interface IAccessibleApplication;
interface IAccessibleComponent;

Просмотреть файл

@ -330,6 +330,8 @@ To see more help for a specific command, run:
sys.stdout = stdout
sys.stderr = stderr
orig_env = dict(os.environ)
try:
if stdin.encoding is None:
sys.stdin = codecs.getreader('utf-8')(stdin)
@ -340,6 +342,13 @@ To see more help for a specific command, run:
if stderr.encoding is None:
sys.stderr = codecs.getwriter('utf-8')(stderr)
# Allow invoked processes (which may not have a handle on the
# original stdout file descriptor) to know if the original stdout
# is a TTY. This provides a mechanism to allow said processes to
# enable emitting code codes, for example.
if os.isatty(orig_stdout.fileno()):
os.environ[b'MACH_STDOUT_ISATTY'] = b'1'
return self._run(argv)
except KeyboardInterrupt:
print('mach interrupted by signal or user action. Stopping.')
@ -362,6 +371,9 @@ To see more help for a specific command, run:
return 1
finally:
os.environ.clear()
os.environ.update(orig_env)
sys.stdin = orig_stdin
sys.stdout = orig_stdout
sys.stderr = orig_stderr

Просмотреть файл

@ -6,6 +6,7 @@ from __future__ import unicode_literals
import os
import signal
import sys
import traceback
from collections import defaultdict
from Queue import Empty
@ -62,6 +63,8 @@ def _run_worker(*args, **lintargs):
# it here so it isn't lost.
traceback.print_exc()
raise
finally:
sys.stdout.flush()
class LintRoller(object):

Просмотреть файл

@ -558,7 +558,7 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
// Owned by arena
SECItem* responseSECItem = nullptr;
Result tempRV =
DoOCSPRequest(arena, url, &ocspRequestItem,
DoOCSPRequest(arena, url, mFirstPartyDomain, &ocspRequestItem,
OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
mOCSPGetConfig == CertVerifier::ocspGetEnabled,
responseSECItem);

Просмотреть файл

@ -74,8 +74,8 @@ AppendEscapedBase64Item(const SECItem* encodedRequest, nsACString& path)
Result
DoOCSPRequest(const UniquePLArenaPool& arena, const char* url,
const SECItem* encodedRequest, PRIntervalTime timeout,
bool useGET,
const char* firstPartyDomain, const SECItem* encodedRequest,
PRIntervalTime timeout, bool useGET,
/*out*/ SECItem*& encodedResponse)
{
MOZ_ASSERT(arena.get());
@ -173,7 +173,8 @@ DoOCSPRequest(const UniquePLArenaPool& arena, const char* url,
nsNSSHttpRequestSession* requestSessionPtr;
rv = nsNSSHttpInterface::createFcn(serverSession.get(), "http", path.get(),
method.get(), timeout, &requestSessionPtr);
method.get(), firstPartyDomain, timeout,
&requestSessionPtr);
if (rv != Success) {
return rv;
}

Просмотреть файл

@ -14,6 +14,7 @@ namespace mozilla { namespace psm {
// The memory returned via |encodedResponse| is owned by the given arena.
Result DoOCSPRequest(const UniquePLArenaPool& arena, const char* url,
const char* firstPartyDomain,
const SECItem* encodedRequest, PRIntervalTime timeout,
bool useGET,
/*out*/ SECItem*& encodedResponse);

Просмотреть файл

@ -172,7 +172,6 @@ CertDumpHold=Certificate Hold
CertDumpOCSPResponder=OCSP
CertDumpCAIssuers=CA Issuers
CertDumpCPSPointer=Certification Practice Statement pointer
CertDumpPolicyOidEV=Extended Validation (EV) SSL Server Certificate
CertDumpUserNotice=User Notice
CertDumpLogotype=Logotype
CertDumpECPublicKey=Elliptic Curve Public Key

Просмотреть файл

@ -270,38 +270,36 @@ nsNSSDialogs::ChooseCertificate(nsIInterfaceRequestor* ctx,
return NS_OK;
}
NS_IMETHODIMP
nsNSSDialogs::SetPKCS12FilePassword(nsIInterfaceRequestor *ctx,
nsAString &_password,
bool *_retval)
NS_IMETHODIMP
nsNSSDialogs::SetPKCS12FilePassword(nsIInterfaceRequestor* ctx,
/*out*/ nsAString& password,
/*out*/ bool* confirmedPassword)
{
nsresult rv;
*_retval = true;
// |ctx| is allowed to be null.
NS_ENSURE_ARG(confirmedPassword);
// Get the parent window for the dialog
nsCOMPtr<mozIDOMWindowProxy> parent = do_GetInterface(ctx);
nsCOMPtr<nsIDialogParamBlock> block =
do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
if (!block) return NS_ERROR_FAILURE;
// open up the window
rv = nsNSSDialogHelper::openDialog(parent,
nsCOMPtr<nsIWritablePropertyBag2> retVals = new nsHashPropertyBag();
nsresult rv =
nsNSSDialogHelper::openDialog(parent,
"chrome://pippki/content/setp12password.xul",
block);
if (NS_FAILED(rv)) return rv;
// see if user canceled
int32_t status;
rv = block->GetInt(1, &status);
if (NS_FAILED(rv)) return rv;
*_retval = (status == 0) ? false : true;
if (*_retval) {
// retrieve the password
char16_t *pw;
rv = block->GetString(2, &pw);
if (NS_SUCCEEDED(rv)) {
_password = pw;
free(pw);
}
retVals);
if (NS_FAILED(rv)) {
return rv;
}
return rv;
rv = retVals->GetPropertyAsBool(NS_LITERAL_STRING("confirmedPassword"),
confirmedPassword);
if (NS_FAILED(rv)) {
return rv;
}
if (!*confirmedPassword) {
return NS_OK;
}
return retVals->GetPropertyAsAString(NS_LITERAL_STRING("password"), password);
}
NS_IMETHODIMP

Просмотреть файл

@ -124,15 +124,6 @@ function process()
checkPasswords();
}
function onP12Load(disableOkButton)
{
document.documentElement.getButton("accept").disabled = disableOkButton;
pw1 = document.getElementById("pw1");
params = window.arguments[0].QueryInterface(nsIDialogParamBlock);
// Select first password field
document.getElementById('pw1').focus();
}
function setPassword()
{
var pk11db = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
@ -206,16 +197,6 @@ function setPassword()
return success;
}
function setP12Password()
{
// grab what was entered
params.SetString(2, pw1.value);
// Return value
params.SetInt(1, 1);
// Terminate dialog
return true;
}
function setPasswordStrength()
{
// We weigh the quality of the password by checking the number of:

Просмотреть файл

@ -15,7 +15,8 @@
<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
<script type="application/javascript" src="chrome://pippki/content/password.js"/>
<script type="application/javascript"
src="chrome://pippki/content/changepassword.js"/>
<hbox align="center">
<label value="&setPassword.tokenName.label;: "/>

Просмотреть файл

@ -0,0 +1,128 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* @file Implements the functionality of setp12password.xul: a dialog that lets
* the user confirm the password to set on a PKCS #12 file.
* @argument {nsISupports} window.arguments[0]
* Object to set the return values of calling the dialog on, queryable
* to the underlying type of SetP12PasswordReturnValues.
*/
/**
* @typedef SetP12PasswordReturnValues
* @type nsIWritablePropertyBag2
* @property {Boolean} confirmedPassword
* Set to true if the user entered two matching passwords and
* confirmed the dialog.
* @property {String} password
* The password the user entered. Undefined value if
* |confirmedPassword| is not true.
*/
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
/**
* onload() handler.
*/
function onLoad() {
// Ensure the first password textbox has focus.
document.getElementById("pw1").focus();
}
/**
* ondialogaccept() handler.
*
* @returns {Boolean} true to make the dialog close, false otherwise.
*/
function onDialogAccept() {
let password = document.getElementById("pw1").value;
let retVals = window.arguments[0].QueryInterface(Ci.nsIWritablePropertyBag2);
retVals.setPropertyAsBool("confirmedPassword", true);
retVals.setPropertyAsAString("password", password);
return true;
}
/**
* ondialogcancel() handler.
*
* @returns {Boolean} true to make the dialog close, false otherwise.
*/
function onDialogCancel() {
let retVals = window.arguments[0].QueryInterface(Ci.nsIWritablePropertyBag2);
retVals.setPropertyAsBool("confirmedPassword", false);
return true;
}
/**
* Calculates the strength of the given password, suitable for use in updating
* a progress bar that represents said strength.
*
* The strength of the password is calculated by checking the number of:
* - Characters
* - Numbers
* - Non-alphanumeric chars
* - Upper case characters
*
* @param {String} password
* The password to calculate the strength of.
* @returns {Number}
* The strength of the password in the range [0, 100].
*/
function getPasswordStrength(password) {
let lengthStrength = password.length;
if (lengthStrength > 5) {
lengthStrength = 5;
}
let nonNumericChars = password.replace(/[0-9]/g, "");
let numericStrength = password.length - nonNumericChars.length;
if (numericStrength > 3) {
numericStrength = 3;
}
let nonSymbolChars = password.replace(/\W/g, "");
let symbolStrength = password.length - nonSymbolChars.length;
if (symbolStrength > 3) {
symbolStrength = 3;
}
let nonUpperAlphaChars = password.replace(/[A-Z]/g, "");
let upperAlphaStrength = password.length - nonUpperAlphaChars.length;
if (upperAlphaStrength > 3) {
upperAlphaStrength = 3;
}
let strength = (lengthStrength * 10) - 20 + (numericStrength * 10) +
(symbolStrength * 15) + (upperAlphaStrength * 10);
if (strength < 0) {
strength = 0;
}
if (strength > 100) {
strength = 100;
}
return strength;
}
/**
* oninput() handler for both password textboxes.
*
* @param {Boolean} recalculatePasswordStrength
* Whether to recalculate the strength of the first password.
*/
function onPasswordInput(recalculatePasswordStrength) {
let pw1 = document.getElementById("pw1").value;
if (recalculatePasswordStrength) {
document.getElementById("pwmeter").value = getPasswordStrength(pw1);
}
// Disable the accept button if the two passwords don't match, and enable it
// if the passwords do match.
let pw2 = document.getElementById("pw2").value;
document.documentElement.getButton("accept").disabled = (pw1 != pw2);
}

Просмотреть файл

@ -7,14 +7,17 @@
<!DOCTYPE dialog SYSTEM "chrome://pippki/locale/pippki.dtd">
<dialog id="setp12password" title="&pkcs12.setpassword.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 48em;"
buttons="accept,cancel"
ondialogaccept="return setP12Password();"
onload="onP12Load(true);">
<dialog id="setp12password"
title="&pkcs12.setpassword.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 48em;"
buttons="accept,cancel"
ondialogaccept="return onDialogAccept();"
ondialogcancel="return onDialogCancel();"
onload="onLoad();">
<script type="application/javascript" src="chrome://pippki/content/password.js"/>
<script type="application/javascript"
src="chrome://pippki/content/setp12password.js"/>
<description>&pkcs12.setpassword.message;</description>
<separator />
@ -22,14 +25,12 @@
<columns> <column/> <column/> </columns>
<rows>
<row>
<label value="&pkcs12.setpassword.label1;"/>
<textbox id="pw1" type="password"
oninput="setPasswordStrength(); checkPasswords();"/>
<label value="&pkcs12.setpassword.label1;"/>
<textbox id="pw1" type="password" oninput="onPasswordInput(true);"/>
</row>
<row>
<label value="&pkcs12.setpassword.label2;"/>
<textbox id="pw2" type="password"
oninput="checkPasswords();"/>
<label value="&pkcs12.setpassword.label2;"/>
<textbox id="pw2" type="password" oninput="onPasswordInput(false);"/>
</row>
</rows>
</grid>
@ -41,11 +42,10 @@
<rows> <row/> </rows>
<columns>
<column style="margin: 5px;">
<progressmeter flex="1" id="pwmeter" mode="determined" value="0%"
<progressmeter flex="1" id="pwmeter" mode="determined" value="0"
orient="horizontal"
style="width: 20em; foreground-color: red"/>
style="width: 20em; foreground-color: red"/>
</column>
</columns>
</grid>
</dialog>

Просмотреть файл

@ -4,39 +4,40 @@
pippki.jar:
% content pippki %content/pippki/
content/pippki/changepassword.xul (content/changepassword.xul)
content/pippki/password.js (content/password.js)
content/pippki/resetpassword.xul (content/resetpassword.xul)
content/pippki/resetpassword.js (content/resetpassword.js)
content/pippki/downloadcert.js (content/downloadcert.js)
content/pippki/downloadcert.xul (content/downloadcert.xul)
content/pippki/certManager.js (content/certManager.js)
content/pippki/certManager.xul (content/certManager.xul)
content/pippki/CAOverlay.xul (content/CAOverlay.xul)
content/pippki/WebSitesOverlay.xul (content/WebSitesOverlay.xul)
content/pippki/OthersOverlay.xul (content/OthersOverlay.xul)
content/pippki/MineOverlay.xul (content/MineOverlay.xul)
content/pippki/OrphanOverlay.xul (content/OrphanOverlay.xul)
content/pippki/viewCertDetails.xul (content/viewCertDetails.xul)
content/pippki/editcacert.xul (content/editcacert.xul)
content/pippki/editcacert.js (content/editcacert.js)
* content/pippki/exceptionDialog.xul (content/exceptionDialog.xul)
content/pippki/exceptionDialog.js (content/exceptionDialog.js)
content/pippki/deletecert.xul (content/deletecert.xul)
content/pippki/deletecert.js (content/deletecert.js)
content/pippki/setp12password.xul (content/setp12password.xul)
content/pippki/pippki.js (content/pippki.js)
content/pippki/clientauthask.xul (content/clientauthask.xul)
content/pippki/clientauthask.js (content/clientauthask.js)
content/pippki/certViewer.xul (content/certViewer.xul)
content/pippki/certViewer.js (content/certViewer.js)
content/pippki/OthersOverlay.xul (content/OthersOverlay.xul)
content/pippki/WebSitesOverlay.xul (content/WebSitesOverlay.xul)
content/pippki/certDump.xul (content/certDump.xul)
content/pippki/device_manager.xul (content/device_manager.xul)
content/pippki/device_manager.js (content/device_manager.js)
content/pippki/load_device.xul (content/load_device.xul)
content/pippki/choosetoken.xul (content/choosetoken.xul)
content/pippki/certManager.js (content/certManager.js)
content/pippki/certManager.xul (content/certManager.xul)
content/pippki/certViewer.js (content/certViewer.js)
content/pippki/certViewer.xul (content/certViewer.xul)
content/pippki/changepassword.js (content/changepassword.js)
content/pippki/changepassword.xul (content/changepassword.xul)
content/pippki/choosetoken.js (content/choosetoken.js)
content/pippki/createCertInfo.xul (content/createCertInfo.xul)
content/pippki/choosetoken.xul (content/choosetoken.xul)
content/pippki/clientauthask.js (content/clientauthask.js)
content/pippki/clientauthask.xul (content/clientauthask.xul)
content/pippki/createCertInfo.js (content/createCertInfo.js)
content/pippki/protectedAuth.xul (content/protectedAuth.xul)
content/pippki/createCertInfo.xul (content/createCertInfo.xul)
content/pippki/deletecert.js (content/deletecert.js)
content/pippki/deletecert.xul (content/deletecert.xul)
content/pippki/device_manager.js (content/device_manager.js)
content/pippki/device_manager.xul (content/device_manager.xul)
content/pippki/downloadcert.js (content/downloadcert.js)
content/pippki/downloadcert.xul (content/downloadcert.xul)
content/pippki/editcacert.js (content/editcacert.js)
content/pippki/editcacert.xul (content/editcacert.xul)
content/pippki/exceptionDialog.js (content/exceptionDialog.js)
* content/pippki/exceptionDialog.xul (content/exceptionDialog.xul)
content/pippki/load_device.xul (content/load_device.xul)
content/pippki/pippki.js (content/pippki.js)
content/pippki/protectedAuth.js (content/protectedAuth.js)
content/pippki/protectedAuth.xul (content/protectedAuth.xul)
content/pippki/resetpassword.js (content/resetpassword.js)
content/pippki/resetpassword.xul (content/resetpassword.xul)
content/pippki/setp12password.js (content/setp12password.js)
content/pippki/setp12password.xul (content/setp12password.xul)
content/pippki/viewCertDetails.xul (content/viewCertDetails.xul)

Просмотреть файл

@ -104,15 +104,18 @@
#include "PSMRunnable.h"
#include "RootCertificateTelemetryUtils.h"
#include "ScopedNSSTypes.h"
#include "SharedCertVerifier.h"
#include "SharedSSLState.h"
#include "TransportSecurityInfo.h" // For RememberCertErrorsTable
#include "cert.h"
#include "mozilla/Assertions.h"
#include "mozilla/Casting.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/net/DNS.h"
#include "mozilla/Unused.h"
#include "mozilla/net/DNS.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsIBadCertListener2.h"
@ -120,9 +123,11 @@
#include "nsISiteSecurityService.h"
#include "nsISocketProvider.h"
#include "nsIThreadPool.h"
#include "nsNSSCertificate.h"
#include "nsNSSComponent.h"
#include "nsNSSIOLayer.h"
#include "nsNSSShutDown.h"
#include "nsSSLStatus.h"
#include "nsServiceManagerUtils.h"
#include "nsURLHelper.h"
#include "nsXPCOMCIDInternal.h"
@ -1369,18 +1374,12 @@ AuthCertificate(CertVerifier& certVerifier,
pinningTelemetryInfo.certPinningResultBucket);
}
RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
RefPtr<nsNSSCertificate> nsc;
if (!status || !status->HasServerCert()) {
if (rv == Success) {
nsc = nsNSSCertificate::Create(cert.get(), &evOidPolicy);
} else {
nsc = nsNSSCertificate::Create(cert.get());
}
}
if (rv == Success) {
// Certificate verification succeeded. Delete any potential record of
// certificate error bits.
RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
nullptr,
SECSuccess);
GatherSuccessfulValidationTelemetry(certList);
GatherCertificateTransparencyTelemetry(certList,
certificateTransparencyInfo);
@ -1388,34 +1387,24 @@ AuthCertificate(CertVerifier& certVerifier,
// The connection may get terminated, for example, if the server requires
// a client cert. Let's provide a minimal SSLStatus
// to the caller that contains at least the cert and its status.
RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
if (!status) {
status = new nsSSLStatus();
infoObject->SetSSLStatus(status);
}
if (rv == Success) {
// Certificate verification succeeded delete any potential record
// of certificate error bits.
RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
nullptr,
SECSuccess);
} else {
// Certificate verification failed, update the status' bits.
RememberCertErrorsTable::GetInstance().LookupCertErrorBits(
infoObject, status);
}
if (status && !status->HasServerCert()) {
nsNSSCertificate::EVStatus evStatus;
if (evOidPolicy == SEC_OID_UNKNOWN || rv != Success) {
evStatus = nsNSSCertificate::ev_status_invalid;
if (!status->HasServerCert()) {
EVStatus evStatus;
if (evOidPolicy == SEC_OID_UNKNOWN) {
evStatus = EVStatus::NotEV;
} else {
evStatus = nsNSSCertificate::ev_status_valid;
evStatus = EVStatus::EV;
}
RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert.get());
status->SetServerCert(nsc, evStatus);
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("AuthCertificate setting NEW cert %p\n", nsc.get()));
("AuthCertificate setting NEW cert %p", nsc.get()));
}
status->SetCertificateTransparencyInfo(certificateTransparencyInfo);

Просмотреть файл

@ -1056,7 +1056,7 @@ TransportSecurityInfo::SetStatusErrorBits(nsNSSCertificate* cert,
mSSLStatus = new nsSSLStatus();
}
mSSLStatus->SetServerCert(cert, nsNSSCertificate::ev_status_invalid);
mSSLStatus->SetServerCert(cert, EVStatus::NotEV);
mSSLStatus->mHaveCertErrorBits = true;
mSSLStatus->mIsDomainMismatch =

Просмотреть файл

@ -6,9 +6,14 @@
#include "nsNSSCallbacks.h"
#include "PSMRunnable.h"
#include "ScopedNSSTypes.h"
#include "SharedCertVerifier.h"
#include "SharedSSLState.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Casting.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
@ -20,15 +25,13 @@
#include "nsITokenDialogs.h"
#include "nsIUploadChannel.h"
#include "nsIWebProgressListener.h"
#include "nsNetUtil.h"
#include "nsNSSCertificate.h"
#include "nsNSSComponent.h"
#include "nsNSSIOLayer.h"
#include "nsNetUtil.h"
#include "nsProtectedAuthThread.h"
#include "nsProxyRelease.h"
#include "pkix/pkixtypes.h"
#include "PSMRunnable.h"
#include "ScopedNSSTypes.h"
#include "SharedSSLState.h"
#include "ssl.h"
#include "sslproto.h"
@ -111,6 +114,18 @@ nsHTTPDownloadEvent::Run()
chan->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS |
nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
if (!mRequestSession->mFirstPartyDomain.IsEmpty()) {
NeckoOriginAttributes attrs;
attrs.mFirstPartyDomain =
NS_ConvertUTF8toUTF16(mRequestSession->mFirstPartyDomain);
nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
if (loadInfo) {
rv = loadInfo->SetOriginAttributes(attrs);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// Create a loadgroup for this new channel. This way if the channel
// is redirected, we'll have a way to cancel the resulting channel.
nsCOMPtr<nsILoadGroup> lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
@ -215,6 +230,7 @@ nsNSSHttpRequestSession::createFcn(const nsNSSHttpServerSession* session,
const char* http_protocol_variant,
const char* path_and_query_string,
const char* http_request_method,
const char* first_party_domain,
const PRIntervalTime timeout,
/*out*/ nsNSSHttpRequestSession** pRequest)
{
@ -244,6 +260,8 @@ nsNSSHttpRequestSession::createFcn(const nsNSSHttpServerSession* session,
rs->mURL.AppendInt(session->mPort);
rs->mURL.Append(path_and_query_string);
rs->mFirstPartyDomain.Assign(first_party_domain);
rs->mRequestMethod = http_request_method;
*pRequest = rs;
@ -1083,6 +1101,89 @@ AccumulateCipherSuite(Telemetry::ID probe, const SSLChannelInfo& channelInfo)
Telemetry::Accumulate(probe, value);
}
// In the case of session resumption, the AuthCertificate hook has been bypassed
// (because we've previously successfully connected to our peer). That being the
// case, we unfortunately don't know if the peer's server certificate verified
// as extended validation or not. To address this, we attempt to build a
// verified EV certificate chain here using as much of the original context as
// possible (e.g. stapled OCSP responses, SCTs, the hostname, the first party
// domain, etc.). Note that because we are on the socket thread, this must not
// cause any network requests, hence the use of FLAG_LOCAL_ONLY.
static void
DetermineEVStatusAndSetNewCert(RefPtr<nsSSLStatus> sslStatus, PRFileDesc* fd,
nsNSSSocketInfo* infoObject)
{
MOZ_ASSERT(sslStatus);
MOZ_ASSERT(fd);
MOZ_ASSERT(infoObject);
if (!sslStatus || !fd || !infoObject) {
return;
}
UniqueCERTCertificate cert(SSL_PeerCertificate(fd));
MOZ_ASSERT(cert, "SSL_PeerCertificate failed in TLS handshake callback?");
if (!cert) {
return;
}
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
MOZ_ASSERT(certVerifier,
"Certificate verifier uninitialized in TLS handshake callback?");
if (!certVerifier) {
return;
}
// We don't own these pointers.
const SECItemArray* stapledOCSPResponses = SSL_PeerStapledOCSPResponses(fd);
const SECItem* stapledOCSPResponse = nullptr;
// we currently only support single stapled responses
if (stapledOCSPResponses && stapledOCSPResponses->len == 1) {
stapledOCSPResponse = &stapledOCSPResponses->items[0];
}
const SECItem* sctsFromTLSExtension = SSL_PeerSignedCertTimestamps(fd);
if (sctsFromTLSExtension && sctsFromTLSExtension->len == 0) {
// SSL_PeerSignedCertTimestamps returns null on error and empty item
// when no extension was returned by the server. We always use null when
// no extension was received (for whatever reason), ignoring errors.
sctsFromTLSExtension = nullptr;
}
int flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
if (!infoObject->SharedState().IsOCSPStaplingEnabled() ||
!infoObject->SharedState().IsOCSPMustStapleEnabled()) {
flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
}
SECOidTag evOidPolicy;
UniqueCERTCertList unusedBuiltChain;
const bool saveIntermediates = false;
mozilla::pkix::Result rv = certVerifier->VerifySSLServerCert(
cert,
stapledOCSPResponse,
sctsFromTLSExtension,
mozilla::pkix::Now(),
infoObject,
infoObject->GetHostNameRaw(),
unusedBuiltChain,
saveIntermediates,
flags,
infoObject->GetFirstPartyDomainRaw(),
&evOidPolicy);
RefPtr<nsNSSCertificate> nssc(nsNSSCertificate::Create(cert.get()));
if (rv == Success && evOidPolicy != SEC_OID_UNKNOWN) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback using NEW cert %p (is EV)", nssc.get()));
sslStatus->SetServerCert(nssc, EVStatus::EV);
} else {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback using NEW cert %p (is not EV)", nssc.get()));
sslStatus->SetServerCert(nssc, EVStatus::NotEV);
}
}
void HandshakeCallback(PRFileDesc* fd, void* client_data) {
nsNSSShutDownPreventionLock locker;
SECStatus rv;
@ -1236,11 +1337,7 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback KEEPING existing cert\n"));
} else {
UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd));
RefPtr<nsNSSCertificate> nssc(nsNSSCertificate::Create(serverCert.get()));
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("HandshakeCallback using NEW cert %p\n", nssc.get()));
status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown);
DetermineEVStatusAndSetNewCert(status, fd, infoObject);
}
bool domainMismatch;

Просмотреть файл

@ -99,6 +99,7 @@ public:
const char* httpProtocolVariant,
const char* pathAndQueryString,
const char* httpRequestMethod,
const char* firstPartyDomain,
const PRIntervalTime timeout,
/*out*/ nsNSSHttpRequestSession** pRequest);
@ -123,6 +124,8 @@ public:
nsCString mPostData;
nsCString mPostContentType;
nsCString mFirstPartyDomain;
PRIntervalTime mTimeoutInterval;
RefPtr<nsHTTPListener> mListener;
@ -156,13 +159,14 @@ public:
const char* httpProtocolVariant,
const char* pathAndQueryString,
const char* httpRequestMethod,
const char* firstPartyDomain,
const PRIntervalTime timeout,
/*out*/ nsNSSHttpRequestSession** pRequest)
{
return nsNSSHttpRequestSession::createFcn(session, httpProtocolVariant,
pathAndQueryString,
httpRequestMethod, timeout,
pRequest);
httpRequestMethod, firstPartyDomain,
timeout, pRequest);
}
static Result setPostDataFcn(nsNSSHttpRequestSession* request,

Просмотреть файл

@ -1233,7 +1233,6 @@ ProcessUserNotice(SECItem* derNotice, nsAString& text,
static nsresult
ProcessCertificatePolicies(SECItem *extData,
nsAString &text,
SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
nsINSSComponent *nssComponent)
{
CERTPolicyInfo **policyInfos, *policyInfo;
@ -1260,26 +1259,10 @@ ProcessCertificatePolicies(SECItem *extData,
text.Append(local);
}
bool needColon = true;
if (ev_oid_tag != SEC_OID_UNKNOWN) {
// This is an EV cert. Let's see if this oid is the EV oid,
// because we want to display the EV information string
// next to the correct OID.
if (policyInfo->oid == ev_oid_tag) {
text.Append(':');
text.AppendLiteral(SEPARATOR);
needColon = false;
nssComponent->GetPIPNSSBundleString("CertDumpPolicyOidEV", local);
text.Append(local);
}
}
if (policyInfo->policyQualifiers) {
/* Add all qualifiers on separate lines, indented */
policyQualifiers = policyInfo->policyQualifiers;
if (needColon)
text.Append(':');
text.Append(':');
text.AppendLiteral(SEPARATOR);
while (*policyQualifiers) {
text.AppendLiteral(" ");
@ -1500,7 +1483,6 @@ ProcessMSCAVersion(SECItem *extData,
static nsresult
ProcessExtensionData(SECOidTag oidTag, SECItem *extData,
nsAString &text,
SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
nsINSSComponent *nssComponent)
{
nsresult rv;
@ -1525,7 +1507,7 @@ ProcessExtensionData(SECOidTag oidTag, SECItem *extData,
rv = ProcessAuthKeyId(extData, text, nssComponent);
break;
case SEC_OID_X509_CERTIFICATE_POLICIES:
rv = ProcessCertificatePolicies(extData, text, ev_oid_tag, nssComponent);
rv = ProcessCertificatePolicies(extData, text, nssComponent);
break;
case SEC_OID_X509_CRL_DIST_POINTS:
rv = ProcessCrlDistPoints(extData, text, nssComponent);
@ -1550,7 +1532,6 @@ ProcessExtensionData(SECOidTag oidTag, SECItem *extData,
static nsresult
ProcessSingleExtension(CERTCertExtension *extension,
SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
nsINSSComponent *nssComponent,
nsIASN1PrintableItem **retExtension)
{
@ -1572,7 +1553,7 @@ ProcessSingleExtension(CERTCertExtension *extension,
}
text.AppendLiteral(SEPARATOR);
nsresult rv = ProcessExtensionData(oidTag, &extension->value, extvalue,
ev_oid_tag, nssComponent);
nssComponent);
if (NS_FAILED(rv)) {
extvalue.Truncate();
rv = ProcessRawBytes(nssComponent, &extension->value, extvalue, false);
@ -1768,7 +1749,6 @@ ProcessSubjectPublicKeyInfo(CERTSubjectPublicKeyInfo *spki,
static nsresult
ProcessExtensions(CERTCertExtension **extensions,
nsIASN1Sequence *parentSequence,
SECOidTag ev_oid_tag, // SEC_OID_UNKNOWN means: not EV
nsINSSComponent *nssComponent)
{
nsCOMPtr<nsIASN1Sequence> extensionSequence = new nsNSSASN1Sequence;
@ -1783,7 +1763,6 @@ ProcessExtensions(CERTCertExtension **extensions,
extensionSequence->GetASN1Objects(getter_AddRefs(asn1Objects));
for (i=0; extensions[i] != nullptr; i++) {
rv = ProcessSingleExtension(extensions[i],
ev_oid_tag,
nssComponent,
getter_AddRefs(newExtension));
if (NS_FAILED(rv))
@ -1966,19 +1945,7 @@ nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence,
}
if (mCert->extensions) {
SECOidTag ev_oid_tag = SEC_OID_UNKNOWN;
bool validEV;
rv = hasValidEVOidTag(ev_oid_tag, validEV);
if (NS_FAILED(rv)) {
return rv;
}
if (!validEV) {
ev_oid_tag = SEC_OID_UNKNOWN;
}
rv = ProcessExtensions(mCert->extensions, sequence, ev_oid_tag, nssComponent);
rv = ProcessExtensions(mCert->extensions, sequence, nssComponent);
if (NS_FAILED(rv))
return rv;
}

Просмотреть файл

@ -70,14 +70,14 @@ NS_IMPL_ISUPPORTS(nsNSSCertificate,
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
/*static*/ nsNSSCertificate*
nsNSSCertificate::Create(CERTCertificate* cert, SECOidTag* evOidPolicy)
nsNSSCertificate::Create(CERTCertificate* cert)
{
if (GeckoProcessType_Default != XRE_GetProcessType()) {
NS_ERROR("Trying to initialize nsNSSCertificate in a non-chrome process!");
return nullptr;
}
if (cert)
return new nsNSSCertificate(cert, evOidPolicy);
return new nsNSSCertificate(cert);
else
return new nsNSSCertificate();
}
@ -122,12 +122,10 @@ nsNSSCertificate::InitFromDER(char* certDER, int derLen)
return true;
}
nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert,
SECOidTag* evOidPolicy)
nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert)
: mCert(nullptr)
, mPermDelete(false)
, mCertType(CERT_TYPE_NOT_YET_INITIALIZED)
, mCachedEVStatus(ev_status_unknown)
{
#if defined(DEBUG)
if (GeckoProcessType_Default != XRE_GetProcessType())
@ -140,23 +138,13 @@ nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert,
if (cert) {
mCert.reset(CERT_DupCertificate(cert));
if (evOidPolicy) {
if (*evOidPolicy == SEC_OID_UNKNOWN) {
mCachedEVStatus = ev_status_invalid;
}
else {
mCachedEVStatus = ev_status_valid;
}
mCachedEVOidTag = *evOidPolicy;
}
}
}
nsNSSCertificate::nsNSSCertificate() :
mCert(nullptr),
mPermDelete(false),
mCertType(CERT_TYPE_NOT_YET_INITIALIZED),
mCachedEVStatus(ev_status_unknown)
nsNSSCertificate::nsNSSCertificate()
: mCert(nullptr)
, mPermDelete(false)
, mCertType(CERT_TYPE_NOT_YET_INITIALIZED)
{
if (GeckoProcessType_Default != XRE_GetProcessType())
NS_ERROR("Trying to initialize nsNSSCertificate in a non-chrome process!");
@ -1134,85 +1122,6 @@ nsNSSCertificate::Equals(nsIX509Cert* other, bool* result)
return NS_OK;
}
nsresult
nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<mozilla::psm::SharedCertVerifier>
certVerifier(mozilla::psm::GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
validEV = false;
resultOidTag = SEC_OID_UNKNOWN;
uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
UniqueCERTCertList unusedBuiltChain;
mozilla::pkix::Result result = certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, mozilla::pkix::Now(),
nullptr /* XXX pinarg */,
nullptr /* hostname */,
unusedBuiltChain,
flags,
nullptr /* stapledOCSPResponse */,
nullptr /* sctsFromTLSExtension */,
nullptr /* firstPartyDomain */,
&resultOidTag);
if (result != mozilla::pkix::Success) {
resultOidTag = SEC_OID_UNKNOWN;
}
if (resultOidTag != SEC_OID_UNKNOWN) {
validEV = true;
}
return NS_OK;
}
nsresult
nsNSSCertificate::getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
{
if (mCachedEVStatus != ev_status_unknown) {
validEV = (mCachedEVStatus == ev_status_valid);
if (validEV) {
resultOidTag = mCachedEVOidTag;
}
return NS_OK;
}
nsresult rv = hasValidEVOidTag(resultOidTag, validEV);
if (NS_SUCCEEDED(rv)) {
if (validEV) {
mCachedEVOidTag = resultOidTag;
}
mCachedEVStatus = validEV ? ev_status_valid : ev_status_invalid;
}
return rv;
}
nsresult
nsNSSCertificate::GetIsExtendedValidation(bool* aIsEV)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ENSURE_ARG(aIsEV);
*aIsEV = false;
if (mCachedEVStatus != ev_status_unknown) {
*aIsEV = (mCachedEVStatus == ev_status_valid);
return NS_OK;
}
SECOidTag oid_tag;
return getValidEVOidTag(oid_tag, *aIsEV);
}
namespace mozilla {
// TODO(bug 1036065): It seems like we only construct CERTCertLists for the
@ -1628,7 +1537,8 @@ NS_IMETHODIMP
nsNSSCertificate::Write(nsIObjectOutputStream* aStream)
{
NS_ENSURE_STATE(mCert);
nsresult rv = aStream->Write32(static_cast<uint32_t>(mCachedEVStatus));
// This field used to be the cached EV status, but it is no longer necessary.
nsresult rv = aStream->Write32(0);
if (NS_FAILED(rv)) {
return rv;
}
@ -1644,20 +1554,12 @@ nsNSSCertificate::Read(nsIObjectInputStream* aStream)
{
NS_ENSURE_STATE(!mCert);
uint32_t cachedEVStatus;
nsresult rv = aStream->Read32(&cachedEVStatus);
// This field is no longer used.
uint32_t unusedCachedEVStatus;
nsresult rv = aStream->Read32(&unusedCachedEVStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (cachedEVStatus == static_cast<uint32_t>(ev_status_unknown)) {
mCachedEVStatus = ev_status_unknown;
} else if (cachedEVStatus == static_cast<uint32_t>(ev_status_valid)) {
mCachedEVStatus = ev_status_valid;
} else if (cachedEVStatus == static_cast<uint32_t>(ev_status_invalid)) {
mCachedEVStatus = ev_status_invalid;
} else {
return NS_ERROR_UNEXPECTED;
}
uint32_t len;
rv = aStream->Read32(&len);

Просмотреть файл

@ -37,18 +37,10 @@ public:
friend class nsNSSCertificateFakeTransport;
explicit nsNSSCertificate(CERTCertificate* cert, SECOidTag* evOidPolicy = nullptr);
explicit nsNSSCertificate(CERTCertificate* cert);
nsNSSCertificate();
static nsNSSCertificate* Create(CERTCertificate*cert = nullptr,
SECOidTag* evOidPolicy = nullptr);
static nsNSSCertificate* Create(CERTCertificate* cert = nullptr);
static nsNSSCertificate* ConstructFromDER(char* certDER, int derLen);
nsresult GetIsExtendedValidation(bool* aIsEV);
enum EVStatus {
ev_status_invalid = 0,
ev_status_valid = 1,
ev_status_unknown = 2
};
// This is a separate static method so nsNSSComponent can use it during NSS
// initialization. Other code should probably not use it.
@ -70,11 +62,6 @@ private:
bool InitFromDER(char* certDER, int derLen); // return false on failure
nsresult GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg);
EVStatus mCachedEVStatus;
SECOidTag mCachedEVOidTag;
nsresult hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV);
nsresult getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV);
};
namespace mozilla {

Просмотреть файл

@ -218,10 +218,9 @@ nsNSSCertificateFakeTransport::Write(nsIObjectOutputStream* aStream)
// nsNSSComponent. nsNSSCertificateFakeTransport object is used only to
// carry the certificate serialization.
// This serialization has to match that of nsNSSCertificate,
// so write a fake cached EV Status.
uint32_t status = static_cast<uint32_t>(nsNSSCertificate::ev_status_unknown);
nsresult rv = aStream->Write32(status);
// This serialization has to match that of nsNSSCertificate, so include this
// now-unused field.
nsresult rv = aStream->Write32(0);
if (NS_FAILED(rv)) {
return rv;
}
@ -238,10 +237,10 @@ nsNSSCertificateFakeTransport::Write(nsIObjectOutputStream* aStream)
NS_IMETHODIMP
nsNSSCertificateFakeTransport::Read(nsIObjectInputStream* aStream)
{
// This serialization has to match that of nsNSSCertificate,
// so read the cachedEVStatus but don't actually use it.
uint32_t cachedEVStatus;
nsresult rv = aStream->Read32(&cachedEVStatus);
// This serialization has to match that of nsNSSCertificate, so read the (now
// unused) cachedEVStatus.
uint32_t unusedCachedEVStatus;
nsresult rv = aStream->Read32(&unusedCachedEVStatus);
if (NS_FAILED(rv)) {
return rv;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше