Skip to content
File

Blob: src/workerd/api/blob.h

cpp175 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#pragma once
6 
7#include <workerd/io/compatibility-date.capnp.h>
8#include <workerd/jsg/jsg.h>
9 
10namespace workerd::api {
11 
12class ReadableStream;
13class File;
14 
15// An implementation of the Web Platform Standard Blob API
16class Blob: public jsg::Object {
17 public:
18 // Creates an empty Blob
19 Blob(kj::String type);
20 Blob(jsg::Lock& js, jsg::JsBufferSource data, kj::String type);
21 Blob(jsg::Ref<Blob> parent, kj::ArrayPtr<const byte> data, kj::String type);
22 
23 kj::ArrayPtr<const byte> getData() const KJ_LIFETIMEBOUND;
24 
25 // ---------------------------------------------------------------------------
26 // JS API
27 
28 struct Options {
29 jsg::Optional<kj::String> type;
30 jsg::Unimplemented endings;
31 
32 JSG_STRUCT(type, endings);
33 };
34 
35 using BitsValue = kj::OneOf<jsg::JsBufferSource, kj::String, jsg::Ref<Blob>>;
36 using Bits = kj::Array<BitsValue>;
37 
38 static jsg::Ref<Blob> constructor(
39 jsg::Lock& js, jsg::Optional<Bits> bits, jsg::Optional<Options> options);
40 
41 int getSize() const {
42 return data.size();
43 }
44 kj::StringPtr getType() const KJ_LIFETIMEBOUND {
45 return type;
46 }
47 
48 jsg::Ref<Blob> slice(jsg::Lock& js,
49 jsg::Optional<int> start,
50 jsg::Optional<int> end,
51 jsg::Optional<kj::String> type);
52 
53 // Each of the consumption methods (arrayBuffer, bytes, text) create copies of
54 // the Blob's underlying data.
55 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> arrayBuffer(jsg::Lock& js);
56 jsg::Promise<jsg::JsRef<jsg::JsUint8Array>> bytes(jsg::Lock& js);
57 jsg::Promise<jsg::JsRef<jsg::JsString>> text(jsg::Lock& js);
58 jsg::Ref<ReadableStream> stream(jsg::Lock& js);
59 
60 JSG_RESOURCE_TYPE(Blob, CompatibilityFlags::Reader flags) {
61 if (flags.getJsgPropertyOnPrototypeTemplate()) {
62 JSG_READONLY_PROTOTYPE_PROPERTY(size, getSize);
63 JSG_READONLY_PROTOTYPE_PROPERTY(type, getType);
64 } else {
65 JSG_READONLY_INSTANCE_PROPERTY(size, getSize);
66 JSG_READONLY_INSTANCE_PROPERTY(type, getType);
67 }
68 
69 JSG_METHOD(slice);
70 JSG_METHOD(arrayBuffer);
71 JSG_METHOD(bytes);
72 JSG_METHOD(text);
73 JSG_METHOD(stream);
74 
75 JSG_TS_OVERRIDE({
76 bytes(): Promise<Uint8Array>;
77 arrayBuffer(): Promise<ArrayBuffer>;
78 });
79 }
80 
81 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
82 KJ_SWITCH_ONEOF(ownData) {
83 KJ_CASE_ONEOF(_, Empty) {}
84 KJ_CASE_ONEOF(data, jsg::JsRef<jsg::JsBufferSource>) {
85 tracker.trackField("ownData", data);
86 }
87 KJ_CASE_ONEOF(data, jsg::Ref<Blob>) {
88 tracker.trackField("ownData", data);
89 }
90 }
91 tracker.trackField("type", type);
92 }
93 
94 private:
95 // Sentinel type for the case where the Blob is just ... empty.
96 struct Empty {};
97 
98 kj::OneOf<Empty, jsg::JsRef<jsg::JsBufferSource>, jsg::Ref<Blob>> ownData;
99 kj::ArrayPtr<const byte> data;
100 kj::String type;
101 
102 void visitForGc(jsg::GcVisitor& visitor) {
103 KJ_SWITCH_ONEOF(ownData) {
104 KJ_CASE_ONEOF(_, Empty) {}
105 KJ_CASE_ONEOF(b, jsg::JsRef<jsg::JsBufferSource>) {
106 visitor.visit(b);
107 }
108 KJ_CASE_ONEOF(b, jsg::Ref<Blob>) {
109 visitor.visit(b);
110 }
111 }
112 }
113 
114 // this could just be "friend File;", but clang-cl wants to see the qualified name here.
115 friend class ::workerd::api::File;
116};
117 
118// An implementation of the Web Platform Standard File API
119class File: public Blob {
120 public:
121 // Creates a zero-length File
122 File(kj::String name, kj::String type, double lastModified);
123 File(jsg::Lock& js,
124 jsg::JsBufferSource data,
125 kj::String name,
126 kj::String type,
127 double lastModified);
128 File(jsg::Ref<Blob> parent,
129 kj::ArrayPtr<const byte> data,
130 kj::String name,
131 kj::String type,
132 double lastModified);
133 
134 struct Options {
135 jsg::Optional<kj::String> type;
136 jsg::Optional<double> lastModified;
137 jsg::Unimplemented endings;
138 
139 JSG_STRUCT(type, lastModified, endings);
140 };
141 
142 static jsg::Ref<File> constructor(
143 jsg::Lock& js, jsg::Optional<Bits> bits, kj::String name, jsg::Optional<Options> options);
144 
145 kj::StringPtr getName() {
146 return name;
147 }
148 double getLastModified() {
149 return lastModified;
150 }
151 
152 JSG_RESOURCE_TYPE(File, CompatibilityFlags::Reader flags) {
153 JSG_INHERIT(Blob);
154 if (flags.getJsgPropertyOnPrototypeTemplate()) {
155 JSG_READONLY_PROTOTYPE_PROPERTY(name, getName);
156 JSG_READONLY_PROTOTYPE_PROPERTY(lastModified, getLastModified);
157 } else {
158 JSG_READONLY_INSTANCE_PROPERTY(name, getName);
159 JSG_READONLY_INSTANCE_PROPERTY(lastModified, getLastModified);
160 }
161 }
162 
163 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
164 tracker.trackField("name", name);
165 }
166 
167 private:
168 kj::String name;
169 double lastModified;
170};
171 
172#define EW_BLOB_ISOLATE_TYPES api::Blob, api::Blob::Options, api::File, api::File::Options
173 
174} // namespace workerd::api