Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3334365
D13892.id45717.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
24 KB
Referenced Files
None
Subscribers
None
D13892.id45717.diff
View Options
diff --git a/native/avatars/avatar-hooks.js b/native/avatars/avatar-hooks.js
--- a/native/avatars/avatar-hooks.js
+++ b/native/avatars/avatar-hooks.js
@@ -8,13 +8,9 @@
import filesystem from 'react-native-fs';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
-import {
- uploadMultimedia,
- useBlobServiceUpload,
-} from 'lib/actions/upload-actions.js';
+import { useBlobServiceUpload } from 'lib/actions/upload-actions.js';
import { EditThreadAvatarContext } from 'lib/components/base-edit-thread-avatar-provider.react.js';
import { EditUserAvatarContext } from 'lib/components/edit-user-avatar-provider.react.js';
-import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
import {
extensionFromFilename,
filenameFromPathOrURI,
@@ -44,8 +40,6 @@
import blobServiceUploadHandler from '../utils/blob-service-upload.js';
import { useStaffCanSee } from '../utils/staff-utils.js';
-const useBlobServiceUploads = true;
-
function displayAvatarUpdateFailureAlert(): void {
Alert.alert(
'Couldn’t save avatar',
@@ -59,28 +53,9 @@
media: MediaResult,
metadataUploadLocation: 'keyserver' | 'none',
) => Promise<?UpdateUserAvatarRequest> {
- const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
return React.useCallback(
async (processedMedia, metadataUploadLocation) => {
- const useBlobService =
- metadataUploadLocation !== 'keyserver' || useBlobServiceUploads;
- if (!useBlobService) {
- const { uploadURI, filename, mime, dimensions } = processedMedia;
- const { id } = await callUploadMultimedia(
- {
- uri: uploadURI,
- name: filename,
- type: mime,
- },
- dimensions,
- );
- if (!id) {
- return undefined;
- }
- return { type: 'image', uploadID: id };
- }
-
const { result: encryptionResult } = await encryptMedia(processedMedia);
if (!encryptionResult.success) {
throw new Error('Avatar media encryption failed.');
@@ -132,7 +107,7 @@
}
return { type: 'encrypted_image', uploadID: id };
},
- [callUploadMultimedia, callBlobServiceUpload],
+ [callBlobServiceUpload],
);
}
diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js
--- a/native/input/input-state-container.react.js
+++ b/native/input/input-state-container.react.js
@@ -16,10 +16,7 @@
import {
type BlobServiceUploadAction,
type BlobServiceUploadResult,
- type MultimediaUploadCallbacks,
- type MultimediaUploadExtras,
updateMultimediaMessageMediaActionType,
- uploadMultimedia,
useBlobServiceUpload,
} from 'lib/actions/upload-actions.js';
import {
@@ -32,7 +29,6 @@
CallSingleKeyserverEndpointOptions,
CallSingleKeyserverEndpointResponse,
} from 'lib/keyserver-conn/call-single-keyserver-endpoint.js';
-import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
import { pathFromURI, replaceExtension } from 'lib/media/file-utils.js';
import {
getNextLocalUploadID,
@@ -62,7 +58,6 @@
MediaMissionResult,
MediaMissionStep,
NativeMediaSelection,
- UploadMultimediaResult,
} from 'lib/types/media-types.js';
import { messageTypes } from 'lib/types/message-types-enum.js';
import {
@@ -146,11 +141,6 @@
+dispatch: Dispatch,
+staffCanSee: boolean,
+dispatchActionPromise: DispatchActionPromise,
- +uploadMultimedia: (
- multimedia: Object,
- extras: MultimediaUploadExtras,
- callbacks: MultimediaUploadCallbacks,
- ) => Promise<UploadMultimediaResult>,
+blobServiceUpload: BlobServiceUploadAction,
+sendMultimediaMessage: (
messageInfo: RawMultimediaMessageInfo,
@@ -192,8 +182,6 @@
> = new Map();
pendingThreadUpdateHandlers: Map<string, (ThreadInfo) => mixed> = new Map();
- useBlobServiceUploads = true;
-
// When the user sends a multimedia message that triggers the creation of a
// sidebar, the sidebar gets created right away, but the message needs to wait
// for the uploads to complete before sending. We use this Set to track the
@@ -629,11 +617,6 @@
}
}
- // eslint-disable-next-line no-unused-vars
- shouldEncryptMedia(threadInfo: ThreadInfo): boolean {
- return true;
- }
-
sendMultimediaMessage = async (
selections: $ReadOnlyArray<NativeMediaSelection>,
threadInfo: ThreadInfo,
@@ -722,7 +705,7 @@
creatorID,
media,
},
- { forceMultimediaMessageType: this.shouldEncryptMedia(threadInfo) },
+ { forceMultimediaMessageType: true },
);
this.props.dispatch({
type: createLocalMessageActionType,
@@ -825,29 +808,27 @@
});
}
- if (this.shouldEncryptMedia(threadInfo)) {
- const encryptionStart = Date.now();
- try {
- const { result: encryptionResult, ...encryptionReturn } =
- await encryptMedia(processedMedia);
- encryptionSteps = encryptionReturn.steps;
- if (!encryptionResult.success) {
- onUploadFailed(encryptionResult.reason);
- return await onUploadFinished(encryptionResult);
- }
- if (encryptionResult.shouldDisposePath) {
- filesToDispose.push(encryptionResult.shouldDisposePath);
- }
- processedMedia = encryptionResult;
- } catch (e) {
- onUploadFailed('encryption failed');
- return await onUploadFinished({
- success: false,
- reason: 'encryption_exception',
- time: Date.now() - encryptionStart,
- exceptionMessage: getMessageForException(e),
- });
+ const encryptionStart = Date.now();
+ try {
+ const { result: encryptionResult, ...encryptionReturn } =
+ await encryptMedia(processedMedia);
+ encryptionSteps = encryptionReturn.steps;
+ if (!encryptionResult.success) {
+ onUploadFailed(encryptionResult.reason);
+ return await onUploadFinished(encryptionResult);
+ }
+ if (encryptionResult.shouldDisposePath) {
+ filesToDispose.push(encryptionResult.shouldDisposePath);
}
+ processedMedia = encryptionResult;
+ } catch (e) {
+ onUploadFailed('encryption failed');
+ return await onUploadFinished({
+ success: false,
+ reason: 'encryption_exception',
+ time: Date.now() - encryptionStart,
+ exceptionMessage: getMessageForException(e),
+ });
}
const { uploadURI, filename, mime } = processedMedia;
@@ -861,136 +842,76 @@
mediaMissionResult;
const isThickThread = threadTypeIsThick(threadInfo.type);
- const useBlobService = isThickThread || this.useBlobServiceUploads;
try {
- if (
- useBlobService &&
- (processedMedia.mediaType === 'encrypted_photo' ||
- processedMedia.mediaType === 'encrypted_video')
- ) {
- const uploadMetadataToKeyserver = !isThickThread;
- const uploadPromise = this.props.blobServiceUpload({
- uploadInput: {
- blobInput: {
- type: 'uri',
- uri: uploadURI,
- filename: filename,
- mimeType: mime,
- },
- blobHash: processedMedia.blobHash,
- encryptionKey: processedMedia.encryptionKey,
- dimensions: processedMedia.dimensions,
- thumbHash:
- processedMedia.mediaType === 'encrypted_photo'
- ? processedMedia.thumbHash
- : null,
- },
- keyserverOrThreadID: uploadMetadataToKeyserver ? threadInfo.id : null,
- callbacks: {
- blobServiceUploadHandler,
- onProgress: (percent: number) => {
- this.setProgress(
- localMessageID,
- localMediaID,
- 'uploading',
- percent,
- );
- },
- },
- });
-
- const uploadThumbnailPromise: Promise<?BlobServiceUploadResult> =
- (async () => {
- if (processedMedia.mediaType !== 'encrypted_video') {
- return undefined;
- }
- return await this.props.blobServiceUpload({
- uploadInput: {
- blobInput: {
- type: 'uri',
- uri: processedMedia.uploadThumbnailURI,
- filename: replaceExtension(`thumb${filename}`, 'jpg'),
- mimeType: 'image/jpeg',
- },
- blobHash: processedMedia.thumbnailBlobHash,
- encryptionKey: processedMedia.thumbnailEncryptionKey,
- loop: false,
- dimensions: processedMedia.dimensions,
- thumbHash: processedMedia.thumbHash,
- },
- keyserverOrThreadID: uploadMetadataToKeyserver
- ? threadInfo.id
- : null,
- callbacks: {
- blobServiceUploadHandler,
- },
- });
- })();
-
- [uploadResult, uploadThumbnailResult] = await Promise.all([
- uploadPromise,
- uploadThumbnailPromise,
- ]);
- } else {
- const uploadPromise = this.props.uploadMultimedia(
- { uri: uploadURI, name: filename, type: mime },
- {
- ...processedMedia.dimensions,
- loop:
- processedMedia.mediaType === 'video' ||
- processedMedia.mediaType === 'encrypted_video'
- ? processedMedia.loop
- : undefined,
- encryptionKey: processedMedia.encryptionKey,
- thumbHash:
- processedMedia.mediaType === 'photo' ||
- processedMedia.mediaType === 'encrypted_photo'
- ? processedMedia.thumbHash
- : null,
+ invariant(
+ processedMedia.mediaType === 'encrypted_photo' ||
+ processedMedia.mediaType === 'encrypted_video',
+ 'uploaded media should be encrypted',
+ );
+ const uploadMetadataToKeyserver = !isThickThread;
+ const uploadPromise = this.props.blobServiceUpload({
+ uploadInput: {
+ blobInput: {
+ type: 'uri',
+ uri: uploadURI,
+ filename: filename,
+ mimeType: mime,
},
- {
- onProgress: (percent: number) =>
- this.setProgress(
- localMessageID,
- localMediaID,
- 'uploading',
- percent,
- ),
- performHTTPMultipartUpload: this.performHTTPMultipartUpload,
+ blobHash: processedMedia.blobHash,
+ encryptionKey: processedMedia.encryptionKey,
+ dimensions: processedMedia.dimensions,
+ thumbHash:
+ processedMedia.mediaType === 'encrypted_photo'
+ ? processedMedia.thumbHash
+ : null,
+ },
+ keyserverOrThreadID: uploadMetadataToKeyserver ? threadInfo.id : null,
+ callbacks: {
+ blobServiceUploadHandler,
+ onProgress: (percent: number) => {
+ this.setProgress(
+ localMessageID,
+ localMediaID,
+ 'uploading',
+ percent,
+ );
},
- );
+ },
+ });
- const uploadThumbnailPromise: Promise<?UploadMultimediaResult> =
- (async () => {
- if (
- processedMedia.mediaType !== 'video' &&
- processedMedia.mediaType !== 'encrypted_video'
- ) {
- return undefined;
- }
- return await this.props.uploadMultimedia(
- {
+ const uploadThumbnailPromise: Promise<?BlobServiceUploadResult> =
+ (async () => {
+ if (processedMedia.mediaType !== 'encrypted_video') {
+ return undefined;
+ }
+ return await this.props.blobServiceUpload({
+ uploadInput: {
+ blobInput: {
+ type: 'uri',
uri: processedMedia.uploadThumbnailURI,
- name: replaceExtension(`thumb${filename}`, 'jpg'),
- type: 'image/jpeg',
- },
- {
- ...processedMedia.dimensions,
- loop: false,
- encryptionKey: processedMedia.thumbnailEncryptionKey,
- thumbHash: processedMedia.thumbHash,
+ filename: replaceExtension(`thumb${filename}`, 'jpg'),
+ mimeType: 'image/jpeg',
},
- {
- performHTTPMultipartUpload: this.performHTTPMultipartUpload,
- },
- );
- })();
+ blobHash: processedMedia.thumbnailBlobHash,
+ encryptionKey: processedMedia.thumbnailEncryptionKey,
+ loop: false,
+ dimensions: processedMedia.dimensions,
+ thumbHash: processedMedia.thumbHash,
+ },
+ keyserverOrThreadID: uploadMetadataToKeyserver
+ ? threadInfo.id
+ : null,
+ callbacks: {
+ blobServiceUploadHandler,
+ },
+ });
+ })();
+
+ [uploadResult, uploadThumbnailResult] = await Promise.all([
+ uploadPromise,
+ uploadThumbnailPromise,
+ ]);
- [uploadResult, uploadThumbnailResult] = await Promise.all([
- uploadPromise,
- uploadThumbnailPromise,
- ]);
- }
mediaMissionResult = { success: true };
} catch (e) {
uploadExceptionMessage = getMessageForException(e);
@@ -1776,7 +1697,6 @@
);
const hasWiFi = useSelector(state => state.connectivity.hasWiFi);
const calendarQuery = useCalendarQuery();
- const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
const callSendMultimediaMessage =
useInputStateContainerSendMultimediaMessage();
@@ -1799,7 +1719,6 @@
hasWiFi={hasWiFi}
mediaReportsEnabled={mediaReportsEnabled}
calendarQuery={calendarQuery}
- uploadMultimedia={callUploadMultimedia}
blobServiceUpload={callBlobServiceUpload}
sendMultimediaMessage={callSendMultimediaMessage}
sendTextMessage={callSendTextMessage}
diff --git a/web/avatars/avatar-hooks.react.js b/web/avatars/avatar-hooks.react.js
--- a/web/avatars/avatar-hooks.react.js
+++ b/web/avatars/avatar-hooks.react.js
@@ -2,11 +2,7 @@
import * as React from 'react';
-import {
- uploadMultimedia,
- useBlobServiceUpload,
-} from 'lib/actions/upload-actions.js';
-import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
+import { useBlobServiceUpload } from 'lib/actions/upload-actions.js';
import type { UpdateUserAvatarRequest } from 'lib/types/avatar-types.js';
import { authoritativeKeyserverID } from '../authoritative-keyserver.js';
@@ -14,8 +10,6 @@
import { generateThumbHash } from '../media/image-utils.js';
import { validateFile } from '../media/media-utils.js';
-const useBlobServiceUploads = true;
-
type AvatarMediaUploadOptions = {
+uploadMetadataToKeyserver?: boolean,
};
@@ -25,7 +19,6 @@
): File => Promise<UpdateUserAvatarRequest> {
const { uploadMetadataToKeyserver = true } = options;
- const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
const uploadAvatarMedia = React.useCallback(
async (file: File): Promise<UpdateUserAvatarRequest> => {
@@ -35,16 +28,6 @@
throw new Error('Avatar media validation failed.');
}
const { file: fixedFile, dimensions } = result;
- const uploadExtras = {
- ...dimensions,
- loop: false,
- };
- const useBlobService =
- !uploadMetadataToKeyserver || useBlobServiceUploads;
- if (!useBlobService) {
- const { id } = await callUploadMultimedia(fixedFile, uploadExtras);
- return { type: 'image', uploadID: id };
- }
const encryptionResponse = await encryptFile(fixedFile);
const { result: encryptionResult } = encryptionResponse;
@@ -94,7 +77,7 @@
encryptionKey,
};
},
- [callBlobServiceUpload, callUploadMultimedia, uploadMetadataToKeyserver],
+ [callBlobServiceUpload, uploadMetadataToKeyserver],
);
return uploadAvatarMedia;
}
diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js
--- a/web/input/input-state-container.react.js
+++ b/web/input/input-state-container.react.js
@@ -20,10 +20,7 @@
import {
type BlobServiceUploadAction,
type DeleteUploadInput,
- type MultimediaUploadCallbacks,
- type MultimediaUploadExtras,
updateMultimediaMessageMediaActionType,
- uploadMultimedia,
useBlobServiceUpload,
useDeleteUpload,
} from 'lib/actions/upload-actions.js';
@@ -38,7 +35,6 @@
useInputStateContainerSendTextMessage,
} from 'lib/hooks/input-state-container-hooks.js';
import { useNewThickThread } from 'lib/hooks/thread-hooks.js';
-import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
import { getNextLocalUploadID } from 'lib/media/media-utils.js';
import { pendingToRealizedThreadIDsSelector } from 'lib/selectors/thread-selectors.js';
import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
@@ -63,7 +59,6 @@
MediaMissionFailure,
MediaMissionResult,
MediaMissionStep,
- UploadMultimediaResult,
} from 'lib/types/media-types.js';
import { messageTypes } from 'lib/types/message-types-enum.js';
import {
@@ -140,11 +135,6 @@
+dispatch: Dispatch,
+dispatchActionPromise: DispatchActionPromise,
+calendarQuery: () => CalendarQuery,
- +uploadMultimedia: (
- multimedia: Object,
- extras: MultimediaUploadExtras,
- callbacks: MultimediaUploadCallbacks,
- ) => Promise<UploadMultimediaResult>,
+blobServiceUpload: BlobServiceUploadAction,
+deleteUpload: (input: DeleteUploadInput) => Promise<void>,
+sendMultimediaMessage: (
@@ -213,8 +203,6 @@
}>,
>();
- useBlobServiceUploads = true;
-
// When the user sends a multimedia message that triggers the creation of a
// sidebar, the sidebar gets created right away, but the message needs to wait
// for the uploads to complete before sending. We use this Set to track the
@@ -311,7 +299,6 @@
string,
{
+threadID: string,
- +shouldEncrypt: boolean,
+uploads: PendingMultimediaUpload[],
},
>();
@@ -327,26 +314,18 @@
) {
continue;
}
- const { shouldEncrypt } = upload;
let assignedUploads = newlyAssignedUploads.get(messageID);
if (!assignedUploads) {
- assignedUploads = { threadID, shouldEncrypt, uploads: [] };
+ assignedUploads = { threadID, uploads: [] };
newlyAssignedUploads.set(messageID, assignedUploads);
}
- if (shouldEncrypt !== assignedUploads.shouldEncrypt) {
- console.warn(
- `skipping upload ${localUploadID} ` +
- "because shouldEncrypt doesn't match",
- );
- continue;
- }
assignedUploads.uploads.push(upload);
}
}
const newMessageInfos = new Map<string, RawMultimediaMessageInfo>();
for (const [messageID, assignedUploads] of newlyAssignedUploads) {
- const { uploads, threadID, shouldEncrypt } = assignedUploads;
+ const { uploads, threadID } = assignedUploads;
const creatorID = this.props.viewerID;
invariant(creatorID, 'need viewer ID in order to send a message');
const media = uploads.map(
@@ -401,7 +380,7 @@
creatorID,
media,
},
- { forceMultimediaMessageType: shouldEncrypt },
+ { forceMultimediaMessageType: true },
);
newMessageInfos.set(messageID, messageInfo);
}
@@ -445,11 +424,6 @@
return rawMessageInfo;
}
- // eslint-disable-next-line no-unused-vars
- shouldEncryptMedia(threadInfo: ThreadInfo): boolean {
- return true;
- }
-
async sendMultimediaMessage(
messageInfo: RawMultimediaMessageInfo,
): Promise<void> {
@@ -817,29 +791,26 @@
}
const { uri, file: fixedFile, mediaType, dimensions } = result;
- const shouldEncrypt = this.shouldEncryptMedia(threadInfo);
-
- let encryptionResult;
- if (shouldEncrypt) {
- let encryptionResponse;
- const encryptionStart = Date.now();
- try {
- encryptionResponse = await encryptFile(fixedFile);
- } catch (e) {
- return {
- steps,
- result: {
- success: false,
- reason: 'encryption_exception',
- time: Date.now() - encryptionStart,
- exceptionMessage: getMessageForException(e),
- },
- };
- }
- steps.push(...encryptionResponse.steps);
- encryptionResult = encryptionResponse.result;
+ let encryptionResponse;
+ const encryptionStart = Date.now();
+ try {
+ encryptionResponse = await encryptFile(fixedFile);
+ } catch (e) {
+ return {
+ steps,
+ result: {
+ success: false,
+ reason: 'encryption_exception',
+ time: Date.now() - encryptionStart,
+ exceptionMessage: getMessageForException(e),
+ },
+ };
}
- if (encryptionResult && !encryptionResult.success) {
+ const { result: encryptionResult, steps: encryptionSteps } =
+ encryptionResponse;
+ steps.push(...encryptionSteps);
+
+ if (!encryptionResult.success) {
return { steps, result: encryptionResult };
}
@@ -874,7 +845,6 @@
abort: null,
steps,
selectTime,
- shouldEncrypt,
},
},
};
@@ -934,48 +904,32 @@
abortHandler: (abort: () => void) =>
this.handleAbortCallback(threadID, localID, abort),
};
- const useBlobService = isThickThread || this.useBlobServiceUploads;
- if (
- useBlobService &&
- (upload.mediaType === 'encrypted_photo' ||
- upload.mediaType === 'encrypted_video')
- ) {
- const { blobHash, dimensions, thumbHash } = upload;
- invariant(
- encryptionKey && blobHash && dimensions,
- 'incomplete encrypted upload',
- );
- uploadResult = await this.props.blobServiceUpload({
- uploadInput: {
- blobInput: {
- type: 'file',
- file: upload.file,
- },
- blobHash,
- encryptionKey,
- dimensions,
- loop: false,
- thumbHash,
+ const { mediaType, blobHash, dimensions, thumbHash } = upload;
+ invariant(
+ mediaType === 'encrypted_photo' || mediaType === 'encrypted_video',
+ 'uploaded media should be encrypted',
+ );
+ invariant(
+ encryptionKey && blobHash && dimensions,
+ 'incomplete encrypted upload',
+ );
+
+ uploadResult = await this.props.blobServiceUpload({
+ uploadInput: {
+ blobInput: {
+ type: 'file',
+ file: upload.file,
},
- keyserverOrThreadID: isThickThread ? null : threadID,
- callbacks,
- });
- } else {
- let uploadExtras = {
- ...upload.dimensions,
+ blobHash,
+ encryptionKey,
+ dimensions,
loop: false,
- thumbHash: upload.thumbHash,
- };
- if (encryptionKey) {
- uploadExtras = { ...uploadExtras, encryptionKey };
- }
- uploadResult = await this.props.uploadMultimedia(
- upload.file,
- uploadExtras,
- callbacks,
- );
- }
+ thumbHash,
+ },
+ keyserverOrThreadID: isThickThread ? null : threadID,
+ callbacks,
+ });
} catch (e) {
uploadExceptionMessage = getMessageForException(e);
this.handleUploadFailure(threadID, localID);
@@ -1711,7 +1665,6 @@
pendingToRealizedThreadIDsSelector(state.threadStore.threadInfos),
);
const calendarQuery = useSelector(nonThreadCalendarQuery);
- const callUploadMultimedia = useLegacyAshoatKeyserverCall(uploadMultimedia);
const callBlobServiceUpload = useBlobServiceUpload();
const callDeleteUpload = useDeleteUpload();
const callSendMultimediaMessage =
@@ -1750,7 +1703,6 @@
messageStoreMessages={messageStoreMessages}
pendingRealizedThreadIDs={pendingToRealizedThreadIDs}
calendarQuery={calendarQuery}
- uploadMultimedia={callUploadMultimedia}
blobServiceUpload={callBlobServiceUpload}
deleteUpload={callDeleteUpload}
sendMultimediaMessage={callSendMultimediaMessage}
diff --git a/web/input/input-state.js b/web/input/input-state.js
--- a/web/input/input-state.js
+++ b/web/input/input-state.js
@@ -42,7 +42,6 @@
+abort: ?() => void,
+steps: MediaMissionStep[],
+selectTime: number,
- +shouldEncrypt: boolean,
};
export type TypeaheadState = {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 22, 6:53 AM (17 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2560406
Default Alt Text
D13892.id45717.diff (24 KB)
Attached To
Mode
D13892: [native][web] Clean up dead conditions for media encryption
Attached
Detach File
Event Timeline
Log In to Comment