зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound, a=merge
MozReview-Commit-ID: 9qR4QYHAi2x
This commit is contained in:
Коммит
253017b382
|
@ -240,6 +240,9 @@ window:not([chromehidden~="toolbar"]) #nav-bar[nonemptyoverflow] > .overflow-but
|
|||
|
||||
|
||||
%ifdef CAN_DRAW_IN_TITLEBAR
|
||||
%ifdef MENUBAR_CAN_AUTOHIDE
|
||||
#toolbar-menubar:not([autohide=true]) ~ #TabsToolbar > .titlebar-placeholder,
|
||||
%endif
|
||||
#main-window:not([chromemargin]) > #titlebar,
|
||||
#main-window[inFullscreen] > #titlebar,
|
||||
#main-window[inFullscreen] .titlebar-placeholder,
|
||||
|
@ -308,12 +311,6 @@ toolbarpaletteitem {
|
|||
#main-window[tabletmode] #titlebar-max {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#main-window[tabsintitlebar] #TabsToolbar,
|
||||
#main-window[tabsintitlebar] #toolbar-menubar,
|
||||
#main-window[tabsintitlebar] #navigator-toolbox > toolbar:-moz-lwtheme {
|
||||
-moz-window-dragging: drag;
|
||||
}
|
||||
%endif
|
||||
|
||||
%endif
|
||||
|
|
|
@ -5054,8 +5054,7 @@ var CombinedStopReload = {
|
|||
},
|
||||
|
||||
switchToReload(aRequest, aWebProgress) {
|
||||
if (!this.ensureInitialized() || !this._shouldSwitch(aRequest, aWebProgress) ||
|
||||
!this.reload.hasAttribute("displaystop")) {
|
||||
if (!this.ensureInitialized() || !this.reload.hasAttribute("displaystop")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6142,7 +6141,7 @@ function stripUnsafeProtocolOnPaste(pasteData) {
|
|||
// LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL for those.
|
||||
let changed = false;
|
||||
let pasteDataNoJS = pasteData.replace(/\r?\n/g, "")
|
||||
.replace(/^(?:\s*javascript:)+/i,
|
||||
.replace(/^(?:\W*javascript:)+/i,
|
||||
() => {
|
||||
changed = true;
|
||||
return "";
|
||||
|
|
|
@ -634,7 +634,7 @@
|
|||
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
<hbox class="titlebar-placeholder" type="pre-tabs"
|
||||
skipintoolbarset="true"/>
|
||||
skipintoolbarset="true"/>
|
||||
#endif
|
||||
|
||||
<tabs id="tabbrowser-tabs"
|
||||
|
@ -681,6 +681,12 @@
|
|||
</menupopup>
|
||||
</toolbarbutton>
|
||||
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
<hbox class="titlebar-placeholder" type="post-tabs"
|
||||
ordinal="1000"
|
||||
skipintoolbarset="true"/>
|
||||
#endif
|
||||
|
||||
<button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;"
|
||||
ordinal="1000"
|
||||
aria-live="polite" skipintoolbarset="true"/>
|
||||
|
|
|
@ -379,7 +379,7 @@ nsContextMenu.prototype = {
|
|||
|
||||
initLeaveDOMFullScreenItems: function CM_initLeaveFullScreenItem() {
|
||||
// only show the option if the user is in DOM fullscreen
|
||||
var shouldShow = (this.target.ownerDocument.fullscreenElement != null);
|
||||
var shouldShow = this.target.ownerDocument.fullscreen;
|
||||
this.showItem("context-leave-dom-fullscreen", shouldShow);
|
||||
|
||||
// Explicitly show if in DOM fullscreen, but do not hide it has already been shown
|
||||
|
@ -643,7 +643,7 @@ nsContextMenu.prototype = {
|
|||
this.showItem("context-media-loop", onMedia);
|
||||
this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
|
||||
this.showItem("context-media-hidecontrols", this.target.controls && (this.onVideo || (this.onAudio && !this.inSyntheticDoc)));
|
||||
this.showItem("context-video-fullscreen", this.onVideo && this.target.ownerDocument.fullscreenElement == null);
|
||||
this.showItem("context-video-fullscreen", this.onVideo && !this.target.ownerDocument.fullscreen);
|
||||
this.showItem("context-media-eme-learnmore", this.onDRMMedia);
|
||||
this.showItem("context-media-eme-separator", this.onDRMMedia);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ var pairs = [
|
|||
["javascript:", ""],
|
||||
["javascript:1+1", "1+1"],
|
||||
["javascript:document.domain", "document.domain"],
|
||||
[" \u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009javascript:document.domain", "document.domain"],
|
||||
["java\nscript:foo", "foo"],
|
||||
["http://\nexample.com", "http://example.com"],
|
||||
["http://\nexample.com\n", "http://example.com"],
|
||||
|
|
|
@ -31,6 +31,7 @@ for (const type of [
|
|||
"DELETE_HISTORY_URL_CONFIRM",
|
||||
"DIALOG_CANCEL",
|
||||
"DIALOG_OPEN",
|
||||
"DISABLE_ONBOARDING",
|
||||
"INIT",
|
||||
"LOCALE_UPDATED",
|
||||
"MIGRATION_CANCEL",
|
||||
|
@ -49,8 +50,8 @@ for (const type of [
|
|||
"PLACES_BOOKMARK_CHANGED",
|
||||
"PLACES_BOOKMARK_REMOVED",
|
||||
"PLACES_HISTORY_CLEARED",
|
||||
"PLACES_LINKS_DELETED",
|
||||
"PLACES_LINK_BLOCKED",
|
||||
"PLACES_LINK_DELETED",
|
||||
"PREFS_INITIAL_VALUES",
|
||||
"PREF_CHANGED",
|
||||
"SAVE_SESSION_PERF_DATA",
|
||||
|
|
|
@ -74,8 +74,7 @@ this.PrerenderData = new _PrerenderData({
|
|||
icon: "pocket",
|
||||
id: "topstories",
|
||||
order: 1,
|
||||
title: {id: "header_recommended_by", values: {provider: "Pocket"}},
|
||||
topics: [{}]
|
||||
title: {id: "header_recommended_by", values: {provider: "Pocket"}}
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
|
|
|
@ -288,7 +288,9 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
|||
return item;
|
||||
})
|
||||
}));
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINKS_DELETED:
|
||||
return prevState.map(section => Object.assign({}, section,
|
||||
{rows: section.rows.filter(site => !action.data.includes(site.url))}));
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
return prevState.map(section =>
|
||||
Object.assign({}, section, {rows: section.rows.filter(site => site.url !== action.data.url)}));
|
||||
|
|
|
@ -138,9 +138,6 @@
|
|||
"enabled": true,
|
||||
"icon": "pocket",
|
||||
"id": "topstories",
|
||||
"topics": [
|
||||
{}
|
||||
],
|
||||
"initialized": false
|
||||
},
|
||||
{
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -94,7 +94,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
|
|||
// UNINIT: "UNINIT"
|
||||
// }
|
||||
const actionTypes = {};
|
||||
for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
|
||||
for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
|
||||
|
@ -638,7 +638,8 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
|
|||
return item;
|
||||
})
|
||||
}));
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINKS_DELETED:
|
||||
return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => !action.data.includes(site.url)) }));
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
return prevState.map(section => Object.assign({}, section, { rows: section.rows.filter(site => site.url !== action.data.url) }));
|
||||
default:
|
||||
|
@ -1511,10 +1512,14 @@ class TopSitesEdit extends React.PureComponent {
|
|||
"section",
|
||||
{ className: "edit-topsites-inner-wrapper" },
|
||||
React.createElement(
|
||||
"h3",
|
||||
{ className: "section-title" },
|
||||
React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
|
||||
React.createElement(FormattedMessage, { id: "header_top_sites" })
|
||||
"div",
|
||||
{ className: "section-top-bar" },
|
||||
React.createElement(
|
||||
"h3",
|
||||
{ className: "section-title" },
|
||||
React.createElement("span", { className: `icon icon-small-spacer icon-topsites` }),
|
||||
React.createElement(FormattedMessage, { id: "header_top_sites" })
|
||||
)
|
||||
),
|
||||
React.createElement(
|
||||
"ul",
|
||||
|
@ -2569,7 +2574,10 @@ class Section extends React.PureComponent {
|
|||
contextMenuOptions, intl, initialized
|
||||
} = this.props;
|
||||
const maxCards = CARDS_PER_ROW * maxRows;
|
||||
const shouldShowTopics = id === "topstories" && this.props.topics && this.props.topics.length > 0;
|
||||
|
||||
// Show topics only for top stories and if it's not initialized yet (so
|
||||
// content doesn't shift when it is loaded) or has loaded with topics
|
||||
const shouldShowTopics = id === "topstories" && (!this.props.topics || this.props.topics.length > 0);
|
||||
|
||||
const infoOptionIconA11yAttrs = {
|
||||
"aria-haspopup": "true",
|
||||
|
@ -2694,6 +2702,9 @@ const { FormattedMessage } = __webpack_require__(2);
|
|||
const cardContextTypes = __webpack_require__(26);
|
||||
const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
|
||||
|
||||
// Keep track of pending image loads to only request once
|
||||
const gImageLoading = new Map();
|
||||
|
||||
/**
|
||||
* Card component.
|
||||
* Cards are found within a Section component and contain information about a link such
|
||||
|
@ -2706,11 +2717,47 @@ const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
|
|||
class Card extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { showContextMenu: false, activeCard: null };
|
||||
this.state = {
|
||||
activeCard: null,
|
||||
imageLoaded: false,
|
||||
showContextMenu: false
|
||||
};
|
||||
this.onMenuButtonClick = this.onMenuButtonClick.bind(this);
|
||||
this.onMenuUpdate = this.onMenuUpdate.bind(this);
|
||||
this.onLinkClick = this.onLinkClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to conditionally load an image and update state when it loads.
|
||||
*/
|
||||
async maybeLoadImage() {
|
||||
// No need to load if it's already loaded or no image
|
||||
const { image } = this.props.link;
|
||||
if (!this.state.imageLoaded && image) {
|
||||
// Initialize a promise to share a load across multiple card updates
|
||||
if (!gImageLoading.has(image)) {
|
||||
const loaderPromise = new Promise((resolve, reject) => {
|
||||
const loader = new Image();
|
||||
loader.addEventListener("load", resolve);
|
||||
loader.addEventListener("error", reject);
|
||||
loader.src = image;
|
||||
});
|
||||
|
||||
// Save and remove the promise only while it's pending
|
||||
gImageLoading.set(image, loaderPromise);
|
||||
loaderPromise.catch(ex => ex).then(() => gImageLoading.delete(image)).catch();
|
||||
}
|
||||
|
||||
// Wait for the image whether just started loading or reused promise
|
||||
await gImageLoading.get(image);
|
||||
|
||||
// Only update state if we're still waiting to load the original image
|
||||
if (this.props.link.image === image && !this.state.imageLoaded) {
|
||||
this.setState({ imageLoaded: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMenuButtonClick(event) {
|
||||
event.preventDefault();
|
||||
this.setState({
|
||||
|
@ -2742,6 +2789,18 @@ class Card extends React.PureComponent {
|
|||
onMenuUpdate(showContextMenu) {
|
||||
this.setState({ showContextMenu });
|
||||
}
|
||||
componentDidMount() {
|
||||
this.maybeLoadImage();
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.maybeLoadImage();
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// Clear the image state if changing images
|
||||
if (nextProps.link.image !== this.props.link.image) {
|
||||
this.setState({ imageLoaded: false });
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { index, link, dispatch, contextMenuOptions, eventSource, shouldSendImpressionStats } = this.props;
|
||||
const { props } = this;
|
||||
|
@ -2763,7 +2822,7 @@ class Card extends React.PureComponent {
|
|||
hasImage && React.createElement(
|
||||
"div",
|
||||
{ className: "card-preview-image-outer" },
|
||||
React.createElement("div", { className: `card-preview-image${link.image ? " loaded" : ""}`, style: imageStyle })
|
||||
React.createElement("div", { className: `card-preview-image${this.state.imageLoaded ? " loaded" : ""}`, style: imageStyle })
|
||||
),
|
||||
React.createElement(
|
||||
"div",
|
||||
|
@ -2894,7 +2953,7 @@ class Topics extends React.PureComponent {
|
|||
React.createElement(
|
||||
"ul",
|
||||
null,
|
||||
topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
|
||||
topics && topics.map(t => React.createElement(Topic, { key: t.name, url: t.url, name: t.name }))
|
||||
),
|
||||
read_more_endpoint && React.createElement(
|
||||
"a",
|
||||
|
@ -2985,8 +3044,7 @@ var PrerenderData = new _PrerenderData({
|
|||
icon: "pocket",
|
||||
id: "topstories",
|
||||
order: 1,
|
||||
title: { id: "header_recommended_by", values: { provider: "Pocket" } },
|
||||
topics: [{}]
|
||||
title: { id: "header_recommended_by", values: { provider: "Pocket" } }
|
||||
}, {
|
||||
enabled: true,
|
||||
id: "highlights",
|
||||
|
@ -3257,6 +3315,10 @@ class SnippetsMap extends Map {
|
|||
await this.set("blockList", blockList);
|
||||
}
|
||||
|
||||
disableOnboarding() {
|
||||
this._dispatch(ac.SendToMain({ type: at.DISABLE_ONBOARDING }));
|
||||
}
|
||||
|
||||
showFirefoxAccounts() {
|
||||
this._dispatch(ac.SendToMain({ type: at.SHOW_FIREFOX_ACCOUNTS }));
|
||||
}
|
||||
|
@ -3395,7 +3457,6 @@ class SnippetsProvider {
|
|||
if (needsUpdate && this.appData.snippetsURL) {
|
||||
this.snippetsMap.set("snippets-last-update", Date.now());
|
||||
try {
|
||||
// TODO: timeout?
|
||||
const response = await fetch(this.appData.snippetsURL);
|
||||
if (response.status === 200) {
|
||||
const payload = await response.text();
|
||||
|
@ -3409,10 +3470,18 @@ class SnippetsProvider {
|
|||
}
|
||||
}
|
||||
|
||||
_showDefaultSnippets() {
|
||||
_noSnippetFallback() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
_forceOnboardingVisibility(shouldBeVisible) {
|
||||
const onboardingEl = document.getElementById("onboarding-notification-bar");
|
||||
|
||||
if (onboardingEl) {
|
||||
onboardingEl.style.display = shouldBeVisible ? "" : "none";
|
||||
}
|
||||
}
|
||||
|
||||
_showRemoteSnippets() {
|
||||
const snippetsEl = document.getElementById(this.elementId);
|
||||
const payload = this.snippetsMap.get("snippets");
|
||||
|
@ -3480,15 +3549,18 @@ class SnippetsProvider {
|
|||
try {
|
||||
this._showRemoteSnippets();
|
||||
} catch (e) {
|
||||
this._showDefaultSnippets(e);
|
||||
this._noSnippetFallback(e);
|
||||
}
|
||||
|
||||
window.dispatchEvent(new Event(SNIPPETS_ENABLED_EVENT));
|
||||
|
||||
this._forceOnboardingVisibility(true);
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
uninit() {
|
||||
window.dispatchEvent(new Event(SNIPPETS_DISABLED_EVENT));
|
||||
this._forceOnboardingVisibility(false);
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
@ -3508,16 +3580,16 @@ function addSnippetsSubscriber(store) {
|
|||
|
||||
store.subscribe(async () => {
|
||||
const state = store.getState();
|
||||
// state.Snippets.initialized: Should snippets be initialised?
|
||||
// snippets.initialized: Is SnippetsProvider currently initialised?
|
||||
if (state.Snippets.initialized && !snippets.initialized && state.Snippets.onboardingFinished) {
|
||||
// Don't call init multiple times
|
||||
if (!initializing) {
|
||||
initializing = true;
|
||||
await snippets.init({ appData: state.Snippets });
|
||||
initializing = false;
|
||||
}
|
||||
} else if (state.Snippets.initialized === false && snippets.initialized) {
|
||||
// state.Prefs.values["feeds.snippets"]: Should snippets be shown?
|
||||
// state.Snippets.initialized Is the snippets data initialized?
|
||||
// snippets.initialized: Is SnippetsProvider currently initialised?
|
||||
if (state.Prefs.values["feeds.snippets"] && state.Snippets.initialized && !snippets.initialized &&
|
||||
// Don't call init multiple times
|
||||
!initializing) {
|
||||
initializing = true;
|
||||
await snippets.init({ appData: state.Snippets });
|
||||
initializing = false;
|
||||
} else if (state.Prefs.values["feeds.snippets"] === false && snippets.initialized) {
|
||||
snippets.uninit();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -92,7 +92,8 @@ body {
|
|||
background: #F9F9FA;
|
||||
color: #0C0C0D;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;
|
||||
font-size: 16px; }
|
||||
font-size: 16px;
|
||||
overflow-y: scroll; }
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
|
@ -457,26 +458,24 @@ main {
|
|||
padding-inline-start: 3px; }
|
||||
|
||||
.topsite-form .form-wrapper {
|
||||
margin: auto;
|
||||
max-width: 350px;
|
||||
padding: 15px 0; }
|
||||
.topsite-form .form-wrapper .field {
|
||||
margin-inline-start: 205px;
|
||||
position: relative; }
|
||||
.topsite-form .form-wrapper .url input:not(:placeholder-shown):dir(rtl) {
|
||||
direction: ltr;
|
||||
text-align: right; }
|
||||
.topsite-form .form-wrapper .section-title {
|
||||
margin-bottom: 5px;
|
||||
margin-inline-start: 205px; }
|
||||
margin-bottom: 5px; }
|
||||
.topsite-form .form-wrapper input[type='text'] {
|
||||
border: solid 1px rgba(12, 12, 13, 0.2);
|
||||
border-radius: 2px;
|
||||
margin: 5px 0;
|
||||
padding: 7px;
|
||||
width: 350px; }
|
||||
width: 100%; }
|
||||
.topsite-form .form-wrapper input[type='text']:focus {
|
||||
border: solid 1px rgba(12, 12, 13, 0.4); }
|
||||
.topsite-form .form-wrapper input[type='text']::placeholder {
|
||||
font-style: italic; }
|
||||
.topsite-form .form-wrapper .invalid input[type='text'] {
|
||||
border: solid 1px #D70022;
|
||||
box-shadow: 0 0 0 2px rgba(215, 0, 34, 0.35); }
|
||||
|
@ -676,28 +675,19 @@ main {
|
|||
width: 100%;
|
||||
height: 36px; }
|
||||
.search-wrapper input {
|
||||
border: 0;
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
padding-inline-end: 36px;
|
||||
padding-inline-start: 35px;
|
||||
width: 100%;
|
||||
font-size: 15px; }
|
||||
.search-wrapper input:focus {
|
||||
border-color: #0060DF;
|
||||
box-shadow: 0 0 0 2px #0060DF;
|
||||
z-index: 1; }
|
||||
.search-wrapper input:focus + .search-button {
|
||||
z-index: 1;
|
||||
background-color: #0060DF;
|
||||
background-image: url("chrome://browser/skin/forward.svg");
|
||||
fill: #FFF;
|
||||
-moz-context-properties: fill; }
|
||||
.search-wrapper:active input,
|
||||
.search-wrapper input:focus {
|
||||
border-color: #0A84FF;
|
||||
box-shadow: 0 0 0 2px #0A84FF; }
|
||||
.search-wrapper .search-label {
|
||||
background: url("chrome://browser/skin/search-glass.svg") no-repeat 12px center/16px;
|
||||
fill: rgba(12, 12, 13, 0.4);
|
||||
|
@ -705,8 +695,7 @@ main {
|
|||
position: absolute;
|
||||
offset-inline-start: 0;
|
||||
height: 100%;
|
||||
width: 35px;
|
||||
z-index: 2; }
|
||||
width: 35px; }
|
||||
.search-wrapper .search-button {
|
||||
background: url("chrome://browser/skin/forward.svg") no-repeat center center;
|
||||
border-radius: 0 3px 3px 0;
|
||||
|
@ -718,11 +707,11 @@ main {
|
|||
height: 100%;
|
||||
offset-inline-end: 0;
|
||||
position: absolute; }
|
||||
.search-wrapper .search-button:hover {
|
||||
z-index: 1;
|
||||
background-color: #0060DF;
|
||||
fill: #FFF;
|
||||
.search-wrapper .search-button:focus, .search-wrapper .search-button:hover {
|
||||
background-color: rgba(12, 12, 13, 0.1);
|
||||
cursor: pointer; }
|
||||
.search-wrapper .search-button:active {
|
||||
background-color: rgba(12, 12, 13, 0.15); }
|
||||
.search-wrapper .search-button:dir(rtl) {
|
||||
transform: scaleX(-1); }
|
||||
.search-wrapper .contentSearchSuggestionTable {
|
||||
|
@ -1009,13 +998,11 @@ main {
|
|||
.card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title {
|
||||
color: #0060DF; }
|
||||
.card-outer .card-preview-image-outer {
|
||||
background-color: #F9F9FA;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #B1B1B3, #D7D7DB);
|
||||
height: 122px;
|
||||
border-radius: 3px 3px 0 0;
|
||||
overflow: hidden; }
|
||||
.card-outer .card-preview-image-outer:dir(rtl) {
|
||||
background: linear-gradient(225deg, #B1B1B3, #D7D7DB); }
|
||||
.card-outer .card-preview-image-outer::after {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
bottom: 0;
|
||||
|
@ -1028,8 +1015,8 @@ main {
|
|||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
transition: opacity 1s;
|
||||
opacity: 0; }
|
||||
opacity: 0;
|
||||
transition: opacity 1s cubic-bezier(0.07, 0.95, 0, 1); }
|
||||
.card-outer .card-preview-image-outer .card-preview-image.loaded {
|
||||
opacity: 1; }
|
||||
.card-outer .card-details {
|
||||
|
|
|
@ -580,7 +580,51 @@
|
|||
"time_label_hour": "{number}e",
|
||||
"time_label_day": "{number}d",
|
||||
"settings_pane_button_label": "Personelait ho pajenn Ivinell Nevez",
|
||||
"settings_pane_header": "Gwellvezioù an ivinell nevez"
|
||||
"settings_pane_header": "Gwellvezioù an ivinell nevez",
|
||||
"settings_pane_body2": "Dibabit petra a welit war ar bajenn-mañ.",
|
||||
"settings_pane_search_header": "Klask",
|
||||
"settings_pane_search_body": "Klask er web adalek an ivinell nevez.",
|
||||
"settings_pane_topsites_header": "Lec'hiennoù gwellañ",
|
||||
"settings_pane_topsites_body": "Kit war al lec'hiennoù gweladennet ar muiañ ganeoc'h.",
|
||||
"settings_pane_topsites_options_showmore": "Diskouez daou vann",
|
||||
"settings_pane_bookmarks_header": "Sinedoù nevez",
|
||||
"settings_pane_bookmarks_body": "Ho sinedoù nevez strollet en ul lec'h aes da dizhout.",
|
||||
"settings_pane_visit_again_header": "Gweladenniñ en-dro",
|
||||
"settings_pane_visit_again_body": "Firefox a ziskouezo deoc'h ul lodenn eus ho roll istor a c'hallfec'h kaout c'hoant da zerc'hel soñj pe da zistreiñ eno.",
|
||||
"settings_pane_highlights_header": "Mareoù pouezus",
|
||||
"settings_pane_highlights_body2": "Adkavit an traoù dedennus gweladennet pe lakaet er sinedoù nevez ’zo.",
|
||||
"settings_pane_highlights_options_bookmarks": "Sinedoù",
|
||||
"settings_pane_highlights_options_visited": "Lec'hiennoù gweladennet",
|
||||
"settings_pane_snippets_header": "Notennigoù",
|
||||
"settings_pane_snippets_body": "Lennit an hizivadurioù berr ha dous graet gant Mozilla evit Firefox, sevenadur ar genrouedad, hag ur mem dre-zegouezh ur wech an amzer.",
|
||||
"settings_pane_done_button": "Graet",
|
||||
"edit_topsites_button_text": "Embann",
|
||||
"edit_topsites_button_label": "Personelaat ar gevrenn “lec'hiennoù gweladennet ar muiañ”",
|
||||
"edit_topsites_showmore_button": "Diskouez muioc'h",
|
||||
"edit_topsites_showless_button": "Diskouez nebeutoc'h",
|
||||
"edit_topsites_done_button": "Graet",
|
||||
"edit_topsites_pin_button": "Spilhennañ al lec'hienn-mañ",
|
||||
"edit_topsites_unpin_button": "Dispilhennañ al lec'hienn-mañ",
|
||||
"edit_topsites_edit_button": "Embann al lec'hienn-mañ",
|
||||
"edit_topsites_dismiss_button": "Dilemel al lec'hienn-mañ",
|
||||
"edit_topsites_add_button": "Ouzhpennañ",
|
||||
"topsites_form_add_header": "Lec'hiennoù gwellañ nevez",
|
||||
"topsites_form_edit_header": "Embann al Lec'hiennoù Gwellañ",
|
||||
"topsites_form_title_placeholder": "Enankañ un titl",
|
||||
"topsites_form_url_placeholder": "Skrivit pe pegit un URL",
|
||||
"topsites_form_add_button": "Ouzhpennañ",
|
||||
"topsites_form_save_button": "Enrollañ",
|
||||
"topsites_form_cancel_button": "Nullañ",
|
||||
"topsites_form_url_validation": "URL talvoudek azgoulennet",
|
||||
"pocket_read_more": "Danvezioù brudet:",
|
||||
"pocket_read_even_more": "Gwelet muioc'h a istorioù",
|
||||
"pocket_feedback_header": "Ar gwellañ eus ar web, dibabet gant ouzhpenn 25 milion a dud.",
|
||||
"pocket_description": "Dizoloit pennadoù eus an dibab ho pije gellout c'hwitout a-hent all warno, a-drugarez da bPocket, hag a zo bremañ ul lodenn deus Mozilla.",
|
||||
"highlights_empty_state": "Krogit da verdeiñ hag e tiskouezimp deoc’h pennadoù, videoioù ha pajennoù all gweladennet pe lakaet er sinedoù nevez ’zo.",
|
||||
"topstories_empty_state": "Aet oc'h betek penn. Distroit diwezhatoc'h evit muioc’h a istorioù digant {provider}. N’oc'h ket evit gortoz? Dibabit un danvez brudet evit klask muioc’h a bennadoù dedennus eus pep lec’h er web.",
|
||||
"manual_migration_explanation2": "Amprouit Firefox gant sinedoù, roll istor ha gerioù-tremen ur merdeer all.",
|
||||
"manual_migration_cancel_button": "N'am bo ket",
|
||||
"manual_migration_import_button": "Emporzhiañ bremañ"
|
||||
},
|
||||
"ca": {
|
||||
"newtab_page_title": "Pestanya nova",
|
||||
|
@ -955,6 +999,7 @@
|
|||
"default_label_loading": "Indlæser…",
|
||||
"header_top_sites": "Mest besøgte websider",
|
||||
"header_stories": "Tophistorier",
|
||||
"header_highlights": "Højdepunkter",
|
||||
"header_visit_again": "Besøg igen",
|
||||
"header_bookmarks": "Seneste bogmærker",
|
||||
"header_recommended_by": "Anbefalet af {provider}",
|
||||
|
@ -986,6 +1031,7 @@
|
|||
"search_web_placeholder": "Søg på internettet",
|
||||
"search_settings": "Skift søgeindstillinger",
|
||||
"section_info_option": "Info",
|
||||
"section_info_send_feedback": "Send feedback",
|
||||
"welcome_title": "Velkommen til nyt faneblad",
|
||||
"welcome_body": "Firefox vil bruge denne plads til at vise dine mest relevante bogmærker, artikler, videoer og sider, du har besøgt for nylig - så kan du nemmere finde dem.",
|
||||
"welcome_label": "Finder dine højdepunkter",
|
||||
|
@ -995,7 +1041,6 @@
|
|||
"time_label_day": "{number} d.",
|
||||
"settings_pane_button_label": "Tilpas siden Nyt faneblad",
|
||||
"settings_pane_header": "Indstillinger for Nyt faneblad",
|
||||
"settings_pane_body": "Vælg, hvad der vises, når du åbner et nyt faneblad.",
|
||||
"settings_pane_search_header": "Søgning",
|
||||
"settings_pane_search_body": "Søg på nettet fra Nyt faneblad.",
|
||||
"settings_pane_topsites_header": "Mest besøgte websider",
|
||||
|
@ -1005,8 +1050,6 @@
|
|||
"settings_pane_bookmarks_body": "Dine seneste bogmærker samlet ét sted.",
|
||||
"settings_pane_visit_again_header": "Besøg igen",
|
||||
"settings_pane_visit_again_body": "Firefox viser dig dele af din browserhistorik, som du måske vil huske på eller vende tilbage til.",
|
||||
"settings_pane_pocketstories_header": "Tophistorier",
|
||||
"settings_pane_pocketstories_body": "Pocket, en del af Mozilla-familien, hjælper dig med at opdage indhold af høj kvalitet, som du måske ellers ikke ville have fundet.",
|
||||
"settings_pane_done_button": "Færdig",
|
||||
"edit_topsites_button_text": "Rediger",
|
||||
"edit_topsites_button_label": "Tilpas afsnittet Mest besøgte websider",
|
||||
|
@ -1029,10 +1072,7 @@
|
|||
"pocket_read_more": "Populære emner:",
|
||||
"pocket_read_even_more": "Se flere historier",
|
||||
"pocket_feedback_header": "Det bedste fra nettet, udvalgt af mere end 25 millioner mennesker.",
|
||||
"pocket_feedback_body": "Pocket, en del af Mozilla-familien, hjælper dig med at opdage indhold af høj kvalitet, som du måske ellers ikke ville have fundet.",
|
||||
"pocket_send_feedback": "Send feedback",
|
||||
"topstories_empty_state": "Der er ikke flere nye historier. Kom tilbage senere for at se flere tophistorier fra {provider}. Kan du ikke vente? Vælg et populært emne og find flere spændende historier fra hele verden.",
|
||||
"manual_migration_explanation": "Prøv Firefox med dine favorit-websteder og bogmærker fra en anden browser.",
|
||||
"manual_migration_cancel_button": "Nej tak",
|
||||
"manual_migration_import_button": "Importer nu"
|
||||
},
|
||||
|
@ -2586,6 +2626,7 @@
|
|||
"default_label_loading": "’Ga luchdadh…",
|
||||
"header_top_sites": "Brod nan làrach",
|
||||
"header_stories": "Brod nan sgeul",
|
||||
"header_highlights": "Sàr-roghainn",
|
||||
"header_visit_again": "Tadhail a-rithist",
|
||||
"header_bookmarks": "Comharran-lìn o chionn goirid",
|
||||
"header_recommended_by": "’Ga mholadh le {provider}",
|
||||
|
@ -2617,6 +2658,8 @@
|
|||
"search_web_placeholder": "Lorg air an lìon",
|
||||
"search_settings": "Atharraich roghainnean an luirg",
|
||||
"section_info_option": "Fiosrachadh",
|
||||
"section_info_send_feedback": "Cuir thugainn do bheachdan",
|
||||
"section_info_privacy_notice": "Sanas prìobhaideachd",
|
||||
"welcome_title": "Fàilte gun taba ùr",
|
||||
"welcome_body": "Seallaidh Firefox na comharran-lìn, artaigealan, videothan is duilleagan as iomchaidhe dhut, an fheadhainn air an do thadhail thu o chionn goirid, ach an ruig thu iad gu luath.",
|
||||
"welcome_label": "Ag aithneachadh nan highlights agad",
|
||||
|
@ -2626,7 +2669,7 @@
|
|||
"time_label_day": "{number}l",
|
||||
"settings_pane_button_label": "Gnàthaich duilleag nan tabaichean ùra agad",
|
||||
"settings_pane_header": "Roghainnean nan tabaichean ùra",
|
||||
"settings_pane_body": "Tagh na chì thu nuair a dh’fhosglas tu taba ùr.",
|
||||
"settings_pane_body2": "Tagh na chì thu air an duilleag seo.",
|
||||
"settings_pane_search_header": "Lorg",
|
||||
"settings_pane_search_body": "Lorg air an lìon on taba ùr agad.",
|
||||
"settings_pane_topsites_header": "Brod nan làrach",
|
||||
|
@ -2636,8 +2679,11 @@
|
|||
"settings_pane_bookmarks_body": "Na comharran-lìn ùra agad san aon àite ghoireasach.",
|
||||
"settings_pane_visit_again_header": "Tadhail a-rithist",
|
||||
"settings_pane_visit_again_body": "Seallaidh Firefox cuid dhen eachdraidh bhrabhsaidh agad dhut a bu toil leat cuimhneachadh no tadhal air a-rithist ma dh’fhaoidte.",
|
||||
"settings_pane_pocketstories_header": "Brod nan sgeul",
|
||||
"settings_pane_pocketstories_body": "Pocket, ball de theaghlach Mozilla, a cheanglas tu ri susbaint fhìor-mhath nach biodh tu air fhaicinn air dòigh eile.",
|
||||
"settings_pane_highlights_header": "Sàr-roghainn",
|
||||
"settings_pane_highlights_body2": "Faigh greim gu furasta air rudan inntinneach air an do thadhail thu roimhe no a rinn thu comharran-lìn dhiubh.",
|
||||
"settings_pane_highlights_options_bookmarks": "Comharran-lìn",
|
||||
"settings_pane_highlights_options_visited": "Làraichean a thadhladh orra",
|
||||
"settings_pane_snippets_header": "Snippets",
|
||||
"settings_pane_done_button": "Deiseil",
|
||||
"edit_topsites_button_text": "Deasaich",
|
||||
"edit_topsites_button_label": "Gnàthaich earrann brod nan làrach agad",
|
||||
|
@ -2660,10 +2706,7 @@
|
|||
"pocket_read_more": "Cuspairean fèillmhor:",
|
||||
"pocket_read_even_more": "Seall barrachd sgeul",
|
||||
"pocket_feedback_header": "Brod an eadar-lìn, air a dheasachadh le barrachd air 25 millean duine.",
|
||||
"pocket_feedback_body": "Pocket, ball de theaghlach Mozilla, a cheanglas tu ri susbaint fhìor-mhath nach biodh tu air fhaicinn air dòigh eile.",
|
||||
"pocket_send_feedback": "Dè do bheachd air?",
|
||||
"topstories_empty_state": "Sin na naidheachdan uile o {provider} an-dràsta ach bidh barrachd ann a dh’aithghearr. No thoir sùil air cuspair air a bheil fèill mhòr is leugh na tha a’ dol mun cuairt air an lìon an-dràsta.",
|
||||
"manual_migration_explanation": "Feuch Firefox leis na làraichean is comharran-lìn as fhearr leat o bhrabhsair eile.",
|
||||
"manual_migration_cancel_button": "Chan eil, tapadh leibh",
|
||||
"manual_migration_import_button": "Ion-phortaich an-dràsta"
|
||||
},
|
||||
|
@ -3788,8 +3831,8 @@
|
|||
"kk": {
|
||||
"newtab_page_title": "Жаңа бет",
|
||||
"default_label_loading": "Жүктелуде…",
|
||||
"header_top_sites": "Топ сайттар",
|
||||
"header_stories": "Топ хикаялар",
|
||||
"header_top_sites": "Үздік сайттар",
|
||||
"header_stories": "Үздік хикаялар",
|
||||
"header_highlights": "Ерекше жаңалықтар",
|
||||
"header_visit_again": "Қайтадан шолу",
|
||||
"header_bookmarks": "Соңғы бетбелгілер",
|
||||
|
@ -3815,7 +3858,7 @@
|
|||
"menu_action_unpin": "Бекітуді алып тастау",
|
||||
"confirm_history_delete_p1": "Бұл парақтың барлық кездесулерін шолу тарихыңыздан өшіруді қалайсыз ба?",
|
||||
"confirm_history_delete_notice_p2": "Бұл әрекетті болдырмау мүмкін болмайды.",
|
||||
"menu_action_save_to_pocket": "Pocket-ке сақтау",
|
||||
"menu_action_save_to_pocket": "Pocket ішіне сақтау",
|
||||
"search_for_something_with": "{search_term} ұғымын көмегімен іздеу:",
|
||||
"search_button": "Іздеу",
|
||||
"search_header": "{search_engine_name} іздеуі",
|
||||
|
@ -3833,10 +3876,10 @@
|
|||
"time_label_day": "{number} күн",
|
||||
"settings_pane_button_label": "Жаңа бетті баптаңыз",
|
||||
"settings_pane_header": "Жаңа бет баптаулары",
|
||||
"settings_pane_body2": "Бұл парақта не көргіңіз келетінді таңдаңыз.",
|
||||
"settings_pane_body2": "Бұл бетте не көргіңіз келетінді таңдаңыз.",
|
||||
"settings_pane_search_header": "Іздеу",
|
||||
"settings_pane_search_body": "Жаңа беттен интернеттен іздеңіз.",
|
||||
"settings_pane_topsites_header": "Топ сайттар",
|
||||
"settings_pane_topsites_header": "Үздік сайттар",
|
||||
"settings_pane_topsites_body": "Көбірек қаралатын сайттарға қатынау.",
|
||||
"settings_pane_topsites_options_showmore": "Екі жолды көрсету",
|
||||
"settings_pane_bookmarks_header": "Соңғы бетбелгілер",
|
||||
|
@ -3848,7 +3891,7 @@
|
|||
"settings_pane_highlights_options_bookmarks": "Бетбелгілер",
|
||||
"settings_pane_highlights_options_visited": "Ашылған сайттар",
|
||||
"settings_pane_snippets_header": "Үзінділер",
|
||||
"settings_pane_snippets_body": "Mozilla-дан Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
|
||||
"settings_pane_snippets_body": "Mozilla ұсынған Firefox және интернет мәдениеті туралы қысқа жаңалықтарды, және кездейсоқ мемдерді оқыңыз.",
|
||||
"settings_pane_done_button": "Дайын",
|
||||
"edit_topsites_button_text": "Түзету",
|
||||
"edit_topsites_button_label": "Топ сайттар санатын баптау",
|
||||
|
@ -3874,7 +3917,7 @@
|
|||
"pocket_description": "Ол болмаса, сіз жіберіп алатын мүмкіндігі бар жоғары сапалы құраманы Pocket көмегімен табыңыз, ол енді Mozilla-ның бөлігі болып табылады.",
|
||||
"highlights_empty_state": "Шолуды бастаңыз, сіз жақында шолған немесе бетбелгілерге қосқан тамаша мақалалар, видеолар немесе басқа парақтардың кейбіреулері осында көрсетіледі.",
|
||||
"topstories_empty_state": "Дайын. {provider} ұсынған көбірек мақалаларды алу үшін кейінірек тексеріңіз. Күте алмайсыз ба? Интернеттен көбірек тамаша мақалаларды алу үшін әйгілі теманы таңдаңыз.",
|
||||
"manual_migration_explanation2": "Firefox-ты басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",
|
||||
"manual_migration_explanation2": "Firefox қолданбасын басқа браузер бетбелгілері, тарихы және парольдерімен қолданып көріңіз.",
|
||||
"manual_migration_cancel_button": "Жоқ, рахмет",
|
||||
"manual_migration_import_button": "Қазір импорттау"
|
||||
},
|
||||
|
@ -4203,13 +4246,15 @@
|
|||
"manual_migration_import_button": "Importuoti dabar"
|
||||
},
|
||||
"lv": {
|
||||
"newtab_page_title": "Jauna cilne"
|
||||
"newtab_page_title": "Jauna cilne",
|
||||
"default_label_loading": "Notiek ielāde…"
|
||||
},
|
||||
"mk": {
|
||||
"newtab_page_title": "Ново јазиче",
|
||||
"default_label_loading": "Се вчитува…",
|
||||
"header_top_sites": "Врвни мрежни места",
|
||||
"header_stories": "Врвни написи",
|
||||
"header_top_sites": "Популарни мрежни места",
|
||||
"header_stories": "Популарни написи",
|
||||
"header_highlights": "Интереси",
|
||||
"header_visit_again": "Посети повторно",
|
||||
"header_bookmarks": "Скорешни обележувачи",
|
||||
"header_recommended_by": "Препорачано од {provider}",
|
||||
|
@ -4232,7 +4277,7 @@
|
|||
"menu_action_delete": "Избриши од историја",
|
||||
"menu_action_pin": "Прикачи",
|
||||
"menu_action_unpin": "Откачи",
|
||||
"confirm_history_delete_p1": "Дали сте сигурни дека сакате да ја избришете оваа страница отсекаде во Вашата историја на прелистување?",
|
||||
"confirm_history_delete_p1": "Дали сте сигурни дека сакате да ја избришете оваа страница отсекаде во вашата историја на прелистување?",
|
||||
"confirm_history_delete_notice_p2": "Ова дејство не може да се одврати.",
|
||||
"menu_action_save_to_pocket": "Зачувај во Pocket",
|
||||
"search_for_something_with": "Пребарај за {search_term} со:",
|
||||
|
@ -4241,30 +4286,36 @@
|
|||
"search_web_placeholder": "Пребарајте на Интернет",
|
||||
"search_settings": "Промени поставувања за пребарување",
|
||||
"section_info_option": "Инфо",
|
||||
"section_info_send_feedback": "Испрати мислење",
|
||||
"section_info_privacy_notice": "Белешка за приватност",
|
||||
"welcome_title": "Добредојдовте во новото јазиче",
|
||||
"welcome_body": "Firefox ќе го искористи овој простор за да Ви ги прикаже најрелевантните обележувачи, написи, видеа и страници што сте ги посетиле, за да можете лесно да им се навратите.",
|
||||
"welcome_label": "Ги откривам Вашите интереси",
|
||||
"welcome_label": "Ги откривам вашите Интереси",
|
||||
"time_label_less_than_minute": "< 1 м",
|
||||
"time_label_minute": "{number} м",
|
||||
"time_label_hour": "{number} ч",
|
||||
"time_label_day": "{number} д",
|
||||
"settings_pane_button_label": "Прилагодете ја страницата на Вашето Ново јазиче",
|
||||
"settings_pane_button_label": "Прилагодете ја страницата на вашето Ново јазиче",
|
||||
"settings_pane_header": "Преференци за Ново јазиче",
|
||||
"settings_pane_body": "Изберете што ќе гледате кога ќе отворите ново јазиче.",
|
||||
"settings_pane_body2": "Изберете што ќе гледате на оваа страница.",
|
||||
"settings_pane_search_header": "Пребарување",
|
||||
"settings_pane_search_body": "Пребарајте низ Интернет од Вашето ново јазиче.",
|
||||
"settings_pane_search_body": "Пребарајте низ Интернет од вашето ново јазиче.",
|
||||
"settings_pane_topsites_header": "Врвни мрежни места",
|
||||
"settings_pane_topsites_body": "Пристапете до мрежните места што ги посетувате најмногу.",
|
||||
"settings_pane_topsites_options_showmore": "Прикажи два реда",
|
||||
"settings_pane_bookmarks_header": "Скорешни обележувачи",
|
||||
"settings_pane_bookmarks_body": "Вашите нови обележувачи во едно згодно место.",
|
||||
"settings_pane_visit_again_header": "Посети повторно",
|
||||
"settings_pane_visit_again_body": "Firefox ќе прикаже делови од Вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
|
||||
"settings_pane_pocketstories_header": "Врвни написи",
|
||||
"settings_pane_pocketstories_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
|
||||
"settings_pane_visit_again_body": "Firefox ќе прикаже делови од вашата историја на прелистување кои можеби би сакале да ги запомните или пак да им се навратите.",
|
||||
"settings_pane_highlights_header": "Интереси",
|
||||
"settings_pane_highlights_body2": "Навратете се на интересни места што неодамна сте ги посетиле или обележале.",
|
||||
"settings_pane_highlights_options_bookmarks": "Обележувачи",
|
||||
"settings_pane_highlights_options_visited": "Посетени мрежни места",
|
||||
"settings_pane_snippets_header": "Исечоци",
|
||||
"settings_pane_snippets_body": "Прочитајте кратки и слатки новости од Mozilla во врска со Firefox, Интернет-културата и повремените случајни меми.",
|
||||
"settings_pane_done_button": "Готово",
|
||||
"edit_topsites_button_text": "Уреди",
|
||||
"edit_topsites_button_label": "Прилагодете ги Вашите Врвни мрежни места",
|
||||
"edit_topsites_button_label": "Прилагодете ги вашите Популарни мрежни места",
|
||||
"edit_topsites_showmore_button": "Прикажи повеќе",
|
||||
"edit_topsites_showless_button": "Прикажи помалку",
|
||||
"edit_topsites_done_button": "Готово",
|
||||
|
@ -4284,10 +4335,10 @@
|
|||
"pocket_read_more": "Популарни теми:",
|
||||
"pocket_read_even_more": "Види повеќе написи",
|
||||
"pocket_feedback_header": "Најдоброто од Интернет, одбрано од повеќе од 25 милиони луѓе.",
|
||||
"pocket_feedback_body": "Pocket, дел од семејството на Mozilla, ќе Ви помогне да стигнете до високо-квалитетни содржини кои можеби не би ги откриле на друг начин.",
|
||||
"pocket_send_feedback": "Остави коментар",
|
||||
"pocket_description": "Откријте високо-квалитетни содржини, коишто инаку би можеле да ги пропуштите, со помош на Pocket, кој сега е дел од Mozilla.",
|
||||
"highlights_empty_state": "Започнете со прелистување и ние овде ќе ви прикажеме некои од одличните написи, видеа и други страници што неодамна сте ги поселите или обележале.",
|
||||
"topstories_empty_state": "Имате видено сѐ! Навратете се подоцна за нови содржини од {provider}. Не можете да чекате? Изберете популарна тема и откријте уште одлични содржини ширум Интернет.",
|
||||
"manual_migration_explanation": "Пробајте го Firefox со Вашите омилени мрежни места и обележувачи од друг прелистувач.",
|
||||
"manual_migration_explanation2": "Пробајте го Firefox со обележувачите, историјата и лозинките на друг прелистувач.",
|
||||
"manual_migration_cancel_button": "Не, благодарам",
|
||||
"manual_migration_import_button": "Увези сега"
|
||||
},
|
||||
|
@ -6092,7 +6143,7 @@
|
|||
"header_stories": "เรื่องราวเด่น",
|
||||
"header_highlights": "รายการเด่น",
|
||||
"header_visit_again": "เยี่ยมชมอีกครั้ง",
|
||||
"header_bookmarks": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
|
||||
"header_bookmarks": "ที่คั่นหน้าล่าสุด",
|
||||
"header_recommended_by": "แนะนำโดย {provider}",
|
||||
"header_bookmarks_placeholder": "คุณยังไม่มีที่คั่นหน้าใด ๆ",
|
||||
"header_stories_from": "จาก",
|
||||
|
@ -6139,7 +6190,7 @@
|
|||
"settings_pane_topsites_header": "ไซต์เด่น",
|
||||
"settings_pane_topsites_body": "เข้าถึงเว็บไซต์ที่คุณเยี่ยมชมมากที่สุด",
|
||||
"settings_pane_topsites_options_showmore": "แสดงสองแถว",
|
||||
"settings_pane_bookmarks_header": "ที่คั่นหน้าเมื่อเร็ว ๆ นี้",
|
||||
"settings_pane_bookmarks_header": "ที่คั่นหน้าล่าสุด",
|
||||
"settings_pane_bookmarks_body": "ที่คั่นหน้าที่สร้างใหม่ของคุณในตำแหน่งที่ตั้งเดียวที่สะดวก",
|
||||
"settings_pane_visit_again_header": "เยี่ยมชมอีกครั้ง",
|
||||
"settings_pane_highlights_header": "รายการเด่น",
|
||||
|
@ -6617,8 +6668,8 @@
|
|||
"settings_pane_highlights_body2": "根据您最近访问的页面和添加的书签推荐您感兴趣的东西。",
|
||||
"settings_pane_highlights_options_bookmarks": "书签",
|
||||
"settings_pane_highlights_options_visited": "访问过的网站",
|
||||
"settings_pane_snippets_header": "板报",
|
||||
"settings_pane_snippets_body": "阅读和了解 Mozilla 就 Firefox、互联网文化等提供的一些简短而有趣的更新。",
|
||||
"settings_pane_snippets_header": "只言片语",
|
||||
"settings_pane_snippets_body": "阅读 Mozilla 就 Firefox、互联网文化、偶尔还有模因提供的一些简短而有趣的小文章。",
|
||||
"settings_pane_done_button": "完成",
|
||||
"edit_topsites_button_text": "编辑",
|
||||
"edit_topsites_button_label": "定制您的“常用网站”区域",
|
||||
|
@ -6641,7 +6692,7 @@
|
|||
"pocket_read_more": "热门主题:",
|
||||
"pocket_read_even_more": "查看更多文章",
|
||||
"pocket_feedback_header": "由超过 2500 万人挑选出来的网上精华内容。",
|
||||
"pocket_description": "借助 Pocket(目前所属 Mozilla)发现有趣的高品质内容。",
|
||||
"pocket_description": "借助 Pocket(目前属 Mozilla 旗下)发现您不容错过的高品质内容。",
|
||||
"highlights_empty_state": "开始浏览旅程吧,之后这里会显示您最近看过或加了书签的精彩文章、视频以及其他页面。",
|
||||
"topstories_empty_state": "所有文章都读完啦!晚点再来,{provider} 将推荐更多热门文章。等不及了?选择一个热门话题,找到更多网上的好文章。",
|
||||
"manual_migration_explanation2": "把在其他浏览器中保存的书签、历史记录和密码带到 Firefox 吧。",
|
||||
|
@ -6733,8 +6784,8 @@
|
|||
"topsites_form_url_validation": "請輸入有效的網址",
|
||||
"pocket_read_more": "熱門主題:",
|
||||
"pocket_read_even_more": "檢視更多文章",
|
||||
"pocket_feedback_header": "由超過兩千五百萬人找出來的 Web 最佳內容。",
|
||||
"pocket_description": "透過現在也是 Mozilla 旗下一員的 Pocket 的幫助,發現您先前可能錯過的高品質內容。",
|
||||
"pocket_feedback_header": "超過兩千五百萬人共同探索出的 Web 最佳內容。",
|
||||
"pocket_description": "透過 Mozilla 旗下的 Pocket 服務,發現您可能錯過的優質內容。",
|
||||
"highlights_empty_state": "開始上網,我們就會把您在網路上發現的好文章、影片、剛加入書籤的頁面顯示於此。",
|
||||
"topstories_empty_state": "所有文章都讀完啦!晚點再來,{provider} 將提供更多推薦故事。等不及了?選擇熱門主題,看看 Web 上各式精采資訊。",
|
||||
"manual_migration_explanation2": "試試將其他瀏覽器的書籤、瀏覽記錄與密碼匯入 Firefox。",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>false</em:unpack>
|
||||
<em:version>2017.09.22.1389-2ee94db4</em:version>
|
||||
<em:version>2017.09.28.0954-66ebf91c</em:version>
|
||||
<em:name>Activity Stream</em:name>
|
||||
<em:description>A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.</em:description>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -160,7 +160,7 @@ this.HighlightsFeed = class HighlightsFeed {
|
|||
break;
|
||||
case at.MIGRATION_COMPLETED:
|
||||
case at.PLACES_HISTORY_CLEARED:
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINKS_DELETED:
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
this.fetchHighlights(true);
|
||||
break;
|
||||
|
|
|
@ -42,11 +42,24 @@ class HistoryObserver extends Observer {
|
|||
* @param {obj} uri A URI object representing the link's url
|
||||
* {str} uri.spec The URI as a string
|
||||
*/
|
||||
onDeleteURI(uri) {
|
||||
this.dispatch({
|
||||
type: at.PLACES_LINK_DELETED,
|
||||
data: {url: uri.spec}
|
||||
});
|
||||
async onDeleteURI(uri) {
|
||||
// Add to an existing array of links if we haven't dispatched yet
|
||||
const {spec} = uri;
|
||||
if (this._deletedLinks) {
|
||||
this._deletedLinks.push(spec);
|
||||
} else {
|
||||
// Store an array of synchronously deleted links
|
||||
this._deletedLinks = [spec];
|
||||
|
||||
// Only dispatch a single action when we've gotten all deleted urls
|
||||
await Promise.resolve().then(() => {
|
||||
this.dispatch({
|
||||
type: at.PLACES_LINKS_DELETED,
|
||||
data: this._deletedLinks
|
||||
});
|
||||
delete this._deletedLinks;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,9 @@ const {utils: Cu} = Components;
|
|||
const {actionCreators: ac, actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {Prefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
|
||||
const {PrerenderData} = Cu.import("resource://activity-stream/common/PrerenderData.jsm", {});
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
|
||||
|
||||
this.PrefsFeed = class PrefsFeed {
|
||||
constructor(prefMap) {
|
||||
|
@ -27,11 +30,30 @@ this.PrefsFeed = class PrefsFeed {
|
|||
}
|
||||
}
|
||||
|
||||
_initOnboardingPref() {
|
||||
const snippetsEnabled = this._prefs.get("feeds.snippets");
|
||||
if (!snippetsEnabled) {
|
||||
this.setOnboardingDisabledDefault(true);
|
||||
}
|
||||
}
|
||||
|
||||
setOnboardingDisabledDefault(value) {
|
||||
const branch = Services.prefs.getDefaultBranch("");
|
||||
branch.setBoolPref(ONBOARDING_FINISHED_PREF, value);
|
||||
}
|
||||
|
||||
onPrefChanged(name, value) {
|
||||
if (this._prefMap.has(name)) {
|
||||
this.store.dispatch(ac.BroadcastToContent({type: at.PREF_CHANGED, data: {name, value}}));
|
||||
}
|
||||
|
||||
this._checkPrerender(name);
|
||||
|
||||
if (name === "feeds.snippets") {
|
||||
// If snippets are disabled, onboarding notifications should also be
|
||||
// disabled because they look like snippets.
|
||||
this.setOnboardingDisabledDefault(!value);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
|
@ -47,6 +69,7 @@ this.PrefsFeed = class PrefsFeed {
|
|||
this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
|
||||
|
||||
this._setPrerenderPref();
|
||||
this._initOnboardingPref();
|
||||
}
|
||||
removeListeners() {
|
||||
this._prefs.ignoreBranch(this);
|
||||
|
@ -58,10 +81,14 @@ this.PrefsFeed = class PrefsFeed {
|
|||
break;
|
||||
case at.UNINIT:
|
||||
this.removeListeners();
|
||||
this.setOnboardingDisabledDefault(false);
|
||||
break;
|
||||
case at.SET_PREF:
|
||||
this._prefs.set(action.data.name, action.data.value);
|
||||
break;
|
||||
case at.DISABLE_ONBOARDING:
|
||||
this.setOnboardingDisabledDefault(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,8 +17,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
|
|||
// Url to fetch snippets, in the urlFormatter service format.
|
||||
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
|
||||
const TELEMETRY_PREF = "datareporting.healthreport.uploadEnabled";
|
||||
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
|
||||
const FXA_USERNAME_PREF = "services.sync.username";
|
||||
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
|
||||
// Prefix for any target matching a search engine.
|
||||
const TARGET_SEARCHENGINE_PREFIX = "searchEngine-";
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
|
|||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
const ACTIVITY_STREAM_ID = "activity-stream";
|
||||
const ACTIVITY_STREAM_ENDPOINT_PREF = "browser.newtabpage.activity-stream.telemetry.ping.endpoint";
|
||||
|
||||
// This is a mapping table between the user preferences and its encoding code
|
||||
|
@ -160,12 +161,10 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
* Lazily initialize PingCentre to send pings
|
||||
*/
|
||||
get pingCentre() {
|
||||
const ACTIVITY_STREAM_ID = "activity-stream";
|
||||
Object.defineProperty(this, "pingCentre",
|
||||
{
|
||||
value: new PingCentre({
|
||||
topic: ACTIVITY_STREAM_ID,
|
||||
filter: ACTIVITY_STREAM_ID,
|
||||
overrideEndpointPref: ACTIVITY_STREAM_ENDPOINT_PREF
|
||||
})
|
||||
});
|
||||
|
@ -363,7 +362,8 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
|
||||
async sendEvent(event_object) {
|
||||
if (this.telemetryEnabled) {
|
||||
this.pingCentre.sendPing(event_object);
|
||||
this.pingCentre.sendPing(event_object,
|
||||
{filter: ACTIVITY_STREAM_ID});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -264,8 +264,8 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
// All these actions mean we need new top sites
|
||||
case at.MIGRATION_COMPLETED:
|
||||
case at.PLACES_HISTORY_CLEARED:
|
||||
case at.PLACES_LINK_DELETED:
|
||||
case at.PLACES_LINK_BLOCKED:
|
||||
case at.PLACES_LINKS_DELETED:
|
||||
this.frecentCache.expire();
|
||||
this.refresh();
|
||||
break;
|
||||
|
|
|
@ -358,13 +358,22 @@ describe("Reducers", () => {
|
|||
});
|
||||
it("should remove blocked and deleted urls from all rows in all sections", () => {
|
||||
const blockAction = {type: at.PLACES_LINK_BLOCKED, data: {url: "www.foo.bar"}};
|
||||
const deleteAction = {type: at.PLACES_LINK_DELETED, data: {url: "www.foo.bar"}};
|
||||
const deleteAction = {type: at.PLACES_LINKS_DELETED, data: ["www.foo.bar"]};
|
||||
const newBlockState = Sections(oldState, blockAction);
|
||||
const newDeleteState = Sections(oldState, deleteAction);
|
||||
newBlockState.concat(newDeleteState).forEach(section => {
|
||||
assert.deepEqual(section.rows, [{url: "www.other.url"}]);
|
||||
});
|
||||
});
|
||||
it("should remove all deleted urls", () => {
|
||||
const deleteAction = {type: at.PLACES_LINKS_DELETED, data: ["www.foo.bar", "www.other.url"]};
|
||||
|
||||
const newState = Sections(oldState, deleteAction);
|
||||
|
||||
newState.forEach(section => {
|
||||
assert.lengthOf(section.rows, 0);
|
||||
});
|
||||
});
|
||||
it("should not update state for empty action.data on PLACES_BOOKMARK_ADDED", () => {
|
||||
const nextState = Sections(undefined, {type: at.PLACES_BOOKMARK_ADDED});
|
||||
assert.equal(nextState, INITIAL_STATE.Sections);
|
||||
|
|
|
@ -296,10 +296,10 @@ describe("Highlights Feed", () => {
|
|||
assert.calledOnce(feed.fetchHighlights);
|
||||
assert.calledWith(feed.fetchHighlights, true);
|
||||
});
|
||||
it("should fetch highlights on PLACES_LINK_DELETED", async () => {
|
||||
it("should fetch highlights on PLACES_LINKS_DELETED", async () => {
|
||||
await feed.fetchHighlights();
|
||||
feed.fetchHighlights = sinon.spy();
|
||||
feed.onAction({type: at.PLACES_LINK_DELETED});
|
||||
feed.onAction({type: at.PLACES_LINKS_DELETED});
|
||||
assert.calledOnce(feed.fetchHighlights);
|
||||
assert.calledWith(feed.fetchHighlights, true);
|
||||
});
|
||||
|
|
|
@ -179,9 +179,20 @@ describe("PlacesFeed", () => {
|
|||
assert.property(observer, "QueryInterface");
|
||||
});
|
||||
describe("#onDeleteURI", () => {
|
||||
it("should dispatch a PLACES_LINK_DELETED action with the right url", () => {
|
||||
it("should dispatch a PLACES_LINKS_DELETED action with the right url", async() => {
|
||||
await observer.onDeleteURI({spec: "foo.com"});
|
||||
|
||||
assert.calledWith(dispatch, {type: at.PLACES_LINKS_DELETED, data: ["foo.com"]});
|
||||
});
|
||||
it("should dispatch a PLACES_LINKS_DELETED action with multiple urls", async() => {
|
||||
const promise = observer.onDeleteURI({spec: "bar.com"});
|
||||
observer.onDeleteURI({spec: "foo.com"});
|
||||
assert.calledWith(dispatch, {type: at.PLACES_LINK_DELETED, data: {url: "foo.com"}});
|
||||
await promise;
|
||||
|
||||
const result = dispatch.firstCall.args[0].data;
|
||||
assert.lengthOf(result, 2);
|
||||
assert.equal(result[0], "bar.com");
|
||||
assert.equal(result[1], "foo.com");
|
||||
});
|
||||
});
|
||||
describe("#onClearHistory", () => {
|
||||
|
|
|
@ -4,6 +4,7 @@ const {PrerenderData} = require("common/PrerenderData.jsm");
|
|||
const {initialPrefs} = PrerenderData;
|
||||
|
||||
const PRERENDER_PREF_NAME = "prerender";
|
||||
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
|
||||
|
||||
describe("PrefsFeed", () => {
|
||||
let feed;
|
||||
|
@ -62,6 +63,44 @@ describe("PrefsFeed", () => {
|
|||
assert.calledWith(feed._prefs.set, PRERENDER_PREF_NAME, false);
|
||||
});
|
||||
});
|
||||
describe("Onboarding", () => {
|
||||
let sandbox;
|
||||
let defaultBranch;
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
defaultBranch = {setBoolPref: sandbox.stub()};
|
||||
sandbox.stub(global.Services.prefs, "getDefaultBranch").returns(defaultBranch);
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
it("should set ONBOARDING_FINISHED_PREF to true if prefs.feeds.snippets if false", () => {
|
||||
FAKE_PREFS.set("feeds.snippets", false);
|
||||
feed.onAction({type: at.INIT});
|
||||
assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
|
||||
});
|
||||
it("should not set ONBOARDING_FINISHED_PREF if prefs.feeds.snippets is true", () => {
|
||||
FAKE_PREFS.set("feeds.snippets", true);
|
||||
feed.onAction({type: at.INIT});
|
||||
assert.notCalled(defaultBranch.setBoolPref);
|
||||
});
|
||||
it("should set ONBOARDING_FINISHED_PREF to true if the feeds.snippets pref changes to false", () => {
|
||||
feed.onPrefChanged("feeds.snippets", false);
|
||||
assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
|
||||
});
|
||||
it("should set ONBOARDING_FINISHED_PREF to false if the feeds.snippets pref changes to true", () => {
|
||||
feed.onPrefChanged("feeds.snippets", true);
|
||||
assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, false);
|
||||
});
|
||||
it("should not set ONBOARDING_FINISHED_PREF if an unrelated pref changes", () => {
|
||||
feed.onPrefChanged("foo", true);
|
||||
assert.notCalled(defaultBranch.setBoolPref);
|
||||
});
|
||||
it("should set ONBOARDING_FINISHED_PREF to true if a DISABLE_ONBOARDING action was received", () => {
|
||||
feed.onAction({type: at.DISABLE_ONBOARDING});
|
||||
assert.calledWith(defaultBranch.setBoolPref, ONBOARDING_FINISHED_PREF, true);
|
||||
});
|
||||
});
|
||||
describe("onPrefChanged prerendering", () => {
|
||||
it("should not change the prerender pref if the pref is not included in invalidatingPrefs", () => {
|
||||
feed.onPrefChanged("foo123", true);
|
||||
|
|
|
@ -507,9 +507,9 @@ describe("Top Sites Feed", () => {
|
|||
assert.calledOnce(feed.refresh);
|
||||
assert.equal(feed.refresh.firstCall.args[0], null);
|
||||
});
|
||||
it("should call refresh without a target on PLACES_LINK_DELETED action", async () => {
|
||||
it("should call refresh without a target on PLACES_LINKS_DELETED action", async () => {
|
||||
sinon.stub(feed, "refresh");
|
||||
await feed.onAction({type: at.PLACES_LINK_DELETED});
|
||||
await feed.onAction({type: at.PLACES_LINKS_DELETED});
|
||||
assert.calledOnce(feed.refresh);
|
||||
assert.equal(feed.refresh.firstCall.args[0], null);
|
||||
});
|
||||
|
|
|
@ -27,6 +27,8 @@ overrider.set({
|
|||
ContentSearchUIController: function() {}, // NB: This is a function/constructor
|
||||
dump() {},
|
||||
fetch() {},
|
||||
// eslint-disable-next-line object-shorthand
|
||||
Image: function() {}, // NB: This is a function/constructor
|
||||
Preferences: FakePrefs,
|
||||
Services: {
|
||||
locale: {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>false</em:unpack>
|
||||
<em:version>73</em:version>
|
||||
<em:version>76</em:version>
|
||||
<em:name>Shield Recipe Client</em:name>
|
||||
<em:description>Client to download and run recipes for SHIELD, Heartbeat, etc.</em:description>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -12,6 +12,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "ShellService", "resource:///modules/ShellService.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive", "resource://gre/modules/TelemetryArchive.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NormandyApi", "resource://shield-recipe-client/lib/NormandyApi.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this,
|
||||
|
@ -107,7 +108,7 @@ this.ClientEnvironment = {
|
|||
const mostRecentPings = {};
|
||||
for (const ping of pings) {
|
||||
if (ping.type in mostRecentPings) {
|
||||
if (mostRecentPings[ping.type].timeStampCreated < ping.timeStampCreated) {
|
||||
if (mostRecentPings[ping.type].timestampCreated < ping.timestampCreated) {
|
||||
mostRecentPings[ping.type] = ping;
|
||||
}
|
||||
} else {
|
||||
|
@ -128,7 +129,7 @@ this.ClientEnvironment = {
|
|||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(environment, "channel", () => {
|
||||
return Services.appinfo.defaultUpdateChannel;
|
||||
return UpdateUtils.getUpdateChannel(false);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(environment, "isDefaultBrowser", () => {
|
||||
|
|
|
@ -21,6 +21,7 @@ Cu.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm");
|
|||
Cu.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm");
|
||||
Cu.import("resource://shield-recipe-client/lib/Sampling.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "AddonStudies", "resource://shield-recipe-client/lib/AddonStudies.jsm");
|
||||
|
||||
|
@ -82,7 +83,7 @@ this.NormandyDriver = function(sandboxManager) {
|
|||
client() {
|
||||
const appinfo = {
|
||||
version: Services.appinfo.version,
|
||||
channel: Services.appinfo.defaultUpdateChannel,
|
||||
channel: UpdateUtils.getUpdateChannel(false),
|
||||
isDefaultBrowser: ShellService.isDefaultBrowser() || null,
|
||||
searchEngine: null,
|
||||
syncSetup: Preferences.isSet("services.sync.username"),
|
||||
|
|
|
@ -12,6 +12,7 @@ add_task(async function testTelemetry() {
|
|||
// setup
|
||||
await TelemetryController.submitExternalPing("testfoo", {foo: 1});
|
||||
await TelemetryController.submitExternalPing("testbar", {bar: 2});
|
||||
await TelemetryController.submitExternalPing("testfoo", {foo: 3});
|
||||
const environment = ClientEnvironment.getEnvironment();
|
||||
|
||||
// Test it can access telemetry
|
||||
|
@ -19,8 +20,8 @@ add_task(async function testTelemetry() {
|
|||
is(typeof telemetry, "object", "Telemetry is accesible");
|
||||
|
||||
// Test it reads different types of telemetry
|
||||
is(telemetry.testfoo.payload.foo, 1, "value 'foo' is in mock telemetry");
|
||||
is(telemetry.testbar.payload.bar, 2, "value 'bar' is in mock telemetry");
|
||||
is(telemetry.testfoo.payload.foo, 3, "telemetry filters pull the latest ping from a type");
|
||||
is(telemetry.testbar.payload.bar, 2, "telemetry filters pull from submitted telemetry pings");
|
||||
});
|
||||
|
||||
add_task(async function testUserId() {
|
||||
|
|
|
@ -1,35 +1,24 @@
|
|||
fbjs@0.8.12 BSD-3-Clause
|
||||
BSD License
|
||||
|
||||
For fbjs software
|
||||
fbjs@0.8.16 MIT
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
react-dom@15.6.1 BSD-3-Clause
|
||||
|
@ -66,7 +55,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
object-assign@4.1.0 MIT
|
||||
object-assign@4.1.1 MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
@ -157,38 +146,28 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
create-react-class@15.6.0 BSD-3-Clause
|
||||
BSD License
|
||||
|
||||
For React software
|
||||
create-react-class@15.6.2 MIT
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
mozjexl@1.1.5 MIT
|
||||
|
|
|
@ -1 +1 @@
|
|||
/* eslint-disable */this.PropTypes=function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,b),e.l=!0,e.exports}var c={};return b.m=a,b.c=c,b.d=function(a,c,d){b.o(a,c)||Object.defineProperty(a,c,{configurable:!1,enumerable:!0,get:d})},b.n=function(a){var c=a&&a.__esModule?function(){return a['default']}:function(){return a};return b.d(c,'a',c),c},b.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},b.p='',b(b.s=101)}({0:function(a){'use strict';var g=function(){};!1,a.exports=function(h,i,j,a,b,c,d,e){if(g(i),!h){var f;if(void 0===i)f=new Error('Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.');else{var k=[j,a,b,c,d,e],l=0;f=new Error(i.replace(/%s/g,function(){return k[l++]})),f.name='Invariant Violation'}throw f.framesToPop=1,f}}},101:function(a,b,c){a.exports=c(102)()},102:function(a,b,c){'use strict';var d=c(5),e=c(0),f=c(19);a.exports=function(){function a(a,b,c,d,g,h){h===f||e(!1,'Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types')}function b(){return a}a.isRequired=a;var c={array:a,bool:a,func:a,number:a,object:a,string:a,symbol:a,any:a,arrayOf:b,element:a,instanceOf:b,node:a,objectOf:b,oneOf:b,oneOfType:b,shape:b};return c.checkPropTypes=d,c.PropTypes=c,c}},19:function(a){'use strict';a.exports='SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'},5:function(a){'use strict';function b(a){return function(){return a}}var c=function(){};c.thatReturns=b,c.thatReturnsFalse=b(!1),c.thatReturnsTrue=b(!0),c.thatReturnsNull=b(null),c.thatReturnsThis=function(){return this},c.thatReturnsArgument=function(a){return a},a.exports=c}});this.EXPORTED_SYMBOLS = ["PropTypes"];
|
||||
/* eslint-disable */this.PropTypes=function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,b),e.l=!0,e.exports}var c={};return b.m=a,b.c=c,b.d=function(a,c,d){b.o(a,c)||Object.defineProperty(a,c,{configurable:!1,enumerable:!0,get:d})},b.n=function(a){var c=a&&a.__esModule?function(){return a['default']}:function(){return a};return b.d(c,'a',c),c},b.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},b.p='',b(b.s=100)}({0:function(a){'use strict';var g=function(){};!1,a.exports=function(h,i,j,a,b,c,d,e){if(g(i),!h){var f;if(void 0===i)f=new Error('Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.');else{var k=[j,a,b,c,d,e],l=0;f=new Error(i.replace(/%s/g,function(){return k[l++]})),f.name='Invariant Violation'}throw f.framesToPop=1,f}}},100:function(a,b,c){a.exports=c(101)()},101:function(a,b,c){'use strict';var d=c(5),e=c(0),f=c(19);a.exports=function(){function a(a,b,c,d,g,h){h===f||e(!1,'Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types')}function b(){return a}a.isRequired=a;var c={array:a,bool:a,func:a,number:a,object:a,string:a,symbol:a,any:a,arrayOf:b,element:a,instanceOf:b,node:a,objectOf:b,oneOf:b,oneOfType:b,shape:b};return c.checkPropTypes=d,c.PropTypes=c,c}},19:function(a){'use strict';a.exports='SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'},5:function(a){'use strict';function b(a){return function(){return a}}var c=function(){};c.thatReturns=b,c.thatReturnsFalse=b(!1),c.thatReturnsTrue=b(!0),c.thatReturnsNull=b(null),c.thatReturnsThis=function(){return this},c.thatReturnsArgument=function(a){return a},a.exports=c}});this.EXPORTED_SYMBOLS = ["PropTypes"];
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1 +1 @@
|
|||
/* eslint-disable */this.classnames=function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,b),e.l=!0,e.exports}var c={};return b.m=a,b.c=c,b.d=function(a,c,d){b.o(a,c)||Object.defineProperty(a,c,{configurable:!1,enumerable:!0,get:d})},b.n=function(a){var c=a&&a.__esModule?function(){return a['default']}:function(){return a};return b.d(c,'a',c),c},b.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},b.p='',b(b.s=93)}({93:function(a,b){var c,d;(function(){'use strict';function e(){for(var a,b=[],c=0;c<arguments.length;c++)if(a=arguments[c],a){var d=typeof a;if('string'==d||'number'==d)b.push(a);else if(Array.isArray(a))b.push(e.apply(null,a));else if('object'==d)for(var g in a)f.call(a,g)&&a[g]&&b.push(g)}return b.join(' ')}var f={}.hasOwnProperty;'undefined'!=typeof a&&a.exports?a.exports=e:(c=[],d=function(){return e}.apply(b,c),!(d!==void 0&&(a.exports=d)))})()}});this.EXPORTED_SYMBOLS = ["classnames"];
|
||||
/* eslint-disable */this.classnames=function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,b),e.l=!0,e.exports}var c={};return b.m=a,b.c=c,b.d=function(a,c,d){b.o(a,c)||Object.defineProperty(a,c,{configurable:!1,enumerable:!0,get:d})},b.n=function(a){var c=a&&a.__esModule?function(){return a['default']}:function(){return a};return b.d(c,'a',c),c},b.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},b.p='',b(b.s=92)}({92:function(a,b){var c,d;(function(){'use strict';function e(){for(var a,b=[],c=0;c<arguments.length;c++)if(a=arguments[c],a){var d=typeof a;if('string'==d||'number'==d)b.push(a);else if(Array.isArray(a))b.push(e.apply(null,a));else if('object'==d)for(var g in a)f.call(a,g)&&a[g]&&b.push(g)}return b.join(' ')}var f={}.hasOwnProperty;'undefined'!=typeof a&&a.exports?a.exports=e:(c=[],d=function(){return e}.apply(b,c),!(d!==void 0&&(a.exports=d)))})()}});this.EXPORTED_SYMBOLS = ["classnames"];
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -648,7 +648,7 @@ class ContextMenu {
|
|||
cleanTarget.ownerDocument = {
|
||||
// used for nsContextMenu.initLeaveDOMFullScreenItems and
|
||||
// nsContextMenu.initMediaPlayerItems
|
||||
fullscreenElement: context.target.ownerDocument.fullscreenElement,
|
||||
fullscreen: context.target.ownerDocument.fullscreen,
|
||||
|
||||
// used for nsContextMenu.initMiscItems
|
||||
contentType: context.target.ownerDocument.contentType,
|
||||
|
|
|
@ -38,7 +38,6 @@ class PingCentre {
|
|||
}
|
||||
|
||||
this._topic = options.topic;
|
||||
this._filter = options.filter;
|
||||
this._prefs = Services.prefs.getBranch("");
|
||||
|
||||
this._setPingEndpoint(options.topic, options.overrideEndpointPref);
|
||||
|
@ -92,12 +91,12 @@ class PingCentre {
|
|||
this._fhrEnabled = this._prefs.getBoolPref(prefKey);
|
||||
}
|
||||
|
||||
_createExperimentsString(activeExperiments) {
|
||||
_createExperimentsString(activeExperiments, filter) {
|
||||
let experimentsString = "";
|
||||
for (let experimentID in activeExperiments) {
|
||||
if (!activeExperiments[experimentID] ||
|
||||
!activeExperiments[experimentID].branch ||
|
||||
(this._filter && !experimentID.includes(this._filter))) {
|
||||
(filter && !experimentID.includes(filter))) {
|
||||
continue;
|
||||
}
|
||||
let expString = `${experimentID}:${activeExperiments[experimentID].branch}`;
|
||||
|
@ -106,9 +105,10 @@ class PingCentre {
|
|||
return experimentsString;
|
||||
}
|
||||
|
||||
async sendPing(data) {
|
||||
async sendPing(data, options) {
|
||||
let filter = options && options.filter;
|
||||
let experiments = TelemetryEnvironment.getActiveExperiments();
|
||||
let experimentsString = this._createExperimentsString(experiments);
|
||||
let experimentsString = this._createExperimentsString(experiments, filter);
|
||||
if (!this.enabled) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
|
|
@ -554,6 +554,7 @@ html|span.ac-emphasize-text-url {
|
|||
color: -moz-menubartext;
|
||||
}
|
||||
|
||||
#nav-bar,
|
||||
#toolbar-menubar:not([autohide="true"]):not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
|
||||
#TabsToolbar:not(:-moz-lwtheme):-moz-system-metric(menubar-drag) {
|
||||
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-drag");
|
||||
|
|
|
@ -92,14 +92,14 @@
|
|||
to { transform: perspective(5000px) rotateY(360deg); }
|
||||
}
|
||||
|
||||
:root:not([uidensity=compact]) #PanelUI-button {
|
||||
:root:not([uidensity=compact]):not([chromehidden~="toolbar"]) #PanelUI-button {
|
||||
margin-inline-start: 3px;
|
||||
border-inline-start: 1px solid;
|
||||
border-image: linear-gradient(transparent 4px, rgba(0,0,0,.1) 4px, rgba(0,0,0,.1) calc(100% - 4px), transparent calc(100% - 4px));
|
||||
border-image-slice: 1;
|
||||
}
|
||||
|
||||
:root:not([uidensity=compact]) #nav-bar[brighttext] > #PanelUI-button {
|
||||
:root:not([uidensity=compact]):not([chromehidden~="toolbar"]) #nav-bar[brighttext] > #PanelUI-button {
|
||||
border-image-source: linear-gradient(transparent 4px, rgba(100%,100%,100%,.2) 4px, rgba(100%,100%,100%,.2) calc(100% - 4px), transparent calc(100% - 4px));
|
||||
}
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill="#0C0C0D" d="M9.24 16.03v2.73c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24v-2.73h-.9c-.75 0-1.35-.59-1.35-1.33v-8h11.2v8c0 .74-.6 1.33-1.36 1.33h-.9v2.73c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24v-2.73H9.24zM2.76 6.5c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zm14.64 0c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zM6.6 0c.06 0 .12.03.16.1l.9 1.58a6.04 6.04 0 0 1 4.84 0L13.4.1a.18.18 0 0 1 .24-.07c.09.05.12.15.07.24l-.89 1.58a5.04 5.04 0 0 1 2.86 4.43H4.48c0-1.9 1.15-3.56 2.85-4.43L6.45.26a.17.17 0 0 1 .07-.24A.18.18 0 0 1 6.6 0zm.9 3.33c-.26 0-.47.2-.47.46a.47.47 0 0 0 .93 0 .47.47 0 0 0-.47-.46zm5.16 0c-.25 0-.47.2-.47.46a.47.47 0 0 0 .94 0 .47.47 0 0 0-.47-.46z"/>
|
||||
<path fill="context-fill" d="M9.24 16.03v2.73c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24v-2.73h-.9c-.75 0-1.35-.59-1.35-1.33v-8h11.2v8c0 .74-.6 1.33-1.36 1.33h-.9v2.73c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24v-2.73H9.24zM2.76 6.5c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zm14.64 0c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zM6.6 0c.06 0 .12.03.16.1l.9 1.58a6.04 6.04 0 0 1 4.84 0L13.4.1a.18.18 0 0 1 .24-.07c.09.05.12.15.07.24l-.89 1.58a5.04 5.04 0 0 1 2.86 4.43H4.48c0-1.9 1.15-3.56 2.85-4.43L6.45.26a.17.17 0 0 1 .07-.24A.18.18 0 0 1 6.6 0zm.9 3.33c-.26 0-.47.2-.47.46a.47.47 0 0 0 .93 0 .47.47 0 0 0-.47-.46zm5.16 0c-.25 0-.47.2-.47.46a.47.47 0 0 0 .94 0 .47.47 0 0 0-.47-.46z"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -2,5 +2,5 @@
|
|||
- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill="#0C0C0D" fill-rule="evenodd" d="M16.84 15.03c-.27.63-.59 1.2-.96 1.74-.51.72-.93 1.23-1.25 1.5-.5.46-1.03.7-1.6.71-.41 0-.9-.12-1.48-.35a4.25 4.25 0 0 0-1.6-.36c-.5 0-1.05.12-1.63.36A4.4 4.4 0 0 1 6.9 19c-.55.02-1.1-.22-1.64-.73-.35-.3-.78-.82-1.3-1.56-.56-.78-1.02-1.7-1.38-2.73A10.03 10.03 0 0 1 2 10.7c0-1.2.26-2.25.78-3.12a4.6 4.6 0 0 1 3.86-2.28c.43 0 1 .13 1.71.4.71.26 1.16.4 1.36.4.15 0 .66-.16 1.51-.47.8-.3 1.5-.41 2.05-.37 1.51.13 2.65.72 3.4 1.8a3.8 3.8 0 0 0-2 3.44 4.1 4.1 0 0 0 2.5 3.68c-.1.3-.21.57-.33.84zM13.37 1.36c0 .9-.33 1.74-.98 2.52-.8.92-1.75 1.46-2.79 1.37a3.95 3.95 0 0 1 1.02-2.89c.34-.38.76-.7 1.28-.95.51-.25 1-.39 1.45-.41.02.12.02.24.02.36z"/>
|
||||
<path fill="context-fill" d="M16.84 15.03c-.27.63-.59 1.2-.96 1.74-.51.72-.93 1.23-1.25 1.5-.5.46-1.03.7-1.6.71-.41 0-.9-.12-1.48-.35a4.25 4.25 0 0 0-1.6-.36c-.5 0-1.05.12-1.63.36A4.4 4.4 0 0 1 6.9 19c-.55.02-1.1-.22-1.64-.73-.35-.3-.78-.82-1.3-1.56-.56-.78-1.02-1.7-1.38-2.73A10.03 10.03 0 0 1 2 10.7c0-1.2.26-2.25.78-3.12a4.6 4.6 0 0 1 3.86-2.28c.43 0 1 .13 1.71.4.71.26 1.16.4 1.36.4.15 0 .66-.16 1.51-.47.8-.3 1.5-.41 2.05-.37 1.51.13 2.65.72 3.4 1.8a3.8 3.8 0 0 0-2 3.44 4.1 4.1 0 0 0 2.5 3.68c-.1.3-.21.57-.33.84zM13.37 1.36c0 .9-.33 1.74-.98 2.52-.8.92-1.75 1.46-2.79 1.37a3.95 3.95 0 0 1 1.02-2.89c.34-.38.76-.7 1.28-.95.51-.25 1-.39 1.45-.41.02.12.02.24.02.36z"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 970 B После Ширина: | Высота: | Размер: 955 B |
|
@ -582,6 +582,8 @@ button > hbox > label {
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: text-bottom;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
#updateDeck > hbox > label {
|
||||
|
|
|
@ -528,21 +528,15 @@ tabbrowser {
|
|||
|
||||
/* Tab separators */
|
||||
|
||||
%ifdef CAN_DRAW_IN_TITLEBAR
|
||||
/* Add space for dragging the window */
|
||||
%ifdef MOZ_WIDGET_COCOA
|
||||
:root[tabsintitlebar]:not([sizemode=fullscreen]) .titlebar-placeholder[type="pre-tabs"]
|
||||
%elifdef MENUBAR_CAN_AUTOHIDE
|
||||
:root[tabsintitlebar][sizemode=normal] #toolbar-menubar[autohide=true] ~ #TabsToolbar > .titlebar-placeholder[type="pre-tabs"]
|
||||
%else
|
||||
:root[tabsintitlebar][sizemode=normal] #TabsToolbar > .titlebar-placeholder[type="pre-tabs"]
|
||||
%endif
|
||||
{
|
||||
.titlebar-placeholder[type="pre-tabs"],
|
||||
.titlebar-placeholder[type="post-tabs"] {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.titlebar-placeholder[type="pre-tabs"] {
|
||||
border-inline-end: 1px solid;
|
||||
opacity: 0.2;
|
||||
}
|
||||
%endif
|
||||
|
||||
.tabbrowser-tab::after,
|
||||
.tabbrowser-tab::before {
|
||||
|
|
|
@ -23,21 +23,17 @@
|
|||
}
|
||||
|
||||
@media (-moz-windows-compositor) {
|
||||
#main-window {
|
||||
-moz-appearance: -moz-win-glass;
|
||||
}
|
||||
|
||||
|
||||
/* On win10, if we don't set this on the entire browser container including
|
||||
* the sidebar, if the sidebar is open the accent color bleeds through in
|
||||
* the titlebar */
|
||||
#browser {
|
||||
-moz-appearance: -moz-win-exclude-glass;
|
||||
}
|
||||
|
||||
@media not all and (-moz-os-version: windows-win7) {
|
||||
@media not all and (-moz-os-version: windows-win8) {
|
||||
@media (-moz-windows-default-theme) {
|
||||
:root[sizemode=normal][tabsintitlebar] {
|
||||
border-top: 1px solid -moz-win-accentcolor;
|
||||
}
|
||||
|
||||
:root[sizemode=normal][tabsintitlebar]:-moz-window-inactive {
|
||||
border-top-color: rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
:root:not(:-moz-lwtheme) {
|
||||
background-color: hsl(0, 0%, 78%);
|
||||
}
|
||||
|
@ -66,6 +62,14 @@
|
|||
@media (-moz-windows-default-theme: 0) {
|
||||
:root {
|
||||
background-color: transparent;
|
||||
-moz-appearance: -moz-win-glass;
|
||||
}
|
||||
|
||||
/* On win10, if we don't set this on the entire browser container including
|
||||
* the sidebar, if the sidebar is open the accent color bleeds through in
|
||||
* the titlebar */
|
||||
#browser {
|
||||
-moz-appearance: -moz-win-exclude-glass;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,15 +234,15 @@
|
|||
|
||||
@media (-moz-os-version: windows-win7),
|
||||
(-moz-os-version: windows-win8) {
|
||||
#main-window[sizemode="maximized"] #titlebar-buttonbox {
|
||||
margin-inline-end: 3px;
|
||||
}
|
||||
|
||||
#main-window {
|
||||
:root {
|
||||
background-color: transparent;
|
||||
-moz-appearance: -moz-win-borderless-glass;
|
||||
}
|
||||
|
||||
:root[sizemode="maximized"] #titlebar-buttonbox {
|
||||
margin-inline-end: 3px;
|
||||
}
|
||||
|
||||
/* These should be hidden w/ glass enabled. Windows draws its own buttons. */
|
||||
.titlebar-button {
|
||||
display: none;
|
||||
|
@ -247,10 +251,6 @@
|
|||
/* The borders on the glass frame are ours, and inside #browser, and on
|
||||
* win7 we want to make sure they are "glassy", so we can't use #browser
|
||||
* as the exclude-glass container. We use #appcontent instead. */
|
||||
#browser {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
#appcontent {
|
||||
-moz-appearance: -moz-win-exclude-glass;
|
||||
}
|
||||
|
@ -326,6 +326,11 @@
|
|||
border-top-right-radius: 2.5px;
|
||||
}
|
||||
|
||||
/* Set to full fill-opacity to improve visibility of toolbar buttons on aero glass. */
|
||||
#TabsToolbar:not(:-moz-lwtheme) {
|
||||
--toolbarbutton-icon-fill-opacity: 1;
|
||||
}
|
||||
|
||||
#toolbar-menubar:not(:-moz-lwtheme) {
|
||||
text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
|
||||
}
|
||||
|
|
|
@ -296,10 +296,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.titlebar-placeholder[type="caption-buttons"] {
|
||||
margin-left: 22px; /* space needed for Aero Snap */
|
||||
}
|
||||
|
||||
/* titlebar command buttons */
|
||||
|
||||
#titlebar-min {
|
||||
|
|
|
@ -20,12 +20,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media (-moz-windows-compositor) {
|
||||
#main-window {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
#toolbar-menubar {
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
@ -46,6 +40,11 @@
|
|||
}
|
||||
|
||||
@media (-moz-windows-glass) {
|
||||
/* Set to full fill-opacity to improve visibility of toolbar buttons on aero glass. */
|
||||
#TabsToolbar {
|
||||
--toolbarbutton-icon-fill-opacity: 1;
|
||||
}
|
||||
|
||||
/* Make the menubar text readable on aero glass (copied from browser-aero.css). */
|
||||
#toolbar-menubar {
|
||||
text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
|
||||
|
@ -60,6 +59,12 @@
|
|||
|
||||
@media (-moz-os-version: windows-win7),
|
||||
(-moz-os-version: windows-win8) {
|
||||
@media (-moz-windows-compositor) {
|
||||
#main-window {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Always show full-height tab separators on tabs with borders. */
|
||||
.tabbrowser-tab::before {
|
||||
border-image: none !important;
|
||||
|
|
|
@ -96,7 +96,7 @@ add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target)
|
|||
# Xcode state
|
||||
# ===========
|
||||
|
||||
option('--disable-xcode-checks',
|
||||
js_option('--disable-xcode-checks',
|
||||
help='Do not check that Xcode is installed and properly configured')
|
||||
|
||||
@depends(host, '--disable-xcode-checks')
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<script>
|
||||
function fzfn(e){
|
||||
let nc=window.frames[0].document.body.childElementCount;
|
||||
let o=window.frames[0].document.body.childNodes[1%nc];
|
||||
document.getElementById('b').appendChild(o.parentNode.removeChild(o));
|
||||
}
|
||||
window.onload=function(){
|
||||
document.body.onerror=fzfn;
|
||||
document.getElementById('c').addEventListener('DOMNodeRemoved', fzfn, true);
|
||||
document.getElementById('c').attributes[0].name=='id';
|
||||
window.frames[0].document.body.appendChild(document.getElementById('c'));
|
||||
document.getElementById('a').innerHTML=document.createElement('multicol').outerHTML;
|
||||
}
|
||||
</script>
|
||||
<iframe></iframe>
|
||||
<script id='a'></script>
|
||||
<script id='b'></script>
|
||||
<script id='c'></script>
|
|
@ -225,3 +225,4 @@ load 1383780.html
|
|||
pref(clipboard.autocopy,true) load 1385272-1.html
|
||||
load 1393806.html
|
||||
load 1400701.html
|
||||
load 1403377.html
|
||||
|
|
|
@ -7791,15 +7791,19 @@ nsDOMAttributeMap::BlastSubtreeToPieces(nsINode *aNode)
|
|||
Element *element = aNode->AsElement();
|
||||
const nsDOMAttributeMap *map = element->GetAttributeMap();
|
||||
if (map) {
|
||||
// This non-standard style of iteration is presumably used because some
|
||||
// of the code in the loop body can trigger element removal, which
|
||||
// invalidates the iterator.
|
||||
while (true) {
|
||||
auto iter = map->mAttributeCache.ConstIter();
|
||||
if (iter.Done()) {
|
||||
break;
|
||||
nsCOMPtr<nsIAttribute> attr;
|
||||
{
|
||||
// Use an iterator to get an arbitrary attribute from the
|
||||
// cache. The iterator must be destroyed before any other
|
||||
// operations on mAttributeCache, to avoid hash table
|
||||
// assertions.
|
||||
auto iter = map->mAttributeCache.ConstIter();
|
||||
if (iter.Done()) {
|
||||
break;
|
||||
}
|
||||
attr = iter.UserData();
|
||||
}
|
||||
nsCOMPtr<nsIAttribute> attr = iter.UserData();
|
||||
NS_ASSERTION(attr.get(),
|
||||
"non-nsIAttribute somehow made it into the hashmap?!");
|
||||
|
||||
|
@ -13608,6 +13612,29 @@ nsIDocument::ReportHasScrollLinkedEffect()
|
|||
"ScrollLinkedEffectFound2");
|
||||
}
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
// URL-based blacklist for stylo.
|
||||
static bool
|
||||
ShouldUseGeckoBackend(nsIURI* aDocumentURI)
|
||||
{
|
||||
if (!aDocumentURI) {
|
||||
return false;
|
||||
}
|
||||
bool isScheme = false;
|
||||
if (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isScheme))) {
|
||||
nsAutoCString path;
|
||||
aDocumentURI->GetFilePath(path);
|
||||
// about:reader requires support of :scope pseudo-class so we have
|
||||
// to use Gecko backend for now. See bug 1402094.
|
||||
// This should be fixed by bug 1204818.
|
||||
if (path.EqualsLiteral("reader")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // MOZ_STYLO
|
||||
|
||||
void
|
||||
nsIDocument::UpdateStyleBackendType()
|
||||
{
|
||||
|
@ -13626,7 +13653,7 @@ nsIDocument::UpdateStyleBackendType()
|
|||
// Note that, since tests can have XUL support, we still need to
|
||||
// explicitly exclude XUL documents here.
|
||||
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()) &&
|
||||
!IsXULDocument()) {
|
||||
!IsXULDocument() && !ShouldUseGeckoBackend(mDocumentURI)) {
|
||||
mStyleBackendType = StyleBackendType::Servo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3129,7 +3129,7 @@ MediaDecoderStateMachine::RequestAudioData()
|
|||
mStateObj->HandleAudioDecoded(aAudio);
|
||||
},
|
||||
[this, self] (const MediaResult& aError) {
|
||||
LOGV("OnAudioNotDecoded aError=%" PRIu32, static_cast<uint32_t>(aError.Code()));
|
||||
LOGV("OnAudioNotDecoded aError=%s", aError.ErrorName().get());
|
||||
mAudioDataRequest.Complete();
|
||||
switch (aError.Code()) {
|
||||
case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
|
||||
|
@ -3175,7 +3175,7 @@ MediaDecoderStateMachine::RequestVideoData(const media::TimeUnit& aCurrentTime)
|
|||
mStateObj->HandleVideoDecoded(aVideo, videoDecodeStartTime);
|
||||
},
|
||||
[this, self] (const MediaResult& aError) {
|
||||
LOGV("OnVideoNotDecoded aError=%" PRIu32 , static_cast<uint32_t>(aError.Code()));
|
||||
LOGV("OnVideoNotDecoded aError=%s" , aError.ErrorName().get());
|
||||
mVideoDataRequest.Complete();
|
||||
switch (aError.Code()) {
|
||||
case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
|
||||
|
|
|
@ -1635,9 +1635,9 @@ void
|
|||
MediaFormatReader::OnDemuxFailed(TrackType aTrack, const MediaResult& aError)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
LOG("Failed to demux %s, failure:%" PRIu32,
|
||||
LOG("Failed to demux %s, failure:%s",
|
||||
aTrack == TrackType::kVideoTrack ? "video" : "audio",
|
||||
static_cast<uint32_t>(aError.Code()));
|
||||
aError.ErrorName().get());
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
decoder.mDemuxRequest.Complete();
|
||||
switch (aError.Code()) {
|
||||
|
@ -2823,7 +2823,7 @@ void
|
|||
MediaFormatReader::OnSeekFailed(TrackType aTrack, const MediaResult& aError)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
LOGV("%s failure:%s" PRIu32, TrackTypeToStr(aTrack), aError.ErrorName().get());
|
||||
LOGV("%s failure:%s", TrackTypeToStr(aTrack), aError.ErrorName().get());
|
||||
if (aTrack == TrackType::kVideoTrack) {
|
||||
mVideo.mSeekRequest.Complete();
|
||||
} else {
|
||||
|
|
|
@ -61,10 +61,8 @@ public:
|
|||
if (NS_SUCCEEDED(mCode)) {
|
||||
return nsCString();
|
||||
}
|
||||
nsCString name;
|
||||
GetErrorName(mCode, name);
|
||||
return nsPrintfCString("%s (0x%08" PRIx32 ")%s%s",
|
||||
name.get(),
|
||||
ErrorName().get(),
|
||||
static_cast<uint32_t>(mCode),
|
||||
mMessage.IsEmpty() ? "" : " - ",
|
||||
mMessage.get());
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#ifndef CDMCaps_h_
|
||||
#define CDMCaps_h_
|
||||
|
||||
#include "gmp-decryption.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
dom::Optional<dom::MediaKeyStatus> mStatus;
|
||||
};
|
||||
|
||||
// Time is defined as the number of milliseconds since the
|
||||
// Epoch (00:00:00 UTC, January 1, 1970).
|
||||
typedef int64_t UnixTime;
|
||||
|
||||
// Proxies calls CDM, and proxies calls back.
|
||||
|
|
|
@ -133,7 +133,7 @@ public:
|
|||
private:
|
||||
|
||||
// Instantiate CDMProxy instance.
|
||||
// It could be MediaDrmCDMProxy (Widevine on Fennec) or GMPCDMProxy (the rest).
|
||||
// It could be MediaDrmCDMProxy (Widevine on Fennec) or ChromiumCDMProxy (the rest).
|
||||
already_AddRefed<CDMProxy> CreateCDMProxy(nsIEventTarget* aMainThread);
|
||||
|
||||
// Removes promise from mPromises, and returns it.
|
||||
|
|
|
@ -33,57 +33,36 @@
|
|||
*************************************************************************************
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "gmp-platform.h"
|
||||
#include "gmp-video-decode.h"
|
||||
|
||||
#if defined(GMP_FAKE_SUPPORT_DECRYPT)
|
||||
#include "gmp-decryption.h"
|
||||
#include "gmp-test-decryptor.h"
|
||||
#include "gmp-test-storage.h"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define PUBLIC_FUNC __declspec(dllexport)
|
||||
#else
|
||||
#define PUBLIC_FUNC
|
||||
#endif
|
||||
|
||||
GMPPlatformAPI* g_platform_api = nullptr;
|
||||
#include "stddef.h"
|
||||
#include "cdm-test-decryptor.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "content_decryption_module_ext.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
PUBLIC_FUNC GMPErr
|
||||
GMPInit (GMPPlatformAPI* aPlatformAPI) {
|
||||
g_platform_api = aPlatformAPI;
|
||||
return GMPNoErr;
|
||||
}
|
||||
CDM_API
|
||||
void INITIALIZE_CDM_MODULE() {
|
||||
|
||||
PUBLIC_FUNC GMPErr
|
||||
GMPGetAPI (const char* aApiName, void* aHostAPI, void** aPluginApi) {
|
||||
if (!strcmp (aApiName, GMP_API_VIDEO_DECODER)) {
|
||||
// Note: Deliberately advertise in our .info file that we support
|
||||
// video-decode, but we fail the "get" call here to simulate what
|
||||
// happens when decoder init fails.
|
||||
return GMPGenericErr;
|
||||
#if defined(GMP_FAKE_SUPPORT_DECRYPT)
|
||||
}
|
||||
if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
|
||||
*aPluginApi = new FakeDecryptor();
|
||||
return GMPNoErr;
|
||||
#endif
|
||||
}
|
||||
return GMPGenericErr;
|
||||
}
|
||||
}
|
||||
|
||||
PUBLIC_FUNC void
|
||||
GMPShutdown (void) {
|
||||
g_platform_api = nullptr;
|
||||
}
|
||||
CDM_API
|
||||
void* CreateCdmInstance(int cdm_interface_version,
|
||||
const char* key_system,
|
||||
uint32_t key_system_size,
|
||||
GetCdmHostFunc get_cdm_host_func,
|
||||
void* user_data)
|
||||
{
|
||||
cdm::Host_8* host = static_cast<cdm::Host_8*>(
|
||||
get_cdm_host_func(cdm_interface_version, user_data));
|
||||
return new FakeDecryptor(host);
|
||||
}
|
||||
|
||||
|
||||
CDM_API
|
||||
bool
|
||||
VerifyCdmHost_0(const cdm::HostFile* aHostFiles, uint32_t aNumFiles)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // extern "C"
|
|
@ -3,10 +3,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gmp-test-decryptor.h"
|
||||
#include "gmp-test-storage.h"
|
||||
#include "gmp-test-output-protection.h"
|
||||
#include "cdm-test-decryptor.h"
|
||||
#include "cdm-test-storage.h"
|
||||
#include "cdm-test-output-protection.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
@ -14,6 +15,7 @@
|
|||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
@ -21,28 +23,14 @@
|
|||
using namespace std;
|
||||
|
||||
FakeDecryptor* FakeDecryptor::sInstance = nullptr;
|
||||
extern GMPPlatformAPI* g_platform_api; // Defined in gmp-fake.cpp
|
||||
|
||||
class GMPMutexAutoLock
|
||||
{
|
||||
public:
|
||||
explicit GMPMutexAutoLock(GMPMutex* aMutex) : mMutex(aMutex) {
|
||||
mMutex->Acquire();
|
||||
}
|
||||
~GMPMutexAutoLock() {
|
||||
mMutex->Release();
|
||||
}
|
||||
private:
|
||||
GMPMutex* const mMutex;
|
||||
};
|
||||
|
||||
class TestManager {
|
||||
public:
|
||||
TestManager() : mMutex(CreateMutex()) {}
|
||||
TestManager() = default;
|
||||
|
||||
// Register a test with the test manager.
|
||||
void BeginTest(const string& aTestID) {
|
||||
GMPMutexAutoLock lock(mMutex);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
auto found = mTestIDs.find(aTestID);
|
||||
if (found == mTestIDs.end()) {
|
||||
mTestIDs.insert(aTestID);
|
||||
|
@ -57,7 +45,7 @@ public:
|
|||
void EndTest(const string& aTestID) {
|
||||
bool isEmpty = false;
|
||||
{
|
||||
GMPMutexAutoLock lock(mMutex);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
auto found = mTestIDs.find(aTestID);
|
||||
if (found != mTestIDs.end()) {
|
||||
mTestIDs.erase(aTestID);
|
||||
|
@ -74,9 +62,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
~TestManager() {
|
||||
mMutex->Destroy();
|
||||
}
|
||||
~TestManager() = default;
|
||||
|
||||
static void Error(const string& msg) {
|
||||
FakeDecryptor::Message(msg);
|
||||
|
@ -86,37 +72,29 @@ private:
|
|||
FakeDecryptor::Message("test-storage complete");
|
||||
}
|
||||
|
||||
static GMPMutex* CreateMutex() {
|
||||
GMPMutex* mutex = nullptr;
|
||||
g_platform_api->createmutex(&mutex);
|
||||
return mutex;
|
||||
}
|
||||
|
||||
GMPMutex* const mMutex;
|
||||
std::mutex mMutex;
|
||||
set<string> mTestIDs;
|
||||
};
|
||||
|
||||
FakeDecryptor::FakeDecryptor()
|
||||
: mCallback(nullptr)
|
||||
FakeDecryptor::FakeDecryptor(cdm::Host_8* aHost)
|
||||
: mHost(aHost)
|
||||
{
|
||||
MOZ_ASSERT(!sInstance);
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
void FakeDecryptor::DecryptingComplete()
|
||||
{
|
||||
sInstance = nullptr;
|
||||
delete this;
|
||||
}
|
||||
|
||||
void
|
||||
FakeDecryptor::Message(const std::string& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(sInstance);
|
||||
const static std::string sid("fake-session-id");
|
||||
sInstance->mCallback->SessionMessage(sid.c_str(), sid.size(),
|
||||
kGMPLicenseRequest,
|
||||
(const uint8_t*)aMessage.c_str(), aMessage.size());
|
||||
sInstance->mHost->OnSessionMessage(sid.c_str(),
|
||||
sid.size(),
|
||||
cdm::MessageType::kLicenseRequest,
|
||||
aMessage.c_str(),
|
||||
aMessage.size(),
|
||||
nullptr,
|
||||
0);
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
|
@ -130,40 +108,38 @@ Tokenize(const std::string& aString)
|
|||
static const string TruncateRecordId = "truncate-record-id";
|
||||
static const string TruncateRecordData = "I will soon be truncated";
|
||||
|
||||
class ReadThenTask : public GMPTask {
|
||||
template<class Continuation>
|
||||
class WriteRecordSuccessTask {
|
||||
public:
|
||||
ReadThenTask(string aId, ReadContinuation* aThen)
|
||||
WriteRecordSuccessTask(string aId, Continuation aThen)
|
||||
: mId(aId)
|
||||
, mThen(aThen)
|
||||
, mThen(move(aThen))
|
||||
{}
|
||||
void Run() override {
|
||||
ReadRecord(mId, mThen);
|
||||
}
|
||||
void Destroy() override {
|
||||
delete this;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
ReadRecord(FakeDecryptor::sInstance->mHost, mId, mThen);
|
||||
}
|
||||
|
||||
string mId;
|
||||
ReadContinuation* mThen;
|
||||
Continuation mThen;
|
||||
};
|
||||
|
||||
class SendMessageTask : public GMPTask {
|
||||
class WriteRecordFailureTask {
|
||||
public:
|
||||
explicit SendMessageTask(const string& aMessage,
|
||||
TestManager* aTestManager = nullptr,
|
||||
const string& aTestID = "")
|
||||
explicit WriteRecordFailureTask(const string& aMessage,
|
||||
TestManager* aTestManager = nullptr,
|
||||
const string& aTestID = "")
|
||||
: mMessage(aMessage), mTestmanager(aTestManager), mTestID(aTestID) {}
|
||||
|
||||
void Run() override {
|
||||
void operator()()
|
||||
{
|
||||
FakeDecryptor::Message(mMessage);
|
||||
if (mTestmanager) {
|
||||
mTestmanager->EndTest(mTestID);
|
||||
}
|
||||
}
|
||||
|
||||
void Destroy() override {
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
string mMessage;
|
||||
TestManager* const mTestmanager;
|
||||
|
@ -175,12 +151,14 @@ public:
|
|||
TestEmptyContinuation(TestManager* aTestManager, const string& aTestID)
|
||||
: mTestmanager(aTestManager), mTestID(aTestID) {}
|
||||
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) override {
|
||||
if (!aData.empty()) {
|
||||
virtual void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
if (aDataSize) {
|
||||
FakeDecryptor::Message("FAIL TestEmptyContinuation record was not truncated");
|
||||
}
|
||||
mTestmanager->EndTest(mTestID);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -195,15 +173,18 @@ public:
|
|||
const string& aTestID)
|
||||
: mID(aID), mTestmanager(aTestManager), mTestID(aTestID) {}
|
||||
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) override {
|
||||
if (aData != TruncateRecordData) {
|
||||
virtual void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
if (string(reinterpret_cast<const char*>(aData), aDataSize) != TruncateRecordData) {
|
||||
FakeDecryptor::Message("FAIL TruncateContinuation read data doesn't match written data");
|
||||
}
|
||||
auto cont = new TestEmptyContinuation(mTestmanager, mTestID);
|
||||
auto cont = TestEmptyContinuation(mTestmanager, mTestID);
|
||||
auto msg = "FAIL in TruncateContinuation write.";
|
||||
auto failTask = new SendMessageTask(msg, mTestmanager, mTestID);
|
||||
WriteRecord(mID, nullptr, 0, new ReadThenTask(mID, cont), failTask);
|
||||
delete this;
|
||||
WriteRecord(FakeDecryptor::sInstance->mHost, mID, nullptr, 0,
|
||||
WriteRecordSuccessTask<TestEmptyContinuation>(mID, cont),
|
||||
WriteRecordFailureTask(msg, mTestmanager, mTestID));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -219,12 +200,14 @@ public:
|
|||
const string& aTestID)
|
||||
: mValue(aValue), mTestmanager(aTestManager), mTestID(aTestID) {}
|
||||
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) override {
|
||||
if (aData != mValue) {
|
||||
virtual void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
if (string(reinterpret_cast<const char*>(aData), aDataSize) != mValue) {
|
||||
FakeDecryptor::Message("FAIL VerifyAndFinishContinuation read data doesn't match expected data");
|
||||
}
|
||||
mTestmanager->EndTest(mTestID);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -244,15 +227,18 @@ public:
|
|||
, mTestID(aTestID)
|
||||
{}
|
||||
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) override {
|
||||
if (aData != mValue) {
|
||||
virtual void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
if (string(reinterpret_cast<const char*>(aData), aDataSize) != mValue) {
|
||||
FakeDecryptor::Message("FAIL VerifyAndOverwriteContinuation read data doesn't match expected data");
|
||||
}
|
||||
auto cont = new VerifyAndFinishContinuation(mOverwrite, mTestmanager, mTestID);
|
||||
auto cont = VerifyAndFinishContinuation(mOverwrite, mTestmanager, mTestID);
|
||||
auto msg = "FAIL in VerifyAndOverwriteContinuation write.";
|
||||
auto failTask = new SendMessageTask(msg, mTestmanager, mTestID);
|
||||
WriteRecord(mId, mOverwrite, new ReadThenTask(mId, cont), failTask);
|
||||
delete this;
|
||||
WriteRecord(FakeDecryptor::sInstance->mHost, mId, mOverwrite,
|
||||
WriteRecordSuccessTask<VerifyAndFinishContinuation>(mId, cont),
|
||||
WriteRecordFailureTask(msg, mTestmanager, mTestID));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -267,27 +253,21 @@ static const string OpenAgainRecordId = "open-again-record-id";
|
|||
|
||||
class OpenedSecondTimeContinuation : public OpenContinuation {
|
||||
public:
|
||||
explicit OpenedSecondTimeContinuation(GMPRecord* aRecord,
|
||||
TestManager* aTestManager,
|
||||
explicit OpenedSecondTimeContinuation(TestManager* aTestManager,
|
||||
const string& aTestID)
|
||||
: mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) {
|
||||
MOZ_ASSERT(aRecord);
|
||||
: mTestmanager(aTestManager), mTestID(aTestID)
|
||||
{
|
||||
}
|
||||
|
||||
void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) override {
|
||||
if (GMP_SUCCEEDED(aStatus)) {
|
||||
void operator()(bool aSuccess) override {
|
||||
if (!aSuccess) {
|
||||
FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record.");
|
||||
}
|
||||
if (aRecord) {
|
||||
aRecord->Close();
|
||||
}
|
||||
// Succeeded, open should have failed.
|
||||
mTestmanager->EndTest(mTestID);
|
||||
mRecord->Close();
|
||||
}
|
||||
|
||||
private:
|
||||
GMPRecord* mRecord;
|
||||
TestManager* const mTestmanager;
|
||||
const string mTestID;
|
||||
};
|
||||
|
@ -299,18 +279,15 @@ public:
|
|||
const string& aTestID)
|
||||
: mID(aID), mTestmanager(aTestManager), mTestID(aTestID) {}
|
||||
|
||||
void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) override {
|
||||
if (GMP_FAILED(aStatus)) {
|
||||
void operator()(bool aSuccess) override {
|
||||
if (!aSuccess) {
|
||||
FakeDecryptor::Message("FAIL OpenAgainContinuation to open record initially.");
|
||||
mTestmanager->EndTest(mTestID);
|
||||
if (aRecord) {
|
||||
aRecord->Close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto cont = new OpenedSecondTimeContinuation(aRecord, mTestmanager, mTestID);
|
||||
GMPOpenRecord(mID, cont);
|
||||
auto cont = OpenedSecondTimeContinuation(mTestmanager, mTestID);
|
||||
OpenRecord(FakeDecryptor::sInstance->mHost, mID, cont);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -322,8 +299,9 @@ private:
|
|||
static void
|
||||
DoTestStorage(const string& aPrefix, TestManager* aTestManager)
|
||||
{
|
||||
MOZ_ASSERT(FakeDecryptor::sInstance->mHost, "FakeDecryptor::sInstance->mHost should not be null");
|
||||
// Basic I/O tests. We run three cases concurrently. The tests, like
|
||||
// GMPStorage run asynchronously. When they've all passed, we send
|
||||
// CDMStorage run asynchronously. When they've all passed, we send
|
||||
// a message back to the parent process, or a failure message if not.
|
||||
|
||||
// Test 1: Basic I/O test, and test that writing 0 bytes in a record
|
||||
|
@ -336,11 +314,11 @@ DoTestStorage(const string& aPrefix, TestManager* aTestManager)
|
|||
const string id1 = aPrefix + TruncateRecordId;
|
||||
const string testID1 = aPrefix + "write-test-1";
|
||||
aTestManager->BeginTest(testID1);
|
||||
auto cont1 = new TruncateContinuation(id1, aTestManager, testID1);
|
||||
auto cont1 = TruncateContinuation(id1, aTestManager, testID1);
|
||||
auto msg1 = "FAIL in TestStorage writing TruncateRecord.";
|
||||
auto failTask1 = new SendMessageTask(msg1, aTestManager, testID1);
|
||||
WriteRecord(id1, TruncateRecordData,
|
||||
new ReadThenTask(id1, cont1), failTask1);
|
||||
WriteRecord(FakeDecryptor::sInstance->mHost, id1, TruncateRecordData,
|
||||
WriteRecordSuccessTask<TruncateContinuation>(id1, cont1),
|
||||
WriteRecordFailureTask(msg1, aTestManager, testID1));
|
||||
|
||||
// Test 2: Test that overwriting a record with a shorter record truncates
|
||||
// the record to the shorter record.
|
||||
|
@ -354,11 +332,12 @@ DoTestStorage(const string& aPrefix, TestManager* aTestManager)
|
|||
string overwrite = "A shorter record";
|
||||
const string testID2 = aPrefix + "write-test-2";
|
||||
aTestManager->BeginTest(testID2);
|
||||
auto task2 = new VerifyAndOverwriteContinuation(id2, record1, overwrite,
|
||||
aTestManager, testID2);
|
||||
auto task2 = VerifyAndOverwriteContinuation(id2, record1, overwrite,
|
||||
aTestManager, testID2);
|
||||
auto msg2 = "FAIL in TestStorage writing record1.";
|
||||
auto failTask2 = new SendMessageTask(msg2, aTestManager, testID2);
|
||||
WriteRecord(id2, record1, new ReadThenTask(id2, task2), failTask2);
|
||||
WriteRecord(FakeDecryptor::sInstance->mHost, id2, record1,
|
||||
WriteRecordSuccessTask<VerifyAndOverwriteContinuation>(id2, task2),
|
||||
WriteRecordFailureTask(msg2, aTestManager, testID2));
|
||||
|
||||
// Test 3: Test that opening a record while it's already open fails.
|
||||
//
|
||||
|
@ -368,71 +347,33 @@ DoTestStorage(const string& aPrefix, TestManager* aTestManager)
|
|||
const string id3 = aPrefix + OpenAgainRecordId;
|
||||
const string testID3 = aPrefix + "open-test-1";
|
||||
aTestManager->BeginTest(testID3);
|
||||
auto task3 = new OpenedFirstTimeContinuation(id3, aTestManager, testID3);
|
||||
GMPOpenRecord(id3, task3);
|
||||
auto task3 = OpenedFirstTimeContinuation(id3, aTestManager, testID3);
|
||||
OpenRecord(FakeDecryptor::sInstance->mHost, id3, task3);
|
||||
}
|
||||
|
||||
class TestStorageTask : public GMPTask {
|
||||
public:
|
||||
TestStorageTask(const string& aPrefix, TestManager* aTestManager)
|
||||
: mPrefix(aPrefix), mTestManager(aTestManager) {}
|
||||
void Destroy() override { delete this; }
|
||||
void Run() override {
|
||||
DoTestStorage(mPrefix, mTestManager);
|
||||
}
|
||||
private:
|
||||
const string mPrefix;
|
||||
TestManager* const mTestManager;
|
||||
};
|
||||
|
||||
void
|
||||
FakeDecryptor::TestStorage()
|
||||
{
|
||||
auto* testManager = new TestManager();
|
||||
GMPThread* thread1 = nullptr;
|
||||
GMPThread* thread2 = nullptr;
|
||||
|
||||
// Main thread tests.
|
||||
DoTestStorage("mt1-", testManager);
|
||||
DoTestStorage("mt2-", testManager);
|
||||
|
||||
// Off-main-thread tests.
|
||||
if (GMP_SUCCEEDED(g_platform_api->createthread(&thread1))) {
|
||||
thread1->Post(new TestStorageTask("thread1-", testManager));
|
||||
} else {
|
||||
FakeDecryptor::Message("FAIL to create thread1 for storage tests");
|
||||
}
|
||||
|
||||
if (GMP_SUCCEEDED(g_platform_api->createthread(&thread2))) {
|
||||
thread2->Post(new TestStorageTask("thread2-", testManager));
|
||||
} else {
|
||||
FakeDecryptor::Message("FAIL to create thread2 for storage tests");
|
||||
}
|
||||
|
||||
if (thread1) {
|
||||
thread1->Join();
|
||||
}
|
||||
|
||||
if (thread2) {
|
||||
thread2->Join();
|
||||
}
|
||||
|
||||
// Note: Once all tests finish, TestManager will dispatch "test-pass" message,
|
||||
// which ends the test for the parent.
|
||||
}
|
||||
|
||||
class ReportWritten : public GMPTask {
|
||||
class ReportWritten
|
||||
{
|
||||
public:
|
||||
ReportWritten(const string& aRecordId, const string& aValue)
|
||||
: mRecordId(aRecordId)
|
||||
, mValue(aValue)
|
||||
{}
|
||||
void Run() override {
|
||||
void operator()() {
|
||||
FakeDecryptor::Message("stored " + mRecordId + " " + mValue);
|
||||
}
|
||||
void Destroy() override {
|
||||
delete this;
|
||||
}
|
||||
|
||||
const string mRecordId;
|
||||
const string mValue;
|
||||
};
|
||||
|
@ -442,18 +383,20 @@ public:
|
|||
explicit ReportReadStatusContinuation(const string& aRecordId)
|
||||
: mRecordId(aRecordId)
|
||||
{}
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) override {
|
||||
if (GMP_FAILED(aErr)) {
|
||||
void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
if (!aSuccess) {
|
||||
FakeDecryptor::Message("retrieve " + mRecordId + " failed");
|
||||
} else {
|
||||
stringstream ss;
|
||||
ss << aData.size();
|
||||
ss << aDataSize;
|
||||
string len;
|
||||
ss >> len;
|
||||
FakeDecryptor::Message("retrieve " + mRecordId + " succeeded (length " +
|
||||
len + " bytes)");
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
string mRecordId;
|
||||
};
|
||||
|
@ -463,13 +406,17 @@ public:
|
|||
explicit ReportReadRecordContinuation(const string& aRecordId)
|
||||
: mRecordId(aRecordId)
|
||||
{}
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) override {
|
||||
if (GMP_FAILED(aErr)) {
|
||||
void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
if (!aSuccess) {
|
||||
FakeDecryptor::Message("retrieved " + mRecordId + " failed");
|
||||
} else {
|
||||
FakeDecryptor::Message("retrieved " + mRecordId + " " + aData);
|
||||
FakeDecryptor::Message("retrieved " + mRecordId + " " +
|
||||
string(reinterpret_cast<const char*>(aData),
|
||||
aDataSize));
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
string mRecordId;
|
||||
};
|
||||
|
@ -490,6 +437,7 @@ FakeDecryptor::UpdateSession(uint32_t aPromiseId,
|
|||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize)
|
||||
{
|
||||
MOZ_ASSERT(FakeDecryptor::sInstance->mHost, "FakeDecryptor::sInstance->mHost should not be null");
|
||||
std::string response((const char*)aResponse, (const char*)(aResponse)+aResponseSize);
|
||||
std::vector<std::string> tokens = Tokenize(response);
|
||||
const string& task = tokens[0];
|
||||
|
@ -499,13 +447,14 @@ FakeDecryptor::UpdateSession(uint32_t aPromiseId,
|
|||
// send "stored record" message on complete.
|
||||
const string& id = tokens[1];
|
||||
const string& value = tokens[2];
|
||||
WriteRecord(id,
|
||||
WriteRecord(FakeDecryptor::sInstance->mHost,
|
||||
id,
|
||||
value,
|
||||
new ReportWritten(id, value),
|
||||
new SendMessageTask("FAIL in writing record."));
|
||||
ReportWritten(id, value),
|
||||
WriteRecordFailureTask("FAIL in writing record."));
|
||||
} else if (task == "retrieve") {
|
||||
const string& id = tokens[1];
|
||||
ReadRecord(id, new ReportReadStatusContinuation(id));
|
||||
ReadRecord(FakeDecryptor::sInstance->mHost, id, ReportReadStatusContinuation(id));
|
||||
} else if (task == "shutdown-mode") {
|
||||
const string& mode = tokens[1];
|
||||
if (mode == "timeout") {
|
||||
|
@ -516,8 +465,10 @@ FakeDecryptor::UpdateSession(uint32_t aPromiseId,
|
|||
Message("shutdown-token received " + sShutdownToken);
|
||||
}
|
||||
} else if (task == "retrieve-shutdown-token") {
|
||||
ReadRecord("shutdown-token", new ReportReadRecordContinuation("shutdown-token"));
|
||||
ReadRecord(FakeDecryptor::sInstance->mHost,
|
||||
"shutdown-token",
|
||||
ReportReadRecordContinuation("shutdown-token"));
|
||||
} else if (task == "test-op-apis") {
|
||||
mozilla::gmptest::TestOuputProtectionAPIs();
|
||||
mozilla::cdmtest::TestOuputProtectionAPIs();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef FAKE_DECRYPTOR_H__
|
||||
#define FAKE_DECRYPTOR_H__
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include <string>
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class FakeDecryptor : public cdm::ContentDecryptionModule_8 {
|
||||
public:
|
||||
explicit FakeDecryptor(cdm::Host_8* aHost);
|
||||
|
||||
void Initialize(bool aAllowDistinctiveIdentifier,
|
||||
bool aAllowPersistentState) override
|
||||
{
|
||||
}
|
||||
|
||||
void SetServerCertificate(uint32_t aPromiseId,
|
||||
const uint8_t* aServerCertificateData,
|
||||
uint32_t aServerCertificateDataSize)
|
||||
override
|
||||
{
|
||||
}
|
||||
|
||||
void CreateSessionAndGenerateRequest(uint32_t aPromiseId,
|
||||
cdm::SessionType aSessionType,
|
||||
cdm::InitDataType aInitDataType,
|
||||
const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize)
|
||||
override
|
||||
{
|
||||
}
|
||||
|
||||
void LoadSession(uint32_t aPromiseId,
|
||||
cdm::SessionType aSessionType,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdSize,
|
||||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize) override;
|
||||
|
||||
void CloseSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void TimerExpired(void* aContext) override
|
||||
{
|
||||
}
|
||||
|
||||
cdm::Status Decrypt(const cdm::InputBuffer& aEncryptedBuffer,
|
||||
cdm::DecryptedBlock* aDecryptedBuffer) override
|
||||
{
|
||||
return cdm::Status::kDecodeError;
|
||||
}
|
||||
|
||||
cdm::Status InitializeAudioDecoder(
|
||||
const cdm::AudioDecoderConfig& aAudioDecoderConfig) override
|
||||
{
|
||||
return cdm::Status::kDecodeError;
|
||||
}
|
||||
|
||||
cdm::Status InitializeVideoDecoder(
|
||||
const cdm::VideoDecoderConfig& aVideoDecoderConfig) override
|
||||
{
|
||||
return cdm::Status::kDecodeError;
|
||||
}
|
||||
|
||||
void DeinitializeDecoder(cdm::StreamType aDecoderType) override
|
||||
{
|
||||
}
|
||||
|
||||
void ResetDecoder(cdm::StreamType aDecoderType) override
|
||||
{
|
||||
}
|
||||
|
||||
cdm::Status DecryptAndDecodeFrame(
|
||||
const cdm::InputBuffer& aEncryptedBuffer,
|
||||
cdm::VideoFrame* aVideoFrame) override
|
||||
{
|
||||
return cdm::Status::kDecodeError;
|
||||
}
|
||||
|
||||
cdm::Status DecryptAndDecodeSamples(
|
||||
const cdm::InputBuffer& aEncryptedBuffer,
|
||||
cdm::AudioFrames* aAudioFrame) override
|
||||
{
|
||||
return cdm::Status::kDecodeError;
|
||||
}
|
||||
|
||||
void OnPlatformChallengeResponse(
|
||||
const cdm::PlatformChallengeResponse& aResponse) override
|
||||
{
|
||||
}
|
||||
|
||||
void OnQueryOutputProtectionStatus(cdm::QueryResult aResult,
|
||||
uint32_t aLinkMask,
|
||||
uint32_t aOutputProtectionMask) override
|
||||
{
|
||||
}
|
||||
|
||||
void Destroy() override
|
||||
{
|
||||
delete this;
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
static void Message(const std::string& aMessage);
|
||||
|
||||
cdm::Host_8* mHost;
|
||||
|
||||
static FakeDecryptor* sInstance;
|
||||
|
||||
private:
|
||||
|
||||
virtual ~FakeDecryptor() {}
|
||||
|
||||
void TestStorage();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -15,7 +15,7 @@
|
|||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmptest {
|
||||
namespace cdmtest {
|
||||
|
||||
#if defined(XP_WIN)
|
||||
typedef HRESULT(STDAPICALLTYPE * OPMGetVideoOutputsFromHMONITORProc)
|
||||
|
@ -125,5 +125,5 @@ TestOuputProtectionAPIs()
|
|||
FakeDecryptor::Message("OP tests completed");
|
||||
}
|
||||
|
||||
} // namespace gmptest
|
||||
} // namespace cdmtest
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,252 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "cdm-test-storage.h"
|
||||
#include <vector>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
using namespace cdm;
|
||||
using namespace std;
|
||||
|
||||
class WriteRecordClient : public FileIOClient
|
||||
{
|
||||
public:
|
||||
WriteRecordClient(function<void()>&& aOnSuccess,
|
||||
function<void()>&& aOnFailure,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize)
|
||||
: mOnSuccess(move(aOnSuccess))
|
||||
, mOnFailure(move(aOnFailure))
|
||||
{
|
||||
mData.insert(mData.end(), aData, aData + aDataSize);
|
||||
}
|
||||
|
||||
void OnOpenComplete(Status aStatus) override
|
||||
{
|
||||
// If we hit an error, fail.
|
||||
if (aStatus != Status::kSuccess) {
|
||||
Done(aStatus);
|
||||
} else if (mFileIO) { // Otherwise, write our data to the file.
|
||||
mFileIO->Write(mData.empty() ? nullptr : &mData.front(), mData.size());
|
||||
}
|
||||
}
|
||||
|
||||
void OnReadComplete(Status aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void OnWriteComplete(Status aStatus) override
|
||||
{
|
||||
Done(aStatus);
|
||||
}
|
||||
|
||||
void Do(const string& aName, Host_8* aHost)
|
||||
{
|
||||
// Initialize the FileIO.
|
||||
mFileIO = aHost->CreateFileIO(this);
|
||||
mFileIO->Open(aName.c_str(), aName.size());
|
||||
}
|
||||
|
||||
private:
|
||||
void Done(cdm::FileIOClient::Status aStatus)
|
||||
{
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
if (mFileIO) {
|
||||
// will delete mFileIO inside Close.
|
||||
mFileIO->Close();
|
||||
}
|
||||
|
||||
if (IO_SUCCEEDED(aStatus)) {
|
||||
mOnSuccess();
|
||||
} else {
|
||||
mOnFailure();
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
FileIO* mFileIO = nullptr;
|
||||
function<void()> mOnSuccess;
|
||||
function<void()> mOnFailure;
|
||||
std::vector<uint8_t> mData;
|
||||
};
|
||||
|
||||
void
|
||||
WriteRecord(Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
const uint8_t* aData,
|
||||
uint32_t aNumBytes,
|
||||
function<void()>&& aOnSuccess,
|
||||
function<void()>&& aOnFailure)
|
||||
{
|
||||
// client will be delete in WriteRecordClient::Done
|
||||
WriteRecordClient* client = new WriteRecordClient(move(aOnSuccess),
|
||||
move(aOnFailure),
|
||||
aData,
|
||||
aNumBytes);
|
||||
client->Do(aRecordName, aHost);
|
||||
}
|
||||
|
||||
void
|
||||
WriteRecord(Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
const std::string& aData,
|
||||
function<void()> &&aOnSuccess,
|
||||
function<void()>&& aOnFailure)
|
||||
{
|
||||
return WriteRecord(aHost,
|
||||
aRecordName,
|
||||
(const uint8_t*)aData.c_str(),
|
||||
aData.size(),
|
||||
move(aOnSuccess),
|
||||
move(aOnFailure));
|
||||
}
|
||||
|
||||
class ReadRecordClient : public FileIOClient
|
||||
{
|
||||
public:
|
||||
explicit ReadRecordClient(function<void(bool, const uint8_t*, uint32_t)>&& aOnReadComplete)
|
||||
: mOnReadComplete(move(aOnReadComplete))
|
||||
{
|
||||
}
|
||||
|
||||
void OnOpenComplete(Status aStatus) override
|
||||
{
|
||||
auto err = aStatus;
|
||||
if (aStatus != Status::kSuccess) {
|
||||
Done(err, reinterpret_cast<const uint8_t*>(""), 0);
|
||||
} else {
|
||||
mFileIO->Read();
|
||||
}
|
||||
}
|
||||
|
||||
void OnReadComplete(Status aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
Done(aStatus, aData, aDataSize);
|
||||
}
|
||||
|
||||
void OnWriteComplete(Status aStatus) override
|
||||
{
|
||||
}
|
||||
|
||||
void Do(const string& aName, Host_8* aHost)
|
||||
{
|
||||
mFileIO = aHost->CreateFileIO(this);
|
||||
mFileIO->Open(aName.c_str(), aName.size());
|
||||
}
|
||||
|
||||
private:
|
||||
void Done(cdm::FileIOClient::Status aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize)
|
||||
{
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
if (mFileIO) {
|
||||
// will delete mFileIO inside Close.
|
||||
mFileIO->Close();
|
||||
}
|
||||
|
||||
if (IO_SUCCEEDED(aStatus)) {
|
||||
mOnReadComplete(true, aData, aDataSize);
|
||||
} else {
|
||||
mOnReadComplete(false, reinterpret_cast<const uint8_t*>(""), 0);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
FileIO* mFileIO = nullptr;
|
||||
function<void(bool, const uint8_t*, uint32_t)> mOnReadComplete;
|
||||
};
|
||||
|
||||
void
|
||||
ReadRecord(Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
function<void(bool, const uint8_t*, uint32_t)>&& aOnReadComplete)
|
||||
{
|
||||
// client will be delete in ReadRecordClient::Done
|
||||
ReadRecordClient* client = new ReadRecordClient(move(aOnReadComplete));
|
||||
client->Do(aRecordName, aHost);
|
||||
}
|
||||
|
||||
class OpenRecordClient : public FileIOClient
|
||||
{
|
||||
public:
|
||||
explicit OpenRecordClient(function<void(bool)>&& aOpenComplete)
|
||||
: mOpenComplete(move(aOpenComplete))
|
||||
{
|
||||
}
|
||||
|
||||
void OnOpenComplete(Status aStatus) override
|
||||
{
|
||||
Done(aStatus);
|
||||
}
|
||||
|
||||
void OnReadComplete(Status aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void OnWriteComplete(Status aStatus) override
|
||||
{
|
||||
}
|
||||
|
||||
void Do(const string& aName, Host_8* aHost)
|
||||
{
|
||||
// Initialize the FileIO.
|
||||
mFileIO = aHost->CreateFileIO(this);
|
||||
mFileIO->Open(aName.c_str(), aName.size());
|
||||
}
|
||||
|
||||
private:
|
||||
void Done(cdm::FileIOClient::Status aStatus)
|
||||
{
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
if (mFileIO) {
|
||||
// will delete mFileIO inside Close.
|
||||
mFileIO->Close();
|
||||
}
|
||||
|
||||
if (IO_SUCCEEDED(aStatus)) {
|
||||
mOpenComplete(true);
|
||||
} else {
|
||||
mOpenComplete(false);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
FileIO* mFileIO = nullptr;
|
||||
function<void(bool)> mOpenComplete;;
|
||||
};
|
||||
|
||||
void
|
||||
OpenRecord(Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
function<void(bool)>&& aOpenComplete)
|
||||
{
|
||||
// client will be delete in OpenRecordClient::Done
|
||||
OpenRecordClient* client = new OpenRecordClient(move(aOpenComplete));
|
||||
client->Do(aRecordName, aHost);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TEST_CDM_STORAGE_H__
|
||||
#define TEST_CDM_STORAGE_H__
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
// This include is required in order for content_decryption_module to work
|
||||
// on Unix systems.
|
||||
#include "stddef.h"
|
||||
#include "content_decryption_module.h"
|
||||
|
||||
#define IO_SUCCEEDED(x) ((x) == cdm::FileIOClient::Status::kSuccess)
|
||||
#define IO_FAILED(x) ((x) != cdm::FileIOClient::Status::kSuccess)
|
||||
|
||||
class ReadContinuation {
|
||||
public:
|
||||
virtual ~ReadContinuation() {}
|
||||
virtual void operator()(bool aSuccess,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) = 0;
|
||||
};
|
||||
|
||||
void WriteRecord(cdm::Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
const std::string& aData,
|
||||
std::function<void()>&& aOnSuccess,
|
||||
std::function<void()>&& aOnFailure);
|
||||
|
||||
void WriteRecord(cdm::Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
const uint8_t* aData,
|
||||
uint32_t aNumBytes,
|
||||
std::function<void()>&& aOnSuccess,
|
||||
std::function<void()>&& aOnFailure);
|
||||
|
||||
void ReadRecord(cdm::Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
std::function<void(bool, const uint8_t*, uint32_t)>&& aOnReadComplete);
|
||||
|
||||
class OpenContinuation {
|
||||
public:
|
||||
virtual ~OpenContinuation() {}
|
||||
virtual void operator()(bool aSuccess) = 0;
|
||||
};
|
||||
|
||||
void OpenRecord(cdm::Host_8* aHost,
|
||||
const std::string& aRecordName,
|
||||
std::function<void(bool)>&& aOpenComplete);
|
||||
#endif // TEST_CDM_STORAGE_H__
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "fake",
|
||||
"description": "Fake CDM Plugin",
|
||||
"version": "1",
|
||||
"x-cdm-module-versions": "4",
|
||||
"x-cdm-interface-versions": "8",
|
||||
"x-cdm-host-versions": "8",
|
||||
"x-cdm-codecs": ""
|
||||
}
|
|
@ -7,17 +7,16 @@
|
|||
FINAL_TARGET = 'dist/bin/gmp-fake/1.0'
|
||||
|
||||
FINAL_TARGET_FILES += [
|
||||
'fake.info',
|
||||
'fake.voucher',
|
||||
'manifest.json',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'gmp-fake.cpp',
|
||||
'gmp-test-decryptor.cpp',
|
||||
'gmp-test-storage.cpp',
|
||||
'cdm-fake.cpp',
|
||||
'cdm-test-decryptor.cpp',
|
||||
'cdm-test-storage.cpp',
|
||||
]
|
||||
|
||||
DEFINES['GMP_FAKE_SUPPORT_DECRYPT'] = True
|
||||
DEFINES['CDM_IMPLEMENTATION'] = True
|
||||
|
||||
SharedLibrary("fake")
|
||||
|
|
@ -1 +0,0 @@
|
|||
gmp-fakeopenh264 placeholder voucher
|
|
@ -50,12 +50,6 @@
|
|||
#include "gmp-video-frame-i420.h"
|
||||
#include "gmp-video-frame-encoded.h"
|
||||
|
||||
#if defined(GMP_FAKE_SUPPORT_DECRYPT)
|
||||
#include "gmp-decryption.h"
|
||||
#include "gmp-test-decryptor.h"
|
||||
#include "gmp-test-storage.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -476,12 +470,6 @@ extern "C" {
|
|||
if (!strcmp (aApiName, GMP_API_VIDEO_ENCODER)) {
|
||||
*aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
|
||||
return GMPNoErr;
|
||||
#if defined(GMP_FAKE_SUPPORT_DECRYPT)
|
||||
}
|
||||
if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
|
||||
*aPluginApi = new FakeDecryptor();
|
||||
return GMPNoErr;
|
||||
#endif
|
||||
}
|
||||
return GMPGenericErr;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ FINAL_TARGET = 'dist/bin/gmp-fakeopenh264/1.0'
|
|||
|
||||
FINAL_TARGET_FILES += [
|
||||
'fakeopenh264.info',
|
||||
'fakeopenh264.voucher',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
Name: fake
|
||||
Description: Fake GMP Plugin.
|
||||
Version: 1.0
|
||||
APIs: decode-video[h264:broken], eme-decrypt-v9[fake]
|
||||
Libraries: dxva2.dll
|
|
@ -1 +0,0 @@
|
|||
gmp-fake placeholder voucher
|
|
@ -1,84 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef FAKE_DECRYPTOR_H__
|
||||
#define FAKE_DECRYPTOR_H__
|
||||
|
||||
#include "gmp-decryption.h"
|
||||
#include <string>
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class FakeDecryptor : public GMPDecryptor {
|
||||
public:
|
||||
|
||||
explicit FakeDecryptor();
|
||||
|
||||
void Init(GMPDecryptorCallback* aCallback,
|
||||
bool aDistinctiveIdentifierRequired,
|
||||
bool aPersistentStateRequired) override
|
||||
{
|
||||
mCallback = aCallback;
|
||||
}
|
||||
|
||||
void CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const char* aInitDataType,
|
||||
uint32_t aInitDataTypeSize,
|
||||
const uint8_t* aInitData,
|
||||
uint32_t aInitDataSize,
|
||||
GMPSessionType aSessionType) override
|
||||
{
|
||||
}
|
||||
|
||||
void LoadSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aResponse,
|
||||
uint32_t aResponseSize) override;
|
||||
|
||||
void CloseSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override
|
||||
{
|
||||
}
|
||||
|
||||
void SetServerCertificate(uint32_t aPromiseId,
|
||||
const uint8_t* aServerCert,
|
||||
uint32_t aServerCertSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void Decrypt(GMPBuffer* aBuffer,
|
||||
GMPEncryptedBufferMetadata* aMetadata) override
|
||||
{
|
||||
}
|
||||
|
||||
void DecryptingComplete() override;
|
||||
|
||||
static void Message(const std::string& aMessage);
|
||||
|
||||
private:
|
||||
|
||||
virtual ~FakeDecryptor() {}
|
||||
static FakeDecryptor* sInstance;
|
||||
|
||||
void TestStorage();
|
||||
|
||||
GMPDecryptorCallback* mCallback;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,226 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "gmp-test-storage.h"
|
||||
#include <vector>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class WriteRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
GMPErr Init(GMPRecord* aRecord,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) {
|
||||
mRecord = aRecord;
|
||||
mOnSuccess = aOnSuccess;
|
||||
mOnFailure = aOnFailure;
|
||||
mData.insert(mData.end(), aData, aData + aDataSize);
|
||||
return mRecord->Open();
|
||||
}
|
||||
|
||||
void OpenComplete(GMPErr aStatus) override {
|
||||
if (GMP_SUCCEEDED(aStatus)) {
|
||||
mRecord->Write(mData.empty() ? nullptr : &mData.front(), mData.size());
|
||||
} else {
|
||||
GMPRunOnMainThread(mOnFailure);
|
||||
mOnSuccess->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override {}
|
||||
|
||||
void WriteComplete(GMPErr aStatus) override {
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
mRecord->Close();
|
||||
if (GMP_SUCCEEDED(aStatus)) {
|
||||
GMPRunOnMainThread(mOnSuccess);
|
||||
mOnFailure->Destroy();
|
||||
} else {
|
||||
GMPRunOnMainThread(mOnFailure);
|
||||
mOnSuccess->Destroy();
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
GMPRecord* mRecord;
|
||||
GMPTask* mOnSuccess;
|
||||
GMPTask* mOnFailure;
|
||||
std::vector<uint8_t> mData;
|
||||
};
|
||||
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const uint8_t* aData,
|
||||
uint32_t aNumBytes,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure)
|
||||
{
|
||||
GMPRecord* record;
|
||||
auto* client = new WriteRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
aRecordName.size(),
|
||||
&record,
|
||||
client);
|
||||
if (GMP_FAILED(err)) {
|
||||
GMPRunOnMainThread(aOnFailure);
|
||||
aOnSuccess->Destroy();
|
||||
return err;
|
||||
}
|
||||
return client->Init(record, aOnSuccess, aOnFailure, aData, aNumBytes);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const std::string& aData,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure)
|
||||
{
|
||||
return WriteRecord(aRecordName,
|
||||
(const uint8_t*)aData.c_str(),
|
||||
aData.size(),
|
||||
aOnSuccess,
|
||||
aOnFailure);
|
||||
}
|
||||
|
||||
class ReadRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
GMPErr Init(GMPRecord* aRecord,
|
||||
ReadContinuation* aContinuation) {
|
||||
mRecord = aRecord;
|
||||
mContinuation = aContinuation;
|
||||
return mRecord->Open();
|
||||
}
|
||||
|
||||
void OpenComplete(GMPErr aStatus) override {
|
||||
auto err = mRecord->Read();
|
||||
if (GMP_FAILED(err)) {
|
||||
mContinuation->ReadComplete(err, "");
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override {
|
||||
// Note: Call Close() before running continuation, in case the
|
||||
// continuation tries to open the same record; if we call Close()
|
||||
// after running the continuation, the Close() call will arrive
|
||||
// just after the Open() call succeeds, immediately closing the
|
||||
// record we just opened.
|
||||
mRecord->Close();
|
||||
std::string data((const char*)aData, aDataSize);
|
||||
mContinuation->ReadComplete(GMPNoErr, data);
|
||||
delete this;
|
||||
}
|
||||
|
||||
void WriteComplete(GMPErr aStatus) override {
|
||||
}
|
||||
|
||||
private:
|
||||
GMPRecord* mRecord;
|
||||
ReadContinuation* mContinuation;
|
||||
};
|
||||
|
||||
GMPErr
|
||||
ReadRecord(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation)
|
||||
{
|
||||
MOZ_ASSERT(aContinuation);
|
||||
GMPRecord* record;
|
||||
auto* client = new ReadRecordClient();
|
||||
auto err = GMPOpenRecord(aRecordName.c_str(),
|
||||
aRecordName.size(),
|
||||
&record,
|
||||
client);
|
||||
if (GMP_FAILED(err)) {
|
||||
return err;
|
||||
}
|
||||
return client->Init(record, aContinuation);
|
||||
}
|
||||
|
||||
extern GMPPlatformAPI* g_platform_api; // Defined in gmp-fake.cpp
|
||||
|
||||
GMPErr
|
||||
GMPOpenRecord(const char* aName,
|
||||
uint32_t aNameLength,
|
||||
GMPRecord** aOutRecord,
|
||||
GMPRecordClient* aClient)
|
||||
{
|
||||
MOZ_ASSERT(g_platform_api);
|
||||
return g_platform_api->createrecord(aName, aNameLength, aOutRecord, aClient);
|
||||
}
|
||||
|
||||
GMPErr
|
||||
GMPRunOnMainThread(GMPTask* aTask)
|
||||
{
|
||||
MOZ_ASSERT(g_platform_api);
|
||||
return g_platform_api->runonmainthread(aTask);
|
||||
}
|
||||
|
||||
class OpenRecordClient : public GMPRecordClient {
|
||||
public:
|
||||
/*
|
||||
* This function will take the memory ownership of the parameters and
|
||||
* delete them when done.
|
||||
*/
|
||||
static void Open(const std::string& aRecordName,
|
||||
OpenContinuation* aContinuation) {
|
||||
MOZ_ASSERT(aContinuation);
|
||||
(new OpenRecordClient(aContinuation))->Do(aRecordName);
|
||||
}
|
||||
|
||||
void OpenComplete(GMPErr aStatus) override {
|
||||
Done(aStatus);
|
||||
}
|
||||
|
||||
void ReadComplete(GMPErr aStatus,
|
||||
const uint8_t* aData,
|
||||
uint32_t aDataSize) override {
|
||||
MOZ_CRASH("Should not reach here.");
|
||||
}
|
||||
|
||||
void WriteComplete(GMPErr aStatus) override {
|
||||
MOZ_CRASH("Should not reach here.");
|
||||
}
|
||||
|
||||
private:
|
||||
explicit OpenRecordClient(OpenContinuation* aContinuation)
|
||||
: mRecord(nullptr), mContinuation(aContinuation) {}
|
||||
|
||||
void Do(const std::string& aName) {
|
||||
auto err = GMPOpenRecord(aName.c_str(), aName.size(), &mRecord, this);
|
||||
if (GMP_FAILED(err) ||
|
||||
GMP_FAILED(err = mRecord->Open())) {
|
||||
Done(err);
|
||||
}
|
||||
}
|
||||
|
||||
void Done(GMPErr err) {
|
||||
// mContinuation is responsible for closing mRecord.
|
||||
mContinuation->OpenComplete(err, mRecord);
|
||||
delete mContinuation;
|
||||
delete this;
|
||||
}
|
||||
|
||||
GMPRecord* mRecord;
|
||||
OpenContinuation* mContinuation;
|
||||
};
|
||||
|
||||
void
|
||||
GMPOpenRecord(const std::string& aRecordName,
|
||||
OpenContinuation* aContinuation)
|
||||
{
|
||||
OpenRecordClient::Open(aRecordName, aContinuation);
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TEST_GMP_STORAGE_H__
|
||||
#define TEST_GMP_STORAGE_H__
|
||||
|
||||
#include "gmp-errors.h"
|
||||
#include "gmp-platform.h"
|
||||
#include <string>
|
||||
|
||||
class ReadContinuation {
|
||||
public:
|
||||
virtual ~ReadContinuation() {}
|
||||
virtual void ReadComplete(GMPErr aErr, const std::string& aData) = 0;
|
||||
};
|
||||
|
||||
// Reads a record to storage using GMPRecord.
|
||||
// Calls ReadContinuation with read data.
|
||||
GMPErr
|
||||
ReadRecord(const std::string& aRecordName,
|
||||
ReadContinuation* aContinuation);
|
||||
|
||||
// Writes a record to storage using GMPRecord.
|
||||
// Runs continuation when data is written.
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const std::string& aData,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure);
|
||||
|
||||
GMPErr
|
||||
WriteRecord(const std::string& aRecordName,
|
||||
const uint8_t* aData,
|
||||
uint32_t aNumBytes,
|
||||
GMPTask* aOnSuccess,
|
||||
GMPTask* aOnFailure);
|
||||
|
||||
GMPErr
|
||||
GMPOpenRecord(const char* aName,
|
||||
uint32_t aNameLength,
|
||||
GMPRecord** aOutRecord,
|
||||
GMPRecordClient* aClient);
|
||||
|
||||
GMPErr
|
||||
GMPRunOnMainThread(GMPTask* aTask);
|
||||
|
||||
class OpenContinuation {
|
||||
public:
|
||||
virtual ~OpenContinuation() {}
|
||||
virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0;
|
||||
};
|
||||
|
||||
void
|
||||
GMPOpenRecord(const std::string& aRecordName,
|
||||
OpenContinuation* aContinuation);
|
||||
|
||||
#endif // TEST_GMP_STORAGE_H__
|
|
@ -8,9 +8,7 @@
|
|||
#include "content_decryption_module_ext.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "gmp-api/gmp-entrypoints.h"
|
||||
#include "gmp-api/gmp-decryption.h"
|
||||
#include "gmp-api/gmp-video-codec.h"
|
||||
#include "gmp-api/gmp-platform.h"
|
||||
#include "WidevineUtils.h"
|
||||
#include "GMPLog.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
|
|
@ -494,7 +494,7 @@ ChromiumCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId)
|
|||
|
||||
void
|
||||
ChromiumCDMProxy::OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
UnixTime aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
|
|
|
@ -71,7 +71,7 @@ public:
|
|||
const nsTArray<uint8_t>& aMessage) override;
|
||||
|
||||
void OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime) override;
|
||||
UnixTime aExpiryTime) override;
|
||||
|
||||
void OnSessionClosed(const nsAString& aSessionId) override;
|
||||
|
||||
|
@ -130,4 +130,4 @@ private:
|
|||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPCDMProxy_h_
|
||||
#endif // ChromiumCDMProxy_h_
|
||||
|
|
|
@ -1,231 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPCDMCallbackProxy.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/EMEUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
GMPCDMCallbackProxy::GMPCDMCallbackProxy(CDMProxy* aProxy,
|
||||
nsIEventTarget* aMainThread)
|
||||
: mProxy(aProxy)
|
||||
, mMainThread(aMainThread)
|
||||
{}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::SetDecryptorId(uint32_t aId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::SetDecryptorId",
|
||||
[proxy, aId]() { proxy->OnSetDecryptorId(aId); }),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::SetSessionId(uint32_t aToken,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction(
|
||||
"GMPCDMCallbackProxy::SetSessionId",
|
||||
[proxy, aToken, sid]() { proxy->OnSetSessionId(aToken, sid); }),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::ResolveLoadSessionPromise(uint32_t aPromiseId,
|
||||
bool aSuccess)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::ResolveLoadSessionPromise",
|
||||
[proxy, aPromiseId, aSuccess]() {
|
||||
proxy->OnResolveLoadSessionPromise(aPromiseId,
|
||||
aSuccess);
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
// Note: CDMProxy proxies this from non-main threads to main thread.
|
||||
mProxy->ResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::RejectPromise",
|
||||
[proxy, aPromiseId, aException, aMessage]() {
|
||||
proxy->OnRejectPromise(
|
||||
aPromiseId, aException, aMessage);
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
|
||||
dom::MediaKeyMessageType aMessageType,
|
||||
const nsTArray<uint8_t>& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
nsTArray<uint8_t> msg(aMessage);
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::SessionMessage",
|
||||
[proxy, sid, aMessageType, msg]() mutable {
|
||||
proxy->OnSessionMessage(sid, aMessageType, msg);
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::ExpirationChange",
|
||||
[proxy, sid, aExpiryTime]() {
|
||||
proxy->OnExpirationChange(sid, aExpiryTime);
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
bool keyStatusesChange = false;
|
||||
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
{
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
keyStatusesChange = caps.RemoveKeysForSession(NS_ConvertUTF8toUTF16(aSessionId));
|
||||
}
|
||||
if (keyStatusesChange) {
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction(
|
||||
"GMPCDMCallbackProxy::SessionClosed",
|
||||
[proxy, sid]() { proxy->OnKeyStatusesChange(sid); }),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::SessionClosed",
|
||||
[proxy, sid]() { proxy->OnSessionClosed(sid); }),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
auto msg = NS_ConvertUTF8toUTF16(aMessage);
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::SessionError",
|
||||
[proxy, sid, aException, aSystemCode, msg]() {
|
||||
proxy->OnSessionError(
|
||||
sid, aException, aSystemCode, msg);
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::BatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
BatchedKeyStatusChangedInternal(aSessionId, aKeyInfos);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::BatchedKeyStatusChangedInternal(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos)
|
||||
{
|
||||
bool keyStatusesChange = false;
|
||||
{
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
for (size_t i = 0; i < aKeyInfos.Length(); i++) {
|
||||
keyStatusesChange |=
|
||||
caps.SetKeyStatus(aKeyInfos[i].mKeyId,
|
||||
NS_ConvertUTF8toUTF16(aSessionId),
|
||||
aKeyInfos[i].mStatus);
|
||||
}
|
||||
}
|
||||
if (keyStatusesChange) {
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction(
|
||||
"GMPCDMCallbackProxy::BatchedKeyStatusChangedInternal",
|
||||
[proxy, sid]() { proxy->OnKeyStatusesChange(sid); }),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::Decrypted(uint32_t aId,
|
||||
DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
mProxy->OnDecrypted(aId, aResult, aDecryptedData);
|
||||
}
|
||||
|
||||
void
|
||||
GMPCDMCallbackProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnOwnerThread());
|
||||
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
mMainThread->Dispatch(
|
||||
NS_NewRunnableFunction("GMPCDMCallbackProxy::Terminated",
|
||||
[proxy]() { proxy->Terminated(); }),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,74 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GMPCDMCallbackProxy_h_
|
||||
#define GMPCDMCallbackProxy_h_
|
||||
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Proxies call backs from the CDM on the GMP thread back to the MediaKeys
|
||||
// object on the main thread.
|
||||
class GMPCDMCallbackProxy : public GMPDecryptorProxyCallback {
|
||||
public:
|
||||
|
||||
void SetDecryptorId(uint32_t aId) override;
|
||||
|
||||
void SetSessionId(uint32_t aCreateSessionToken,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
void ResolveLoadSessionPromise(uint32_t aPromiseId,
|
||||
bool aSuccess) override;
|
||||
|
||||
void ResolvePromise(uint32_t aPromiseId) override;
|
||||
|
||||
void RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
void SessionMessage(const nsCString& aSessionId,
|
||||
dom::MediaKeyMessageType aMessageType,
|
||||
const nsTArray<uint8_t>& aMessage) override;
|
||||
|
||||
void ExpirationChange(const nsCString& aSessionId,
|
||||
UnixTime aExpiryTime) override;
|
||||
|
||||
void SessionClosed(const nsCString& aSessionId) override;
|
||||
|
||||
void SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) override;
|
||||
|
||||
void Decrypted(uint32_t aId,
|
||||
DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) override;
|
||||
|
||||
void BatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos) override;
|
||||
|
||||
void Terminated() override;
|
||||
|
||||
~GMPCDMCallbackProxy() {}
|
||||
|
||||
private:
|
||||
friend class GMPCDMProxy;
|
||||
GMPCDMCallbackProxy(CDMProxy* aProxy, nsIEventTarget* aMainThread);
|
||||
|
||||
void BatchedKeyStatusChangedInternal(const nsCString& aSessionId,
|
||||
const nsTArray<CDMKeyInfo>& aKeyInfos);
|
||||
// Warning: Weak ref.
|
||||
CDMProxy* mProxy;
|
||||
|
||||
const nsCOMPtr<nsIEventTarget> mMainThread;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPCDMCallbackProxy_h_
|
|
@ -9,7 +9,6 @@
|
|||
#include "GMPLoader.h"
|
||||
#include "GMPVideoDecoderChild.h"
|
||||
#include "GMPVideoEncoderChild.h"
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPVideoHost.h"
|
||||
#include "nsDebugImpl.h"
|
||||
#include "nsIFile.h"
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPChild.h"
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPVideoDecoderChild.h"
|
||||
#include "GMPVideoEncoderChild.h"
|
||||
#include "ChromiumCDMChild.h"
|
||||
|
@ -50,21 +49,6 @@ GMPContentChild::ProcessingError(Result aCode, const char* aReason)
|
|||
mGMPChild->ProcessingError(aCode, aReason);
|
||||
}
|
||||
|
||||
PGMPDecryptorChild*
|
||||
GMPContentChild::AllocPGMPDecryptorChild()
|
||||
{
|
||||
GMPDecryptorChild* actor = new GMPDecryptorChild(this);
|
||||
actor->AddRef();
|
||||
return actor;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPContentChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
|
||||
{
|
||||
static_cast<GMPDecryptorChild*>(aActor)->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
PGMPVideoDecoderChild*
|
||||
GMPContentChild::AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId)
|
||||
{
|
||||
|
@ -110,22 +94,6 @@ GMPContentChild::DeallocPChromiumCDMChild(PChromiumCDMChild* aActor)
|
|||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
|
||||
{
|
||||
GMPDecryptorChild* child = static_cast<GMPDecryptorChild*>(aActor);
|
||||
|
||||
void* ptr = nullptr;
|
||||
GMPErr err = mGMPChild->GetAPI(GMP_API_DECRYPTOR, nullptr, &ptr, child->DecryptorId());
|
||||
if (err != GMPNoErr || !ptr) {
|
||||
NS_WARNING("GMPGetAPI call failed trying to construct decryptor.");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
child->Init(static_cast<GMPDecryptor*>(ptr));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPContentChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor,
|
||||
const uint32_t& aDecryptorId)
|
||||
|
@ -183,12 +151,6 @@ void
|
|||
GMPContentChild::CloseActive()
|
||||
{
|
||||
// Invalidate and remove any remaining API objects.
|
||||
const ManagedContainer<PGMPDecryptorChild>& decryptors =
|
||||
ManagedPGMPDecryptorChild();
|
||||
for (auto iter = decryptors.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
iter.Get()->GetKey()->SendShutdown();
|
||||
}
|
||||
|
||||
const ManagedContainer<PGMPVideoDecoderChild>& videoDecoders =
|
||||
ManagedPGMPVideoDecoderChild();
|
||||
for (auto iter = videoDecoders.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
|
@ -210,8 +172,7 @@ GMPContentChild::CloseActive()
|
|||
bool
|
||||
GMPContentChild::IsUsed()
|
||||
{
|
||||
return !ManagedPGMPDecryptorChild().IsEmpty() ||
|
||||
!ManagedPGMPVideoDecoderChild().IsEmpty() ||
|
||||
return !ManagedPGMPVideoDecoderChild().IsEmpty() ||
|
||||
!ManagedPGMPVideoEncoderChild().IsEmpty() ||
|
||||
!ManagedPChromiumCDMChild().IsEmpty();
|
||||
}
|
||||
|
|
|
@ -23,15 +23,11 @@ public:
|
|||
|
||||
MessageLoop* GMPMessageLoop();
|
||||
|
||||
mozilla::ipc::IPCResult RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) override;
|
||||
mozilla::ipc::IPCResult RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor, const uint32_t& aDecryptorId) override;
|
||||
mozilla::ipc::IPCResult RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) override;
|
||||
mozilla::ipc::IPCResult RecvPChromiumCDMConstructor(
|
||||
PChromiumCDMChild* aActor) override;
|
||||
|
||||
PGMPDecryptorChild* AllocPGMPDecryptorChild() override;
|
||||
bool DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor) override;
|
||||
|
||||
PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild(const uint32_t& aDecryptorId) override;
|
||||
bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) override;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPContentParent.h"
|
||||
#include "GMPDecryptorParent.h"
|
||||
#include "GMPParent.h"
|
||||
#include "GMPServiceChild.h"
|
||||
#include "GMPVideoDecoderParent.h"
|
||||
|
@ -67,8 +66,9 @@ private:
|
|||
void
|
||||
GMPContentParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
MOZ_ASSERT(mDecryptors.IsEmpty() && mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() && mChromiumCDMs.IsEmpty());
|
||||
MOZ_ASSERT(mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() &&
|
||||
mChromiumCDMs.IsEmpty());
|
||||
NS_DispatchToCurrentThread(new ReleaseGMPContentParent(this));
|
||||
}
|
||||
|
||||
|
@ -107,15 +107,6 @@ GMPContentParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
|
|||
CloseIfUnused();
|
||||
}
|
||||
|
||||
void
|
||||
GMPContentParent::DecryptorDestroyed(GMPDecryptorParent* aSession)
|
||||
{
|
||||
MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
|
||||
|
||||
MOZ_ALWAYS_TRUE(mDecryptors.RemoveElement(aSession));
|
||||
CloseIfUnused();
|
||||
}
|
||||
|
||||
void
|
||||
GMPContentParent::AddCloseBlocker()
|
||||
{
|
||||
|
@ -134,8 +125,9 @@ GMPContentParent::RemoveCloseBlocker()
|
|||
void
|
||||
GMPContentParent::CloseIfUnused()
|
||||
{
|
||||
if (mDecryptors.IsEmpty() && mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() && mChromiumCDMs.IsEmpty() &&
|
||||
if (mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() &&
|
||||
mChromiumCDMs.IsEmpty() &&
|
||||
mCloseBlockerCount == 0) {
|
||||
RefPtr<GMPContentParent> toClose;
|
||||
if (mParent) {
|
||||
|
@ -151,23 +143,6 @@ GMPContentParent::CloseIfUnused()
|
|||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPContentParent::GetGMPDecryptor(GMPDecryptorParent** aGMPDP)
|
||||
{
|
||||
PGMPDecryptorParent* pdp = SendPGMPDecryptorConstructor();
|
||||
if (!pdp) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
GMPDecryptorParent* dp = static_cast<GMPDecryptorParent*>(pdp);
|
||||
// This addref corresponds to the Proxy pointer the consumer is returned.
|
||||
// It's dropped by calling Close() on the interface.
|
||||
NS_ADDREF(dp);
|
||||
mDecryptors.AppendElement(dp);
|
||||
*aGMPDP = dp;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget>
|
||||
GMPContentParent::GMPEventTarget()
|
||||
{
|
||||
|
@ -292,21 +267,5 @@ GMPContentParent::DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor)
|
|||
return true;
|
||||
}
|
||||
|
||||
PGMPDecryptorParent*
|
||||
GMPContentParent::AllocPGMPDecryptorParent()
|
||||
{
|
||||
GMPDecryptorParent* ksp = new GMPDecryptorParent(this);
|
||||
NS_ADDREF(ksp);
|
||||
return ksp;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPContentParent::DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor)
|
||||
{
|
||||
GMPDecryptorParent* ksp = static_cast<GMPDecryptorParent*>(aActor);
|
||||
NS_RELEASE(ksp);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
class GMPDecryptorParent;
|
||||
class GMPParent;
|
||||
class GMPVideoDecoderParent;
|
||||
class GMPVideoEncoderParent;
|
||||
|
@ -34,9 +33,6 @@ public:
|
|||
nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
|
||||
void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
|
||||
|
||||
nsresult GetGMPDecryptor(GMPDecryptorParent** aGMPKS);
|
||||
void DecryptorDestroyed(GMPDecryptorParent* aSession);
|
||||
|
||||
already_AddRefed<ChromiumCDMParent> GetChromiumCDM();
|
||||
void ChromiumCDMDestroyed(ChromiumCDMParent* aCDM);
|
||||
|
||||
|
@ -93,9 +89,6 @@ private:
|
|||
PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() override;
|
||||
bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) override;
|
||||
|
||||
PGMPDecryptorParent* AllocPGMPDecryptorParent() override;
|
||||
bool DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor) override;
|
||||
|
||||
PChromiumCDMParent* AllocPChromiumCDMParent() override;
|
||||
bool DeallocPChromiumCDMParent(PChromiumCDMParent* aActor) override;
|
||||
|
||||
|
@ -109,7 +102,6 @@ private:
|
|||
|
||||
nsTArray<RefPtr<GMPVideoDecoderParent>> mVideoDecoders;
|
||||
nsTArray<RefPtr<GMPVideoEncoderParent>> mVideoEncoders;
|
||||
nsTArray<RefPtr<GMPDecryptorParent>> mDecryptors;
|
||||
nsTArray<RefPtr<ChromiumCDMParent>> mChromiumCDMs;
|
||||
nsCOMPtr<nsISerialEventTarget> mGMPEventTarget;
|
||||
RefPtr<GMPParent> mParent;
|
||||
|
|
|
@ -1,382 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPChild.h"
|
||||
#include "base/task.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "runnable_utils.h"
|
||||
#include <ctime>
|
||||
|
||||
#define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current())
|
||||
|
||||
#define CALL_ON_GMP_THREAD(_func, ...) \
|
||||
CallOnGMPThread(&GMPDecryptorChild::_func, __VA_ARGS__)
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
static uint32_t sDecryptorCount = 1;
|
||||
|
||||
GMPDecryptorChild::GMPDecryptorChild(GMPContentChild* aPlugin)
|
||||
: mSession(nullptr)
|
||||
, mPlugin(aPlugin)
|
||||
, mDecryptorId(sDecryptorCount++)
|
||||
{
|
||||
MOZ_ASSERT(mPlugin);
|
||||
}
|
||||
|
||||
GMPDecryptorChild::~GMPDecryptorChild()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename MethodType, typename... ParamType>
|
||||
void
|
||||
GMPDecryptorChild::CallMethod(MethodType aMethod, ParamType&&... aParams)
|
||||
{
|
||||
MOZ_ASSERT(ON_GMP_THREAD());
|
||||
// Don't send IPC messages after tear-down.
|
||||
if (mSession) {
|
||||
(this->*aMethod)(Forward<ParamType>(aParams)...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct AddConstReference {
|
||||
typedef const typename RemoveReference<T>::Type& Type;
|
||||
};
|
||||
|
||||
template<typename MethodType, typename... ParamType>
|
||||
void
|
||||
GMPDecryptorChild::CallOnGMPThread(MethodType aMethod, ParamType&&... aParams)
|
||||
{
|
||||
if (ON_GMP_THREAD()) {
|
||||
// Use forwarding reference when we can.
|
||||
CallMethod(aMethod, Forward<ParamType>(aParams)...);
|
||||
} else {
|
||||
// Use const reference when we have to.
|
||||
auto m = &GMPDecryptorChild::CallMethod<
|
||||
decltype(aMethod), typename AddConstReference<ParamType>::Type...>;
|
||||
RefPtr<mozilla::Runnable> t =
|
||||
dont_add_new_uses_of_this::NewRunnableMethod(this, m, aMethod, Forward<ParamType>(aParams)...);
|
||||
mPlugin->GMPMessageLoop()->PostTask(t.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::Init(GMPDecryptor* aSession)
|
||||
{
|
||||
MOZ_ASSERT(aSession);
|
||||
mSession = aSession;
|
||||
// The ID of this decryptor is the IPDL actor ID. Note it's unique inside
|
||||
// the child process, but not necessarily across all gecko processes. However,
|
||||
// since GMPDecryptors are segregated by node ID/origin, we shouldn't end up
|
||||
// with clashes in the content process.
|
||||
SendSetDecryptorId(DecryptorId());
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendSetSessionId,
|
||||
aCreateSessionToken, nsCString(aSessionId, aSessionIdLength));
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::ResolveLoadSessionPromise(uint32_t aPromiseId,
|
||||
bool aSuccess)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendResolveLoadSessionPromise, aPromiseId, aSuccess);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::ResolvePromise(uint32_t aPromiseId)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendResolvePromise, aPromiseId);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::RejectPromise(uint32_t aPromiseId,
|
||||
GMPDOMException aException,
|
||||
const char* aMessage,
|
||||
uint32_t aMessageLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendRejectPromise,
|
||||
aPromiseId, aException, nsCString(aMessage, aMessageLength));
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::SessionMessage(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
GMPSessionMessageType aMessageType,
|
||||
const uint8_t* aMessage,
|
||||
uint32_t aMessageLength)
|
||||
{
|
||||
nsTArray<uint8_t> msg;
|
||||
msg.AppendElements(aMessage, aMessageLength);
|
||||
CALL_ON_GMP_THREAD(SendSessionMessage,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
aMessageType, Move(msg));
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::ExpirationChange(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendExpirationChange,
|
||||
nsCString(aSessionId, aSessionIdLength), aExpiryTime);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::SessionClosed(const char* aSessionId,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendSessionClosed,
|
||||
nsCString(aSessionId, aSessionIdLength));
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::SessionError(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
GMPDOMException aException,
|
||||
uint32_t aSystemCode,
|
||||
const char* aMessage,
|
||||
uint32_t aMessageLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendSessionError,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
aException, aSystemCode,
|
||||
nsCString(aMessage, aMessageLength));
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::KeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aKeyId,
|
||||
uint32_t aKeyIdLength,
|
||||
GMPMediaKeyStatus aStatus)
|
||||
{
|
||||
AutoTArray<uint8_t, 16> kid;
|
||||
kid.AppendElements(aKeyId, aKeyIdLength);
|
||||
|
||||
nsTArray<GMPKeyInformation> keyInfos;
|
||||
keyInfos.AppendElement(GMPKeyInformation(kid, aStatus));
|
||||
CALL_ON_GMP_THREAD(SendBatchedKeyStatusChanged,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
keyInfos);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::BatchedKeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const GMPMediaKeyInfo* aKeyInfos,
|
||||
uint32_t aKeyInfosLength)
|
||||
{
|
||||
nsTArray<GMPKeyInformation> keyInfos;
|
||||
for (uint32_t i = 0; i < aKeyInfosLength; i++) {
|
||||
nsTArray<uint8_t> keyId;
|
||||
keyId.AppendElements(aKeyInfos[i].keyid, aKeyInfos[i].keyid_size);
|
||||
keyInfos.AppendElement(GMPKeyInformation(keyId, aKeyInfos[i].status));
|
||||
}
|
||||
CALL_ON_GMP_THREAD(SendBatchedKeyStatusChanged,
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
keyInfos);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::Decrypted(GMPBuffer* aBuffer, GMPErr aResult)
|
||||
{
|
||||
if (!ON_GMP_THREAD()) {
|
||||
// We should run this whole method on the GMP thread since the buffer needs
|
||||
// to be deleted after the SendDecrypted call.
|
||||
mPlugin->GMPMessageLoop()->PostTask(
|
||||
NewRunnableMethod<GMPBuffer*, GMPErr>("gmp::GMPDecryptorChild::Decrypted",
|
||||
this,
|
||||
&GMPDecryptorChild::Decrypted,
|
||||
aBuffer,
|
||||
aResult));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aBuffer) {
|
||||
NS_WARNING("GMPDecryptorCallback passed bull GMPBuffer");
|
||||
return;
|
||||
}
|
||||
|
||||
auto buffer = static_cast<GMPBufferImpl*>(aBuffer);
|
||||
if (mSession) {
|
||||
SendDecrypted(buffer->mId, aResult, buffer->mData);
|
||||
}
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorChild::SetCapabilities(uint64_t aCaps)
|
||||
{
|
||||
// Deprecated.
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvInit(const bool& aDistinctiveIdentifierRequired,
|
||||
const bool& aPersistentStateRequired)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mSession->Init(this, aDistinctiveIdentifierRequired, aPersistentStateRequired);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvCreateSession(const uint32_t& aCreateSessionToken,
|
||||
const uint32_t& aPromiseId,
|
||||
const nsCString& aInitDataType,
|
||||
InfallibleTArray<uint8_t>&& aInitData,
|
||||
const GMPSessionType& aSessionType)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
mSession->CreateSession(aCreateSessionToken,
|
||||
aPromiseId,
|
||||
aInitDataType.get(),
|
||||
aInitDataType.Length(),
|
||||
aInitData.Elements(),
|
||||
aInitData.Length(),
|
||||
aSessionType);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvLoadSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
mSession->LoadSession(aPromiseId,
|
||||
aSessionId.get(),
|
||||
aSessionId.Length());
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvUpdateSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
InfallibleTArray<uint8_t>&& aResponse)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
mSession->UpdateSession(aPromiseId,
|
||||
aSessionId.get(),
|
||||
aSessionId.Length(),
|
||||
aResponse.Elements(),
|
||||
aResponse.Length());
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvCloseSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
mSession->CloseSession(aPromiseId,
|
||||
aSessionId.get(),
|
||||
aSessionId.Length());
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvRemoveSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
mSession->RemoveSession(aPromiseId,
|
||||
aSessionId.get(),
|
||||
aSessionId.Length());
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvSetServerCertificate(const uint32_t& aPromiseId,
|
||||
InfallibleTArray<uint8_t>&& aServerCert)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
mSession->SetServerCertificate(aPromiseId,
|
||||
aServerCert.Elements(),
|
||||
aServerCert.Length());
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvDecrypt(const uint32_t& aId,
|
||||
InfallibleTArray<uint8_t>&& aBuffer,
|
||||
const GMPDecryptionData& aMetadata)
|
||||
{
|
||||
if (!mSession) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
// Note: the GMPBufferImpl created here is deleted when the GMP passes
|
||||
// it back in the Decrypted() callback above.
|
||||
GMPBufferImpl* buffer = new GMPBufferImpl(aId, aBuffer);
|
||||
|
||||
// |metadata| lifetime is managed by |buffer|.
|
||||
GMPEncryptedBufferDataImpl* metadata = new GMPEncryptedBufferDataImpl(aMetadata);
|
||||
buffer->SetMetadata(metadata);
|
||||
|
||||
mSession->Decrypt(buffer, metadata);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorChild::RecvDecryptingComplete()
|
||||
{
|
||||
// Reset |mSession| before calling DecryptingComplete(). We should not send
|
||||
// any IPC messages during tear-down.
|
||||
auto session = mSession;
|
||||
mSession = nullptr;
|
||||
|
||||
if (!session) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
session->DecryptingComplete();
|
||||
|
||||
Unused << Send__delete__(this);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef ON_GMP_THREAD
|
||||
#undef CALL_ON_GMP_THREAD
|
|
@ -1,132 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GMPDecryptorChild_h_
|
||||
#define GMPDecryptorChild_h_
|
||||
|
||||
#include "mozilla/gmp/PGMPDecryptorChild.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
#include "GMPEncryptedBufferDataImpl.h"
|
||||
#include <string>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
class GMPContentChild;
|
||||
|
||||
class GMPDecryptorChild : public GMPDecryptorCallback
|
||||
, public PGMPDecryptorChild
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPDecryptorChild);
|
||||
|
||||
explicit GMPDecryptorChild(GMPContentChild* aPlugin);
|
||||
|
||||
void Init(GMPDecryptor* aSession);
|
||||
|
||||
// GMPDecryptorCallback
|
||||
void SetSessionId(uint32_t aCreateSessionToken,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override;
|
||||
void ResolveLoadSessionPromise(uint32_t aPromiseId,
|
||||
bool aSuccess) override;
|
||||
void ResolvePromise(uint32_t aPromiseId) override;
|
||||
|
||||
void RejectPromise(uint32_t aPromiseId,
|
||||
GMPDOMException aException,
|
||||
const char* aMessage,
|
||||
uint32_t aMessageLength) override;
|
||||
|
||||
void SessionMessage(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
GMPSessionMessageType aMessageType,
|
||||
const uint8_t* aMessage,
|
||||
uint32_t aMessageLength) override;
|
||||
|
||||
void ExpirationChange(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
GMPTimestamp aExpiryTime) override;
|
||||
|
||||
void SessionClosed(const char* aSessionId,
|
||||
uint32_t aSessionIdLength) override;
|
||||
|
||||
void SessionError(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
GMPDOMException aException,
|
||||
uint32_t aSystemCode,
|
||||
const char* aMessage,
|
||||
uint32_t aMessageLength) override;
|
||||
|
||||
void KeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const uint8_t* aKeyId,
|
||||
uint32_t aKeyIdLength,
|
||||
GMPMediaKeyStatus aStatus) override;
|
||||
|
||||
void SetCapabilities(uint64_t aCaps) override;
|
||||
|
||||
void Decrypted(GMPBuffer* aBuffer, GMPErr aResult) override;
|
||||
|
||||
void BatchedKeyStatusChanged(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
const GMPMediaKeyInfo* aKeyInfos,
|
||||
uint32_t aKeyInfosLength) override;
|
||||
|
||||
uint32_t DecryptorId() const { return mDecryptorId; }
|
||||
private:
|
||||
~GMPDecryptorChild();
|
||||
|
||||
// GMPDecryptorChild
|
||||
mozilla::ipc::IPCResult RecvInit(const bool& aDistinctiveIdentifierRequired,
|
||||
const bool& aPersistentStateRequired) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvCreateSession(const uint32_t& aCreateSessionToken,
|
||||
const uint32_t& aPromiseId,
|
||||
const nsCString& aInitDataType,
|
||||
InfallibleTArray<uint8_t>&& aInitData,
|
||||
const GMPSessionType& aSessionType) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvLoadSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvUpdateSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
InfallibleTArray<uint8_t>&& aResponse) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvCloseSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvRemoveSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvDecrypt(const uint32_t& aId,
|
||||
InfallibleTArray<uint8_t>&& aBuffer,
|
||||
const GMPDecryptionData& aMetadata) override;
|
||||
|
||||
// Resolve/reject promise on completion.
|
||||
mozilla::ipc::IPCResult RecvSetServerCertificate(const uint32_t& aPromiseId,
|
||||
InfallibleTArray<uint8_t>&& aServerCert) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvDecryptingComplete() override;
|
||||
|
||||
template <typename MethodType, typename... ParamType>
|
||||
void CallMethod(MethodType, ParamType&&...);
|
||||
|
||||
template<typename MethodType, typename... ParamType>
|
||||
void CallOnGMPThread(MethodType, ParamType&&...);
|
||||
|
||||
// GMP's GMPDecryptor implementation.
|
||||
// Only call into this on the (GMP process) main thread.
|
||||
GMPDecryptor* mSession;
|
||||
GMPContentChild* mPlugin;
|
||||
|
||||
const uint32_t mDecryptorId;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPDecryptorChild_h_
|
|
@ -1,509 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPDecryptorParent.h"
|
||||
#include "GMPContentParent.h"
|
||||
#include "GMPUtils.h"
|
||||
#include "MediaData.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef LOG
|
||||
#undef LOG
|
||||
#endif
|
||||
|
||||
extern LogModule* GetGMPLog();
|
||||
|
||||
#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
|
||||
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
|
||||
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
|
||||
|
||||
namespace gmp {
|
||||
|
||||
GMPDecryptorParent::GMPDecryptorParent(GMPContentParent* aPlugin)
|
||||
: mIsOpen(false)
|
||||
, mShuttingDown(false)
|
||||
, mActorDestroyed(false)
|
||||
, mPlugin(aPlugin)
|
||||
, mPluginId(aPlugin->GetPluginId())
|
||||
, mCallback(nullptr)
|
||||
#ifdef DEBUG
|
||||
, mGMPEventTarget(aPlugin->GMPEventTarget())
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(mPlugin && mGMPEventTarget);
|
||||
}
|
||||
|
||||
GMPDecryptorParent::~GMPDecryptorParent()
|
||||
{
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvSetDecryptorId(const uint32_t& aId)
|
||||
{
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->SetDecryptorId(aId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback,
|
||||
bool aDistinctiveIdentifierRequired,
|
||||
bool aPersistentStateRequired)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::Init()", this));
|
||||
|
||||
if (mIsOpen) {
|
||||
NS_WARNING("Trying to re-use an in-use GMP decrypter!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mCallback = aCallback;
|
||||
if (!SendInit(aDistinctiveIdentifierRequired, aPersistentStateRequired)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mIsOpen = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const nsCString& aInitDataType,
|
||||
const nsTArray<uint8_t>& aInitData,
|
||||
GMPSessionType aSessionType)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::CreateSession(token=%u, promiseId=%u, aInitData='%s')",
|
||||
this, aCreateSessionToken, aPromiseId, ToHexString(aInitData).get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
// Caller should ensure parameters passed in from JS are valid.
|
||||
MOZ_ASSERT(!aInitDataType.IsEmpty() && !aInitData.IsEmpty());
|
||||
Unused << SendCreateSession(aCreateSessionToken, aPromiseId, aInitDataType, aInitData, aSessionType);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::LoadSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::LoadSession(sessionId='%s', promiseId=%u)",
|
||||
this, aSessionId.get(), aPromiseId));
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
// Caller should ensure parameters passed in from JS are valid.
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
Unused << SendLoadSession(aPromiseId, aSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::UpdateSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aResponse)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::UpdateSession(sessionId='%s', promiseId=%u response='%s')",
|
||||
this, aSessionId.get(), aPromiseId, ToHexString(aResponse).get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
// Caller should ensure parameters passed in from JS are valid.
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty() && !aResponse.IsEmpty());
|
||||
Unused << SendUpdateSession(aPromiseId, aSessionId, aResponse);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::CloseSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::CloseSession(sessionId='%s', promiseId=%u)",
|
||||
this, aSessionId.get(), aPromiseId));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
// Caller should ensure parameters passed in from JS are valid.
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
Unused << SendCloseSession(aPromiseId, aSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::RemoveSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RemoveSession(sessionId='%s', promiseId=%u)",
|
||||
this, aSessionId.get(), aPromiseId));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
// Caller should ensure parameters passed in from JS are valid.
|
||||
MOZ_ASSERT(!aSessionId.IsEmpty());
|
||||
Unused << SendRemoveSession(aPromiseId, aSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::SetServerCertificate(uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aServerCert)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::SetServerCertificate(promiseId=%u)",
|
||||
this, aPromiseId));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
// Caller should ensure parameters passed in from JS are valid.
|
||||
MOZ_ASSERT(!aServerCert.IsEmpty());
|
||||
Unused << SendSetServerCertificate(aPromiseId, aServerCert);
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::Decrypt(uint32_t aId,
|
||||
const CryptoSample& aCrypto,
|
||||
const nsTArray<uint8_t>& aBuffer)
|
||||
{
|
||||
LOGV(("GMPDecryptorParent[%p]::Decrypt(id=%d)", this, aId));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Caller should ensure parameters passed in are valid.
|
||||
MOZ_ASSERT(!aBuffer.IsEmpty());
|
||||
|
||||
if (aCrypto.mValid) {
|
||||
GMPDecryptionData data(aCrypto.mKeyId,
|
||||
aCrypto.mIV,
|
||||
aCrypto.mPlainSizes,
|
||||
aCrypto.mEncryptedSizes,
|
||||
aCrypto.mSessionIds);
|
||||
|
||||
Unused << SendDecrypt(aId, aBuffer, data);
|
||||
} else {
|
||||
GMPDecryptionData data;
|
||||
Unused << SendDecrypt(aId, aBuffer, data);
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvSetSessionId(token=%u, sessionId='%s')",
|
||||
this, aCreateSessionId, aSessionId.get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->SetSessionId(aCreateSessionId, aSessionId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
|
||||
const bool& aSuccess)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvResolveLoadSessionPromise(promiseId=%u)",
|
||||
this, aPromiseId));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->ResolveLoadSessionPromise(aPromiseId, aSuccess);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvResolvePromise(const uint32_t& aPromiseId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvResolvePromise(promiseId=%u)",
|
||||
this, aPromiseId));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->ResolvePromise(aPromiseId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPExToNsresult(GMPDOMException aDomException) {
|
||||
switch (aDomException) {
|
||||
case kGMPNoModificationAllowedError: return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
case kGMPNotFoundError: return NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
case kGMPNotSupportedError: return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
case kGMPInvalidStateError: return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
case kGMPSyntaxError: return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
case kGMPInvalidModificationError: return NS_ERROR_DOM_INVALID_MODIFICATION_ERR;
|
||||
case kGMPInvalidAccessError: return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
case kGMPSecurityError: return NS_ERROR_DOM_SECURITY_ERR;
|
||||
case kGMPAbortError: return NS_ERROR_DOM_ABORT_ERR;
|
||||
case kGMPQuotaExceededError: return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
|
||||
case kGMPTimeoutError: return NS_ERROR_DOM_TIMEOUT_ERR;
|
||||
case kGMPTypeError: return NS_ERROR_DOM_TYPE_ERR;
|
||||
default: return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId,
|
||||
const GMPDOMException& aException,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvRejectPromise(promiseId=%u, exception=%d, msg='%s')",
|
||||
this, aPromiseId, aException, aMessage.get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->RejectPromise(aPromiseId, GMPExToNsresult(aException), aMessage);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
||||
static dom::MediaKeyMessageType
|
||||
ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
|
||||
switch (aMessageType) {
|
||||
case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
|
||||
case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
|
||||
case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
|
||||
case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request;
|
||||
default: return dom::MediaKeyMessageType::License_request;
|
||||
};
|
||||
};
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvSessionMessage(const nsCString& aSessionId,
|
||||
const GMPSessionMessageType& aMessageType,
|
||||
nsTArray<uint8_t>&& aMessage)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvSessionMessage(sessionId='%s', type=%d, msg='%s')",
|
||||
this, aSessionId.get(), aMessageType, ToHexString(aMessage).get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->SessionMessage(aSessionId, ToMediaKeyMessageType(aMessageType), aMessage);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId,
|
||||
const double& aExpiryTime)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvExpirationChange(sessionId='%s', expiry=%lf)",
|
||||
this, aSessionId.get(), aExpiryTime));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->ExpirationChange(aSessionId, aExpiryTime);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvSessionClosed(const nsCString& aSessionId)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvSessionClosed(sessionId='%s')",
|
||||
this, aSessionId.get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->SessionClosed(aSessionId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvSessionError(const nsCString& aSessionId,
|
||||
const GMPDOMException& aException,
|
||||
const uint32_t& aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvSessionError(sessionId='%s', exception=%d, sysCode=%d, msg='%s')",
|
||||
this, aSessionId.get(),
|
||||
aException, aSystemCode, aMessage.get()));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->SessionError(aSessionId,
|
||||
GMPExToNsresult(aException),
|
||||
aSystemCode,
|
||||
aMessage);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static dom::MediaKeyStatus
|
||||
ToMediaKeyStatus(GMPMediaKeyStatus aStatus) {
|
||||
switch (aStatus) {
|
||||
case kGMPUsable: return dom::MediaKeyStatus::Usable;
|
||||
case kGMPExpired: return dom::MediaKeyStatus::Expired;
|
||||
case kGMPOutputDownscaled: return dom::MediaKeyStatus::Output_downscaled;
|
||||
case kGMPOutputRestricted: return dom::MediaKeyStatus::Output_restricted;
|
||||
case kGMPInternalError: return dom::MediaKeyStatus::Internal_error;
|
||||
case kGMPReleased: return dom::MediaKeyStatus::Released;
|
||||
case kGMPStatusPending: return dom::MediaKeyStatus::Status_pending;
|
||||
default: return dom::MediaKeyStatus::Internal_error;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvBatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
InfallibleTArray<GMPKeyInformation>&& aKeyInfos)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvBatchedKeyStatusChanged(sessionId='%s', KeyInfos len='%zu')",
|
||||
this, aSessionId.get(), aKeyInfos.Length()));
|
||||
|
||||
if (mIsOpen) {
|
||||
nsTArray<CDMKeyInfo> cdmKeyInfos(aKeyInfos.Length());
|
||||
for (uint32_t i = 0; i < aKeyInfos.Length(); i++) {
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvBatchedKeyStatusChanged(keyId=%s, gmp-status=%d)",
|
||||
this, ToHexString(aKeyInfos[i].keyId()).get(), aKeyInfos[i].status()));
|
||||
// If the status is kGMPUnknown, we're going to forget(remove) that key info.
|
||||
if (aKeyInfos[i].status() != kGMPUnknown) {
|
||||
auto status = ToMediaKeyStatus(aKeyInfos[i].status());
|
||||
cdmKeyInfos.AppendElement(CDMKeyInfo(aKeyInfos[i].keyId(),
|
||||
dom::Optional<dom::MediaKeyStatus>(status)));
|
||||
} else {
|
||||
cdmKeyInfos.AppendElement(CDMKeyInfo(aKeyInfos[i].keyId()));
|
||||
}
|
||||
}
|
||||
mCallback->BatchedKeyStatusChanged(aSessionId, cdmKeyInfos);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
DecryptStatus
|
||||
ToDecryptStatus(GMPErr aError)
|
||||
{
|
||||
switch (aError) {
|
||||
case GMPNoErr: return eme::Ok;
|
||||
case GMPNoKeyErr: return eme::NoKeyErr;
|
||||
case GMPAbortedErr: return eme::AbortedErr;
|
||||
default: return eme::GenericErr;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvDecrypted(const uint32_t& aId,
|
||||
const GMPErr& aErr,
|
||||
InfallibleTArray<uint8_t>&& aBuffer)
|
||||
{
|
||||
LOGV(("GMPDecryptorParent[%p]::RecvDecrypted(id=%d, err=%d)",
|
||||
this, aId, aErr));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use a dead GMP decrypter!");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
mCallback->Decrypted(aId, ToDecryptStatus(aErr), aBuffer);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::RecvShutdown()
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::RecvShutdown()", this));
|
||||
|
||||
Shutdown();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Note: may be called via Terminated()
|
||||
void
|
||||
GMPDecryptorParent::Close()
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::Close()", this));
|
||||
MOZ_ASSERT(mGMPEventTarget->IsOnCurrentThread());
|
||||
|
||||
// Consumer is done with us; we can shut down. No more callbacks should
|
||||
// be made to mCallback. Note: do this before Shutdown()!
|
||||
mCallback = nullptr;
|
||||
// Let Shutdown mark us as dead so it knows if we had been alive
|
||||
|
||||
// In case this is the last reference
|
||||
RefPtr<GMPDecryptorParent> kungfudeathgrip(this);
|
||||
this->Release();
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
GMPDecryptorParent::Shutdown()
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::Shutdown()", this));
|
||||
MOZ_ASSERT(mGMPEventTarget->IsOnCurrentThread());
|
||||
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
mShuttingDown = true;
|
||||
|
||||
// Notify client we're gone! Won't occur after Close()
|
||||
if (mCallback) {
|
||||
mCallback->Terminated();
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
mIsOpen = false;
|
||||
if (!mActorDestroyed) {
|
||||
Unused << SendDecryptingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Keep this sync'd up with Shutdown
|
||||
void
|
||||
GMPDecryptorParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
|
||||
|
||||
mIsOpen = false;
|
||||
mActorDestroyed = true;
|
||||
if (mCallback) {
|
||||
// May call Close() (and Shutdown()) immediately or with a delay
|
||||
mCallback->Terminated();
|
||||
mCallback = nullptr;
|
||||
}
|
||||
if (mPlugin) {
|
||||
mPlugin->DecryptorDestroyed(this);
|
||||
mPlugin = nullptr;
|
||||
}
|
||||
MaybeDisconnect(aWhy == AbnormalShutdown);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPDecryptorParent::Recv__delete__()
|
||||
{
|
||||
LOGD(("GMPDecryptorParent[%p]::Recv__delete__()", this));
|
||||
|
||||
if (mPlugin) {
|
||||
mPlugin->DecryptorDestroyed(this);
|
||||
mPlugin = nullptr;
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
|
@ -1,129 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GMPDecryptorParent_h_
|
||||
#define GMPDecryptorParent_h_
|
||||
|
||||
#include "mozilla/gmp/PGMPDecryptorParent.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
#include "GMPCrashHelperHolder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CryptoSample;
|
||||
|
||||
namespace gmp {
|
||||
|
||||
class GMPContentParent;
|
||||
|
||||
class GMPDecryptorParent final : public GMPDecryptorProxy
|
||||
, public PGMPDecryptorParent
|
||||
, public GMPCrashHelperHolder
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
|
||||
|
||||
explicit GMPDecryptorParent(GMPContentParent *aPlugin);
|
||||
|
||||
// GMPDecryptorProxy
|
||||
|
||||
uint32_t GetPluginId() const override { return mPluginId; }
|
||||
|
||||
nsresult Init(GMPDecryptorProxyCallback* aCallback,
|
||||
bool aDistinctiveIdentifierRequired,
|
||||
bool aPersistentStateRequired) override;
|
||||
|
||||
void CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const nsCString& aInitDataType,
|
||||
const nsTArray<uint8_t>& aInitData,
|
||||
GMPSessionType aSessionType) override;
|
||||
|
||||
void LoadSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
void UpdateSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aResponse) override;
|
||||
|
||||
void CloseSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
void RemoveSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
void SetServerCertificate(uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aServerCert) override;
|
||||
|
||||
void Decrypt(uint32_t aId,
|
||||
const CryptoSample& aCrypto,
|
||||
const nsTArray<uint8_t>& aBuffer) override;
|
||||
|
||||
void Close() override;
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
~GMPDecryptorParent();
|
||||
|
||||
// PGMPDecryptorParent
|
||||
|
||||
mozilla::ipc::IPCResult RecvSetDecryptorId(const uint32_t& aId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSetSessionId(const uint32_t& aCreateSessionToken,
|
||||
const nsCString& aSessionId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
|
||||
const bool& aSuccess) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvResolvePromise(const uint32_t& aPromiseId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvRejectPromise(const uint32_t& aPromiseId,
|
||||
const GMPDOMException& aException,
|
||||
const nsCString& aMessage) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSessionMessage(const nsCString& aSessionId,
|
||||
const GMPSessionMessageType& aMessageType,
|
||||
nsTArray<uint8_t>&& aMessage) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvExpirationChange(const nsCString& aSessionId,
|
||||
const double& aExpiryTime) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSessionClosed(const nsCString& aSessionId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSessionError(const nsCString& aSessionId,
|
||||
const GMPDOMException& aException,
|
||||
const uint32_t& aSystemCode,
|
||||
const nsCString& aMessage) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvDecrypted(const uint32_t& aId,
|
||||
const GMPErr& aErr,
|
||||
InfallibleTArray<uint8_t>&& aBuffer) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvBatchedKeyStatusChanged(const nsCString& aSessionId,
|
||||
InfallibleTArray<GMPKeyInformation>&& aKeyInfos) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvShutdown() override;
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
mozilla::ipc::IPCResult Recv__delete__() override;
|
||||
|
||||
bool mIsOpen;
|
||||
bool mShuttingDown;
|
||||
bool mActorDestroyed;
|
||||
RefPtr<GMPContentParent> mPlugin;
|
||||
uint32_t mPluginId;
|
||||
GMPDecryptorProxyCallback* mCallback;
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsISerialEventTarget> const mGMPEventTarget;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPDecryptorChild_h_
|
|
@ -1,63 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GMPDecryptorProxy_h_
|
||||
#define GMPDecryptorProxy_h_
|
||||
|
||||
#include "mozilla/DecryptorProxyCallback.h"
|
||||
#include "GMPCallbackBase.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
class CryptoSample;
|
||||
} // namespace mozilla
|
||||
|
||||
class GMPDecryptorProxyCallback : public DecryptorProxyCallback,
|
||||
public GMPCallbackBase {
|
||||
public:
|
||||
virtual ~GMPDecryptorProxyCallback() {}
|
||||
};
|
||||
|
||||
class GMPDecryptorProxy {
|
||||
public:
|
||||
~GMPDecryptorProxy() {}
|
||||
|
||||
virtual uint32_t GetPluginId() const = 0;
|
||||
|
||||
virtual nsresult Init(GMPDecryptorProxyCallback* aCallback,
|
||||
bool aDistinctiveIdentifierRequired,
|
||||
bool aPersistentStateRequired) = 0;
|
||||
|
||||
virtual void CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aPromiseId,
|
||||
const nsCString& aInitDataType,
|
||||
const nsTArray<uint8_t>& aInitData,
|
||||
GMPSessionType aSessionType) = 0;
|
||||
|
||||
virtual void LoadSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) = 0;
|
||||
|
||||
virtual void UpdateSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aResponse) = 0;
|
||||
|
||||
virtual void CloseSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) = 0;
|
||||
|
||||
virtual void RemoveSession(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) = 0;
|
||||
|
||||
virtual void SetServerCertificate(uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aServerCert) = 0;
|
||||
|
||||
virtual void Decrypt(uint32_t aId,
|
||||
const mozilla::CryptoSample& aCrypto,
|
||||
const nsTArray<uint8_t>& aBuffer) = 0;
|
||||
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
#endif // GMPDecryptorProxy_h_
|
|
@ -1,132 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPEncryptedBufferDataImpl.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
#include "MediaData.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
GMPEncryptedBufferDataImpl::GMPEncryptedBufferDataImpl(const CryptoSample& aCrypto)
|
||||
: mKeyId(aCrypto.mKeyId)
|
||||
, mIV(aCrypto.mIV)
|
||||
, mClearBytes(aCrypto.mPlainSizes)
|
||||
, mCipherBytes(aCrypto.mEncryptedSizes)
|
||||
, mSessionIdList(aCrypto.mSessionIds)
|
||||
{
|
||||
}
|
||||
|
||||
GMPEncryptedBufferDataImpl::GMPEncryptedBufferDataImpl(const GMPDecryptionData& aData)
|
||||
: mKeyId(aData.mKeyId())
|
||||
, mIV(aData.mIV())
|
||||
, mClearBytes(aData.mClearBytes())
|
||||
, mCipherBytes(aData.mCipherBytes())
|
||||
, mSessionIdList(aData.mSessionIds())
|
||||
{
|
||||
MOZ_ASSERT(mClearBytes.Length() == mCipherBytes.Length());
|
||||
}
|
||||
|
||||
GMPEncryptedBufferDataImpl::~GMPEncryptedBufferDataImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GMPEncryptedBufferDataImpl::RelinquishData(GMPDecryptionData& aData)
|
||||
{
|
||||
aData.mKeyId() = Move(mKeyId);
|
||||
aData.mIV() = Move(mIV);
|
||||
aData.mClearBytes() = Move(mClearBytes);
|
||||
aData.mCipherBytes() = Move(mCipherBytes);
|
||||
mSessionIdList.RelinquishData(aData.mSessionIds());
|
||||
}
|
||||
|
||||
const uint8_t*
|
||||
GMPEncryptedBufferDataImpl::KeyId() const
|
||||
{
|
||||
return mKeyId.Elements();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GMPEncryptedBufferDataImpl::KeyIdSize() const
|
||||
{
|
||||
return mKeyId.Length();
|
||||
}
|
||||
|
||||
const uint8_t*
|
||||
GMPEncryptedBufferDataImpl::IV() const
|
||||
{
|
||||
return mIV.Elements();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GMPEncryptedBufferDataImpl::IVSize() const
|
||||
{
|
||||
return mIV.Length();
|
||||
}
|
||||
|
||||
const uint16_t*
|
||||
GMPEncryptedBufferDataImpl::ClearBytes() const
|
||||
{
|
||||
return mClearBytes.Elements();
|
||||
}
|
||||
|
||||
const uint32_t*
|
||||
GMPEncryptedBufferDataImpl::CipherBytes() const
|
||||
{
|
||||
return mCipherBytes.Elements();
|
||||
}
|
||||
|
||||
const GMPStringList*
|
||||
GMPEncryptedBufferDataImpl::SessionIds() const
|
||||
{
|
||||
return &mSessionIdList;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GMPEncryptedBufferDataImpl::NumSubsamples() const
|
||||
{
|
||||
MOZ_ASSERT(mClearBytes.Length() == mCipherBytes.Length());
|
||||
// Return the min of the two, to ensure there's not chance of array index
|
||||
// out-of-bounds shenanigans.
|
||||
return std::min<uint32_t>(mClearBytes.Length(), mCipherBytes.Length());
|
||||
}
|
||||
|
||||
GMPStringListImpl::GMPStringListImpl(const nsTArray<nsCString>& aStrings)
|
||||
: mStrings(aStrings)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GMPStringListImpl::Size() const
|
||||
{
|
||||
return mStrings.Length();
|
||||
}
|
||||
|
||||
void
|
||||
GMPStringListImpl::StringAt(uint32_t aIndex,
|
||||
const char** aOutString,
|
||||
uint32_t *aOutLength) const
|
||||
{
|
||||
if (NS_WARN_IF(aIndex >= Size())) {
|
||||
return;
|
||||
}
|
||||
|
||||
*aOutString = mStrings[aIndex].BeginReading();
|
||||
*aOutLength = mStrings[aIndex].Length();
|
||||
}
|
||||
|
||||
void
|
||||
GMPStringListImpl::RelinquishData(nsTArray<nsCString>& aStrings)
|
||||
{
|
||||
aStrings = Move(mStrings);
|
||||
}
|
||||
|
||||
GMPStringListImpl::~GMPStringListImpl()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
|
@ -1,92 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GMPEncryptedBufferDataImpl_h_
|
||||
#define GMPEncryptedBufferDataImpl_h_
|
||||
|
||||
#include "gmp-decryption.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
class CryptoSample;
|
||||
|
||||
namespace gmp {
|
||||
|
||||
class GMPStringListImpl : public GMPStringList
|
||||
{
|
||||
public:
|
||||
explicit GMPStringListImpl(const nsTArray<nsCString>& aStrings);
|
||||
uint32_t Size() const override;
|
||||
void StringAt(uint32_t aIndex,
|
||||
const char** aOutString, uint32_t *aOutLength) const override;
|
||||
virtual ~GMPStringListImpl() override;
|
||||
void RelinquishData(nsTArray<nsCString>& aStrings);
|
||||
|
||||
private:
|
||||
nsTArray<nsCString> mStrings;
|
||||
};
|
||||
|
||||
class GMPEncryptedBufferDataImpl : public GMPEncryptedBufferMetadata {
|
||||
public:
|
||||
explicit GMPEncryptedBufferDataImpl(const CryptoSample& aCrypto);
|
||||
explicit GMPEncryptedBufferDataImpl(const GMPDecryptionData& aData);
|
||||
virtual ~GMPEncryptedBufferDataImpl();
|
||||
|
||||
void RelinquishData(GMPDecryptionData& aData);
|
||||
|
||||
const uint8_t* KeyId() const override;
|
||||
uint32_t KeyIdSize() const override;
|
||||
const uint8_t* IV() const override;
|
||||
uint32_t IVSize() const override;
|
||||
uint32_t NumSubsamples() const override;
|
||||
const uint16_t* ClearBytes() const override;
|
||||
const uint32_t* CipherBytes() const override;
|
||||
const GMPStringList* SessionIds() const override;
|
||||
|
||||
private:
|
||||
nsTArray<uint8_t> mKeyId;
|
||||
nsTArray<uint8_t> mIV;
|
||||
nsTArray<uint16_t> mClearBytes;
|
||||
nsTArray<uint32_t> mCipherBytes;
|
||||
|
||||
GMPStringListImpl mSessionIdList;
|
||||
};
|
||||
|
||||
class GMPBufferImpl : public GMPBuffer {
|
||||
public:
|
||||
GMPBufferImpl(uint32_t aId, const nsTArray<uint8_t>& aData)
|
||||
: mId(aId)
|
||||
, mData(aData)
|
||||
{
|
||||
}
|
||||
uint32_t Id() const override {
|
||||
return mId;
|
||||
}
|
||||
uint8_t* Data() override {
|
||||
return mData.Elements();
|
||||
}
|
||||
uint32_t Size() const override {
|
||||
return mData.Length();
|
||||
}
|
||||
void Resize(uint32_t aSize) override {
|
||||
mData.SetLength(aSize);
|
||||
}
|
||||
|
||||
// Set metadata object to be freed when this buffer is destroyed.
|
||||
void SetMetadata(GMPEncryptedBufferDataImpl* aMetadata) {
|
||||
mMetadata = aMetadata;
|
||||
}
|
||||
|
||||
uint32_t mId;
|
||||
nsTArray<uint8_t> mData;
|
||||
nsAutoPtr<GMPEncryptedBufferDataImpl> mMetadata;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPEncryptedBufferDataImpl_h_
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "gmp-video-codec.h"
|
||||
#include "gmp-video-frame-encoded.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "IPCMessageUtils.h"
|
||||
|
||||
namespace IPC {
|
||||
|
@ -20,28 +19,6 @@ struct ParamTraits<GMPErr>
|
|||
GMPLastErr>
|
||||
{};
|
||||
|
||||
struct GMPDomExceptionValidator {
|
||||
static bool IsLegalValue(GMPDOMException aValue) {
|
||||
switch (aValue) {
|
||||
case kGMPNoModificationAllowedError:
|
||||
case kGMPNotFoundError:
|
||||
case kGMPNotSupportedError:
|
||||
case kGMPInvalidStateError:
|
||||
case kGMPSyntaxError:
|
||||
case kGMPInvalidModificationError:
|
||||
case kGMPInvalidAccessError:
|
||||
case kGMPSecurityError:
|
||||
case kGMPAbortError:
|
||||
case kGMPQuotaExceededError:
|
||||
case kGMPTimeoutError:
|
||||
case kGMPTypeError:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<GMPVideoFrameType>
|
||||
: public ContiguousEnumSerializer<GMPVideoFrameType,
|
||||
|
@ -49,32 +26,6 @@ struct ParamTraits<GMPVideoFrameType>
|
|||
kGMPVideoFrameInvalid>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<GMPDOMException>
|
||||
: public EnumSerializer<GMPDOMException, GMPDomExceptionValidator>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<GMPSessionMessageType>
|
||||
: public ContiguousEnumSerializer<GMPSessionMessageType,
|
||||
kGMPLicenseRequest,
|
||||
kGMPMessageInvalid>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<GMPMediaKeyStatus>
|
||||
: public ContiguousEnumSerializer<GMPMediaKeyStatus,
|
||||
kGMPUsable,
|
||||
kGMPMediaKeyStatusInvalid>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<GMPSessionType>
|
||||
: public ContiguousEnumSerializer<GMPSessionType,
|
||||
kGMPTemporySession,
|
||||
kGMPSessionInvalid>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<GMPVideoCodecComplexity>
|
||||
: public ContiguousEnumSerializer<GMPVideoCodecComplexity,
|
||||
|
|
|
@ -691,22 +691,6 @@ GMPParent::ReadGMPInfoFile(nsIFile* aFile)
|
|||
}
|
||||
}
|
||||
|
||||
if (cap.mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) {
|
||||
mCanDecrypt = true;
|
||||
|
||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
if (!mozilla::SandboxInfo::Get().CanSandboxMedia()) {
|
||||
nsPrintfCString msg(
|
||||
"GMPParent::ReadGMPMetaData: Plugin \"%s\" is an EME CDM"
|
||||
" but this system can't sandbox it; not loading.",
|
||||
mDisplayName.get());
|
||||
printf_stderr("%s\n", msg.get());
|
||||
LOGD("%s", msg.get());
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mCapabilities.AppendElement(Move(cap));
|
||||
}
|
||||
|
||||
|
@ -789,6 +773,11 @@ GMPParent::ParseChromiumManifest(const nsAString& aJSON)
|
|||
// psapi.dll added for GetMappedFileNameW, which could possibly be avoided
|
||||
// in future versions, see bug 1383611 for details.
|
||||
mLibs = NS_LITERAL_CSTRING("dxva2.dll, psapi.dll");
|
||||
#endif
|
||||
} else if (mDisplayName.EqualsASCII("fake")) {
|
||||
kEMEKeySystem = NS_LITERAL_CSTRING("fake");
|
||||
#if XP_WIN
|
||||
mLibs = NS_LITERAL_CSTRING("dxva2.dll");
|
||||
#endif
|
||||
} else {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "GMPProcessParent.h"
|
||||
#include "GMPServiceParent.h"
|
||||
#include "GMPDecryptorParent.h"
|
||||
#include "GMPVideoDecoderParent.h"
|
||||
#include "GMPVideoEncoderParent.h"
|
||||
#include "GMPTimerParent.h"
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "nsIXULAppInfo.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "GMPDecryptorParent.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "VideoUtils.h"
|
||||
|
@ -425,50 +424,6 @@ GeckoMediaPluginService::GetGMPVideoEncoder(GMPCrashHelper* aHelper,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginService::GetGMPDecryptor(GMPCrashHelper* aHelper,
|
||||
nsTArray<nsCString>* aTags,
|
||||
const nsACString& aNodeId,
|
||||
UniquePtr<GetGMPDecryptorCallback>&& aCallback)
|
||||
{
|
||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
if (!SandboxInfo::Get().CanSandboxMedia()) {
|
||||
NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
|
||||
"EME decryption not available without sandboxing support.");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
|
||||
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
|
||||
NS_ENSURE_ARG(aCallback);
|
||||
|
||||
if (mShuttingDownOnGMPThread) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
GetGMPDecryptorCallback* rawCallback = aCallback.release();
|
||||
RefPtr<AbstractThread> thread(GetAbstractGMPThread());
|
||||
RefPtr<GMPCrashHelper> helper(aHelper);
|
||||
GetContentParent(aHelper, aNodeId, NS_LITERAL_CSTRING(GMP_API_DECRYPTOR), *aTags)
|
||||
->Then(thread, __func__,
|
||||
[rawCallback, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
|
||||
RefPtr<GMPContentParent> parent = wrapper->mParent;
|
||||
UniquePtr<GetGMPDecryptorCallback> callback(rawCallback);
|
||||
GMPDecryptorParent* actor = nullptr;
|
||||
if (parent && NS_SUCCEEDED(parent->GetGMPDecryptor(&actor))) {
|
||||
actor->SetCrashHelper(helper);
|
||||
}
|
||||
callback->Done(actor);
|
||||
},
|
||||
[rawCallback] {
|
||||
UniquePtr<GetGMPDecryptorCallback> callback(rawCallback);
|
||||
callback->Done(nullptr);
|
||||
});
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper)
|
||||
{
|
||||
|
|
|
@ -87,11 +87,6 @@ public:
|
|||
const nsACString& aNodeId,
|
||||
UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
|
||||
override;
|
||||
NS_IMETHOD GetGMPDecryptor(GMPCrashHelper* aHelper,
|
||||
nsTArray<nsCString>* aTags,
|
||||
const nsACString& aNodeId,
|
||||
UniquePtr<GetGMPDecryptorCallback>&& aCallback)
|
||||
override;
|
||||
|
||||
// Helper for backwards compatibility with WebRTC/tests.
|
||||
NS_IMETHOD
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "nsNativeCharsetUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "GMPDecryptorParent.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "runnable_utils.h"
|
||||
#include "VideoUtils.h"
|
||||
|
@ -1955,4 +1954,4 @@ GMPServiceParent::Create(Endpoint<PGMPServiceParent>&& aGMPService)
|
|||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#undef NS_DispatchToMainThread
|
||||
#undef NS_DispatchToMainThread
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
using GMPBufferType from "gmp-video-codec.h";
|
||||
using GMPMediaKeyStatus from "gmp-decryption.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
@ -15,14 +14,6 @@ struct NodeIdData {
|
|||
nsString mGMPName;
|
||||
};
|
||||
|
||||
struct GMPDecryptionData {
|
||||
uint8_t[] mKeyId;
|
||||
uint8_t[] mIV;
|
||||
uint16_t[] mClearBytes;
|
||||
uint32_t[] mCipherBytes;
|
||||
nsCString[] mSessionIds;
|
||||
};
|
||||
|
||||
struct GMPVideoEncodedFrameData
|
||||
{
|
||||
uint32_t mEncodedWidth;
|
||||
|
@ -34,7 +25,6 @@ struct GMPVideoEncodedFrameData
|
|||
GMPBufferType mBufferType;
|
||||
Shmem mBuffer;
|
||||
bool mCompleteFrame;
|
||||
GMPDecryptionData mDecryptionData;
|
||||
};
|
||||
|
||||
struct GMPPlaneData
|
||||
|
@ -55,11 +45,6 @@ struct GMPVideoi420FrameData
|
|||
uint64_t mDuration; // microseconds
|
||||
};
|
||||
|
||||
struct GMPKeyInformation {
|
||||
uint8_t[] keyId;
|
||||
GMPMediaKeyStatus status;
|
||||
};
|
||||
|
||||
struct CDMInputBuffer {
|
||||
Shmem mData;
|
||||
uint8_t[] mKeyId;
|
||||
|
|
|
@ -123,27 +123,15 @@ GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsCString
|
||||
CryptoInfo(const GMPUniquePtr<GMPVideoEncodedFrame>& aInputFrame)
|
||||
{
|
||||
const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData();
|
||||
if (!crypto) {
|
||||
return EmptyCString();
|
||||
}
|
||||
return nsPrintfCString(" kid=%s",
|
||||
ToHexString(crypto->KeyId(), crypto->KeyIdSize()).get());
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoderParent::Decode(GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame,
|
||||
bool aMissingFrames,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo,
|
||||
int64_t aRenderTimeMs)
|
||||
{
|
||||
LOGV(("GMPVideoDecoderParent[%p]::Decode() timestamp=%" PRId64 " keyframe=%d%s",
|
||||
LOGV(("GMPVideoDecoderParent[%p]::Decode() timestamp=%" PRId64 " keyframe=%d",
|
||||
this, aInputFrame->TimeStamp(),
|
||||
aInputFrame->FrameType() == kGMPKeyFrame,
|
||||
CryptoInfo(aInputFrame).get()));
|
||||
aInputFrame->FrameType() == kGMPKeyFrame));
|
||||
|
||||
if (!mIsOpen) {
|
||||
LOGE(("GMPVideoDecoderParent[%p]::Decode() ERROR; dead GMPVideoDecoder", this));
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "GMPVideoHost.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
#include "GMPSharedMemManager.h"
|
||||
#include "GMPEncryptedBufferDataImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
@ -41,9 +40,6 @@ GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(const GMPVideoEncodedFrameDat
|
|||
mBufferType(aFrameData.mBufferType())
|
||||
{
|
||||
MOZ_ASSERT(aHost);
|
||||
if (aFrameData.mDecryptionData().mKeyId().Length() > 0) {
|
||||
mCrypto = new GMPEncryptedBufferDataImpl(aFrameData.mDecryptionData());
|
||||
}
|
||||
aHost->EncodedFrameCreated(this);
|
||||
}
|
||||
|
||||
|
@ -55,18 +51,6 @@ GMPVideoEncodedFrameImpl::~GMPVideoEncodedFrameImpl()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
GMPVideoEncodedFrameImpl::InitCrypto(const CryptoSample& aCrypto)
|
||||
{
|
||||
mCrypto = new GMPEncryptedBufferDataImpl(aCrypto);
|
||||
}
|
||||
|
||||
const GMPEncryptedBufferMetadata*
|
||||
GMPVideoEncodedFrameImpl::GetDecryptionData() const
|
||||
{
|
||||
return mCrypto;
|
||||
}
|
||||
|
||||
GMPVideoFrameFormat
|
||||
GMPVideoEncodedFrameImpl::GetFrameFormat()
|
||||
{
|
||||
|
@ -105,9 +89,6 @@ GMPVideoEncodedFrameImpl::RelinquishFrameData(GMPVideoEncodedFrameData& aFrameDa
|
|||
aFrameData.mCompleteFrame() = mCompleteFrame;
|
||||
aFrameData.mBuffer() = mBuffer;
|
||||
aFrameData.mBufferType() = mBufferType;
|
||||
if (mCrypto) {
|
||||
mCrypto->RelinquishData(aFrameData.mDecryptionData());
|
||||
}
|
||||
|
||||
// This method is called right before Shmem is sent to another process.
|
||||
// We need to effectively zero out our member copy so that we don't
|
||||
|
@ -164,7 +145,6 @@ GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame)
|
|||
mSize = f.mSize; // already set...
|
||||
mCompleteFrame = f.mCompleteFrame;
|
||||
mBufferType = f.mBufferType;
|
||||
mCrypto = new GMPEncryptedBufferDataImpl(*(f.mCrypto));
|
||||
// Don't copy host, that should have been set properly on object creation via host.
|
||||
|
||||
return GMPNoErr;
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include "gmp-errors.h"
|
||||
#include "gmp-video-frame.h"
|
||||
#include "gmp-video-frame-encoded.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "mozilla/ipc/Shmem.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
|
@ -45,7 +44,6 @@ namespace gmp {
|
|||
|
||||
class GMPVideoHostImpl;
|
||||
class GMPVideoEncodedFrameData;
|
||||
class GMPEncryptedBufferDataImpl;
|
||||
|
||||
class GMPVideoEncodedFrameImpl: public GMPVideoEncodedFrame
|
||||
{
|
||||
|
@ -55,8 +53,6 @@ public:
|
|||
GMPVideoEncodedFrameImpl(const GMPVideoEncodedFrameData& aFrameData, GMPVideoHostImpl* aHost);
|
||||
virtual ~GMPVideoEncodedFrameImpl();
|
||||
|
||||
void InitCrypto(const CryptoSample& aCrypto);
|
||||
|
||||
// This is called during a normal destroy sequence, which is
|
||||
// when a consumer is finished or during XPCOM shutdown.
|
||||
void DoneWithAPI();
|
||||
|
@ -97,7 +93,6 @@ public:
|
|||
uint8_t* Buffer() override;
|
||||
GMPBufferType BufferType() const override;
|
||||
void SetBufferType(GMPBufferType aBufferType) override;
|
||||
const GMPEncryptedBufferMetadata* GetDecryptionData() const override;
|
||||
|
||||
private:
|
||||
void DestroyBuffer();
|
||||
|
@ -112,7 +107,6 @@ private:
|
|||
GMPVideoHostImpl* mHost;
|
||||
ipc::Shmem mBuffer;
|
||||
GMPBufferType mBufferType;
|
||||
nsAutoPtr<GMPEncryptedBufferDataImpl> mCrypto;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче