View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import java.util.Map;
18  import java.util.concurrent.ConcurrentHashMap;
19  
20  import org.htmlunit.javascript.host.file.Blob;
21  
22  /**
23   * The user-agent-wide blob URL store defined by
24   * <a href="https://w3c.github.io/FileAPI/#BlobURLStore">Blob URL Store</a>.
25   *
26   * @author Lai Quang Duong
27   * @author Ronald Brill
28   */
29  public class BlobUrlStore {
30  
31      private static final class Entry {
32          private final Blob object_;
33          private final Page owningPage_;
34  
35          Entry(final Blob object, final Page owningPage) {
36              object_ = object;
37              owningPage_ = owningPage;
38          }
39      }
40  
41      private final Map<String, Entry> store_ = new ConcurrentHashMap<>();
42  
43      /**
44       * Adds an entry.
45       *
46       * @param blobUrl the generated {@code blob:} URL
47       * @param object the {@link Blob} (or {@code File}) the URL refers to
48       * @param owningPage the page that created the URL
49       */
50      public void put(final String blobUrl, final Blob object, final Page owningPage) {
51          store_.put(blobUrl, new Entry(object, owningPage));
52      }
53  
54      /**
55       * <a href="https://w3c.github.io/FileAPI/#blob-url-resolve">Resolve a blob URL</a>.
56       *
57       * @param blobUrl the {@code blob:} URL to resolve
58       * @return the stored {@link Blob}, or {@code null} if there is no entry
59       */
60      public Blob resolve(final String blobUrl) {
61          final Entry entry = store_.get(excludeFragment(blobUrl));
62          return entry == null ? null : entry.object_;
63      }
64  
65      /**
66       * Removes an entry.
67       *
68       * @param blobUrl the {@code blob:} URL to remove
69       */
70      public void remove(final String blobUrl) {
71          store_.remove(excludeFragment(blobUrl));
72      }
73  
74      /**
75       * See <a href="https://w3c.github.io/FileAPI/#lifeTime">Lifetime of blob URLs</a>.
76       *
77       * @param owningPage the unloading page
78       */
79      void removeForPage(final Page owningPage) {
80          store_.values().removeIf(entry -> entry.owningPage_ == owningPage);
81      }
82  
83      /**
84       * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
85       *
86       * Removes all entries.
87       */
88      public void clear() {
89          store_.clear();
90      }
91  
92      private static String excludeFragment(final String blobUrl) {
93          final int fragmentIndex = blobUrl.indexOf('#');
94          return fragmentIndex >= 0 ? blobUrl.substring(0, fragmentIndex) : blobUrl;
95      }
96  }