MozReview-Commit-ID: 9qR4QYHAi2x
This commit is contained in:
Wes Kocher 2017-09-28 17:08:09 -07:00
Родитель 29d5db60ba 1cdb198bd9
Коммит 253017b382
310 изменённых файлов: 4281 добавлений и 7529 удалений

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

@ -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 deoch pennadoù, videoioù ha pajennoù all gweladennet pe lakaet er sinedoù nevez zo.",
"topstories_empty_state": "Aet oc'h betek penn. Distroit diwezhatoc'h evit muioch a istorioù digant {provider}. Noc'h ket evit gortoz? Dibabit un danvez brudet evit klask muioch a bennadoù dedennus eus pep lech 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 dhfhosglas 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 dhfhaoidte.",
"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 dhaithghearr. 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

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