Browse Source

Merge branch 'master' of https://github.com/molstar/molstar

Alexander Rose 5 years ago
parent
commit
6bbf20980e

+ 7 - 1
src/mol-plugin-state/transforms/data.ts

@@ -88,7 +88,7 @@ const DownloadBlob = PluginStateTransform.BuiltIn({
     apply({ params }, plugin: PluginContext) {
         return Task.create('Download Blob', async ctx => {
             const entries: SO.Data.BlobEntry[] = [];
-            const data = await ajaxGetMany(ctx, params.sources, params.maxConcurrency || 4);
+            const data = await ajaxGetMany(ctx, params.sources, params.maxConcurrency || 4, plugin.managers.asset);
 
             for (let i = 0; i < data.length; i++) {
                 const r = data[i], src = params.sources[i];
@@ -102,6 +102,12 @@ const DownloadBlob = PluginStateTransform.BuiltIn({
             return new SO.Data.Blob(entries, { label: 'Data Blob', description: `${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}` });
         });
     },
+    dispose({ params }, plugin: PluginContext) {
+        if (!params) return;
+        for (const s of params.sources) {
+            plugin.managers.asset.release({ url: s.url, body: s.body });
+        }
+    }
     // TODO: ??
     // update({ oldParams, newParams, b }) {
     //     return 0 as any;

+ 3 - 2
src/mol-plugin-state/transforms/representation.ts

@@ -141,12 +141,13 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
     },
     update({ a, b, oldParams, newParams, cache }, plugin: PluginContext) {
         return Task.create('Structure Representation', async ctx => {
+            if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
+
+            // dispose isn't called on update so we need to handle it manually
             const oldProvider = plugin.representation.structure.registry.get(oldParams.type.name);
             if (oldProvider.ensureCustomProperties) oldProvider.ensureCustomProperties.detach(a.data);
             Theme.releaseDependencies(plugin.representation.structure.themes, { structure: a.data }, oldParams);
 
-            if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
-
             const provider = plugin.representation.structure.registry.get(newParams.type.name);
             const propertyCtx = { runtime: ctx, fetch: plugin.fetch };
             if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);

+ 18 - 1
src/mol-state/state.ts

@@ -477,7 +477,9 @@ async function update(ctx: UpdateContext) {
 
         for (let i = deletes.length - 1; i >= 0; i--) {
             const cell = ctx.cells.get(deletes[i]);
-            cell?.transform.transformer.definition.dispose?.({ b: cell.obj, params: cell.transform.params, cache: cell.cache }, ctx.parent.globalContext);
+            if (cell) {
+                dispose(cell.transform, cell.obj, cell?.transform.params, cell.cache, ctx.parent.globalContext);
+            }
         }
 
         for (const d of deletes) {
@@ -866,9 +868,11 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo
         return { ref: currentRef, action: 'created', obj };
     } else {
         const oldParams = current.params.values;
+        const oldCache = current.cache;
         const newParams = params.values;
         current.params = params;
 
+
         const updateKind = !!current.obj && current.obj !== StateObject.Null
             ? await updateObject(ctx, current, transform.transformer, parent, current.obj!, oldParams, newParams)
             : StateTransformer.UpdateResult.Recreate;
@@ -876,7 +880,10 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo
         switch (updateKind) {
             case StateTransformer.UpdateResult.Recreate: {
                 const oldObj = current.obj;
+                dispose(transform, oldObj, oldParams, oldCache, ctx.parent.globalContext);
+
                 const newObj = await createObject(ctx, current, transform.transformer, parent, newParams);
+
                 updateTag(newObj, transform);
                 current.obj = newObj;
                 return { ref: currentRef, action: 'replaced', oldObj, obj: newObj };
@@ -885,6 +892,8 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo
                 updateTag(current.obj, transform);
                 return { ref: currentRef, action: 'updated', obj: current.obj! };
             case StateTransformer.UpdateResult.Null: {
+                dispose(transform, current.obj, oldParams, oldCache, ctx.parent.globalContext);
+
                 current.obj = StateObject.Null;
                 return { ref: currentRef, action: 'updated', obj: current.obj! };
             }
@@ -894,6 +903,14 @@ async function updateNode(ctx: UpdateContext, currentRef: Ref): Promise<UpdateNo
     }
 }
 
+function dispose(transform: StateTransform, b: StateObject | undefined, params: any, cache: any, globalContext: any) {
+    transform.transformer.definition.dispose?.({
+        b: b !== StateObject.Null ? b : void 0,
+        params,
+        cache
+    }, globalContext);
+}
+
 function updateTag(obj: StateObject | undefined, transform: StateTransform) {
     if (!obj || obj === StateObject.Null) return;
     (obj.tags as string[] | undefined) = transform.tags;

+ 10 - 1
src/mol-state/transformer.ts

@@ -103,7 +103,16 @@ namespace Transformer {
         /** Parameter interpolation */
         interpolate?(src: P, target: P, t: number, globalCtx: unknown): P
 
-        /** Cleanup resources */
+        /**
+         * Cleanup resources
+         *
+         * Automatically called on deleting an object and on recreating it
+         * (i.e. when update returns UpdateResult.Recreate or UpdateResult.Null)
+         *
+         * Not called on UpdateResult.Updated because the resources might not
+         * have been invalidated. In this case, the possible cleanup has to be handled
+         * manually.
+         */
         dispose?(params: DisposeParams<B, P>, globalCtx: unknown): void
 
         /** Custom conversion to and from JSON */

+ 9 - 2
src/mol-util/data-source.ts

@@ -10,6 +10,7 @@
 import { Task, RuntimeContext } from '../mol-task';
 import { unzip, ungzip } from './zip/zip';
 import { utf8Read } from '../mol-io/common/utf8';
+import { AssetManager } from './assets';
 
 // polyfill XMLHttpRequest in node.js
 const XHR = typeof document === 'undefined' ? require('xhr2') as {
@@ -281,7 +282,7 @@ function ajaxGetInternal<T extends DataType>(title: string | undefined, url: str
 }
 
 export type AjaxGetManyEntry<T> = { kind: 'ok', id: string, result: T } | { kind: 'error', id: string, error: any }
-export async function ajaxGetMany(ctx: RuntimeContext, sources: { id: string, url: string, isBinary?: boolean, body?: string, canFail?: boolean }[], maxConcurrency: number) {
+export async function ajaxGetMany(ctx: RuntimeContext, sources: { id: string, url: string, isBinary?: boolean, body?: string, canFail?: boolean }[], maxConcurrency: number, assetManager?: AssetManager) {
     const len = sources.length;
     const slots: AjaxGetManyEntry<string | Uint8Array>[] = new Array(sources.length);
 
@@ -290,7 +291,13 @@ export async function ajaxGetMany(ctx: RuntimeContext, sources: { id: string, ur
     let currentSrc = 0;
     for (let _i = Math.min(len, maxConcurrency); currentSrc < _i; currentSrc++) {
         const current = sources[currentSrc];
-        promises.push(wrapPromise(currentSrc, current.id, ajaxGet({ url: current.url, type: current.isBinary ? 'binary' : 'string' }).runAsChild(ctx)));
+
+        if (assetManager) {
+            promises.push(wrapPromise(currentSrc, current.id,
+                assetManager.resolve({ url: current.url, body: current.body }, current.isBinary ? 'binary' : 'string').runAsChild(ctx)));
+        } else {
+            promises.push(wrapPromise(currentSrc, current.id, ajaxGet({ url: current.url, type: current.isBinary ? 'binary' : 'string' }).runAsChild(ctx)));
+        }
         promiseKeys.push(currentSrc);
     }