Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^5.0.0",
"@ccx-public/ingest": "file:etc/packages/ingest-4.3.0.tgz",
"@contentauth/c2pa-web": "^0.7.1",
"@contentauth/c2pa-web": "^0.11.0",
"@intl/adobe-locales": "file:etc/packages/adobe-locales-2.35.2.tgz",
"@mapbox/mapbox-sdk": "^0.13.7",
"@netlify/functions": "^2.1.0",
Expand Down
48 changes: 13 additions & 35 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 61 additions & 16 deletions src/lib/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ import {
} from './selectors/validationResult';
import { selectWeb3 } from './selectors/web3Info';
import { selectWebsite } from './selectors/website';
import { loadThumbnail, type ThumbnailInfo } from './thumbnail';
import { toAbsoluteIdentifier } from './resolveThumbnails';
import {
loadThumbnail,
type ThumbnailInfo,
type ThumbnailResult,
} from './thumbnail';
import type { Disposable } from './types';

const MANIFEST_STORE_MIME_TYPE = 'application/x-c2pa-manifest-store';
Expand Down Expand Up @@ -132,9 +137,11 @@ export function getIngredientDataType(
export async function resultToAssetMap({
manifestStore,
source,
thumbnails,
}: {
manifestStore: ManifestStore;
source: Blob | File;
thumbnails: Map<string, Blob>;
}): Promise<DisposableAssetDataMap> {
const assetMap: AssetDataMap = {};
const disposers: (() => void)[] = [];
Expand Down Expand Up @@ -177,6 +184,26 @@ export async function resultToAssetMap({
}
}

async function lookupThumbnail(
ref: Thumbnail | null | undefined,
containingManifestLabel: string,
): Promise<ThumbnailResult> {
const blob = ref?.identifier
? thumbnails.get(toAbsoluteIdentifier(ref.identifier, containingManifestLabel))
: undefined;

if (!blob) {
return loadThumbnail(ref?.format, undefined);
}

const url = URL.createObjectURL(blob);

return loadThumbnail(ref?.format, {
url,
dispose: () => URL.revokeObjectURL(url),
});
}

if (!isManifest && (!manifestStore || hasError || hasOtgp)) {
// Fallback for raw files: render the original source file directly
const url = URL.createObjectURL(source);
Expand Down Expand Up @@ -231,14 +258,11 @@ export async function resultToAssetMap({
runtimeValidationStatuses: ManifestLabelValidationStatusMap,
id: string,
): Promise<AssetData> {
const manifest = manifestStore.manifests?.[manifestStore.active_manifest || ''];
const activeManifestLabel = manifestStore.active_manifest || '';
const manifest = manifestStore.manifests?.[activeManifestLabel];
if (!manifest) throw new Error('Active manifest not found');

// 0.17.x SDK dropped internal thumbnail generation. Pass undefined to skip WASM fetch.
let thumbnail = await loadThumbnail(
manifest.thumbnail?.format,
undefined
);
let thumbnail = await lookupThumbnail(manifest.thumbnail, activeManifestLabel);

if (
!thumbnail.info &&
Expand All @@ -248,7 +272,7 @@ export async function resultToAssetMap({
// Fallback for active manifest: render the original source file directly
const url = URL.createObjectURL(source);
thumbnail = await loadThumbnail(
source.type,
source.type,
{ url, dispose: () => URL.revokeObjectURL(url) }
);
}
Expand All @@ -263,6 +287,7 @@ export async function resultToAssetMap({
manifestStore,
runtimeValidationStatuses,
id,
activeManifestLabel,
),
manifestData: await getManifestData(manifest, rootValidationResult),
dataType: null,
Expand All @@ -284,17 +309,34 @@ export async function resultToAssetMap({
manifestStore: ManifestStore,
runtimeValidationStatuses: ManifestLabelValidationStatusMap,
id: string,
containingManifestLabel: string,
): Promise<AssetData> {
const ingredientManifestLabel = ingredient.active_manifest || (ingredient as ExtendedIngredient).activeManifest;
const ingredientManifest = ingredientManifestLabel ? manifestStore.manifests?.[ingredientManifestLabel] : null;



// 0.17.x SDK dropped internal thumbnail generation. Skip WASM fetch for ingredients.
const thumbnail = await loadThumbnail(
ingredient.thumbnail?.format,
undefined,
);
/**
* The code prioritizes the signed ingredient's own c2pa.thumbnail.claim, if present,
* over the c2pa.thumbnail.ingredient assertion that the consuming manifest's signer
* references via ingredient.thumbnail. When a signed ingredient already provides
* its own claim thumbnail, a consuming signer's separate ingredient assertion
* thumbnail could be an intentional override pointing at a misleading image. The
* claim thumbnail more faithfully represents the ingredient.
*
* When no claim thumbnail is available for an ingredient, we fall back to
* c2pa.thumbnail.ingredient only if the containing manifest is trusted. If it is
* untrusted, we suppress the thumbnail so an untrusted signer can't force a false
* thumbnail to display for an ingredient that has no claim thumbnail of its own. A
* signed ingredient's own claim thumbnail is still shown when present (untrusted
* state is flagged in the UI).
*/
const containingManifestUntrusted = (runtimeValidationStatuses[containingManifestLabel] ?? [])
.some((s) => s.code.includes('signingCredential.untrusted') || s.code.includes('signingCredential.invalid'));

const thumbnail = ingredientManifestLabel && ingredientManifest?.thumbnail
Comment thread
tmathern marked this conversation as resolved.
? await lookupThumbnail(ingredientManifest.thumbnail, ingredientManifestLabel)
: !containingManifestUntrusted
? await lookupThumbnail(ingredient.thumbnail, containingManifestLabel)
: await loadThumbnail(undefined, undefined);

const activeManifestValidationResults =
(ingredient.validation_results?.activeManifest || (ingredient as ExtendedIngredient).validationResults?.activeManifest) ?? undefined;
Expand All @@ -317,12 +359,13 @@ export async function resultToAssetMap({
title: ingredient.title ?? null,
thumbnail: thumbnail.info,
mimeType: ingredient.format || '',
children: (showChildren && ingredientManifest?.ingredients)
children: (showChildren && ingredientManifest?.ingredients && ingredientManifestLabel)
? await processIngredients(
ingredientManifest.ingredients,
manifestStore,
runtimeValidationStatuses,
id,
ingredientManifestLabel,
)
: [],
manifestData: await getManifestData(ingredientManifest, validationResult),
Expand Down Expand Up @@ -469,6 +512,7 @@ export async function resultToAssetMap({
manifestStore: ManifestStore,
runtimeValidationStatuses: ManifestLabelValidationStatusMap,
id: string,
containingManifestLabel: string,
): Promise<string[]> {
const ingredientIds = ingredients.map(async (ingredient, idx) => {
const ingredientId = `${id}.${idx}`;
Expand All @@ -478,6 +522,7 @@ export async function resultToAssetMap({
manifestStore,
runtimeValidationStatuses,
ingredientId,
containingManifestLabel,
);

return ingredientId;
Expand Down
Loading
Loading