Skip to content

Commit 2afbe57

Browse files
committed
Teach PlaygroundJS how to deal with base64 encoding
1 parent 1a600fa commit 2afbe57

3 files changed

Lines changed: 85 additions & 0 deletions

File tree

src/playground/bindings/global_callbacks.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
#include "bindings/global_callbacks.h"
66

7+
#include <openssl/bio.h>
8+
#include <openssl/evp.h>
9+
710
#include "base/file_path.h"
811
#include "base/file_search.h"
912
#include "base/logging.h"
@@ -50,6 +53,81 @@ void AddEventListenerCallback(const v8::FunctionCallbackInfo<v8::Value>& argumen
5053
global->AddEventListener(toString(arguments[0]), v8::Local<v8::Function>::Cast(arguments[1]));
5154
}
5255

56+
static std::string Base64Transform(const std::string& input, bool encode) {
57+
static std::vector<char> buffer;
58+
59+
BIO* base64 = BIO_new(BIO_f_base64());
60+
BIO* bio = BIO_new(BIO_s_mem());
61+
62+
BIO_set_flags(base64, BIO_FLAGS_BASE64_NO_NL);
63+
BIO_push(base64, bio);
64+
65+
int length = 0;
66+
67+
if (encode) {
68+
buffer.resize(4 + (size_t)(input.size() * 1.5)); // +50% for encoding overhead
69+
70+
int ret = BIO_write(base64, input.data(), input.length());
71+
BIO_flush(base64);
72+
73+
if (ret > 0)
74+
length = BIO_read(bio, buffer.data(), buffer.size());
75+
76+
} else {
77+
buffer.resize((size_t)(input.size() * 0.8)); // data will shrink by ~33%.
78+
79+
int ret = BIO_write(bio, input.data(), input.length());
80+
BIO_flush(bio);
81+
82+
if (ret > 0)
83+
length = BIO_read(base64, buffer.data(), buffer.size());
84+
}
85+
86+
BIO_free(base64);
87+
BIO_free_all(bio);
88+
89+
if (length <= 0)
90+
return std::string();
91+
92+
return std::string(buffer.data(), buffer.data() + length);
93+
}
94+
95+
// string atob(string data);
96+
void Base64DecodeCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
97+
if (arguments.Length() < 1) {
98+
ThrowException("unable to execute atob(): 1 argument required, none provided.");
99+
return;
100+
}
101+
102+
if (!arguments[0]->IsString()) {
103+
ThrowException("unable to execute atob(): expected a string for argument 1.");
104+
return;
105+
}
106+
107+
const std::string encoded = toString(arguments[0]);
108+
const std::string plaintext = Base64Transform(encoded, /* encode= */ false);
109+
110+
arguments.GetReturnValue().Set(v8String(plaintext));
111+
}
112+
113+
// string btoa(string data);
114+
void Base64EncodeCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
115+
if (arguments.Length() < 1) {
116+
ThrowException("unable to execute atob(): 1 argument required, none provided.");
117+
return;
118+
}
119+
120+
if (!arguments[0]->IsString()) {
121+
ThrowException("unable to execute atob(): expected a string for argument 1.");
122+
return;
123+
}
124+
125+
const std::string plaintext = toString(arguments[0]);
126+
const std::string encoded = Base64Transform(plaintext, /* encode= */ true);
127+
128+
arguments.GetReturnValue().Set(v8String(encoded));
129+
}
130+
53131
// void clearModuleCache(string prefix);
54132
void ClearModuleCacheCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments) {
55133
auto runtime = Runtime::FromIsolate(arguments.GetIsolate());

src/playground/bindings/global_callbacks.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class Value;
1313
namespace bindings {
1414

1515
void AddEventListenerCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
16+
void Base64DecodeCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
17+
void Base64EncodeCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
1618
void ClearModuleCacheCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
1719
void DispatchEventCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);
1820
void ExecCallback(const v8::FunctionCallbackInfo<v8::Value>& arguments);

src/playground/bindings/global_scope.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ void GlobalScope::InstallPrototypes(v8::Local<v8::ObjectTemplate> global) {
6767
InstallFunction(global, "wait", WaitCallback);
6868
InstallFunction(global, "exec", ExecCallback);
6969

70+
// JavaScript methods for converting a string to base64, and vice versa. Names based on:
71+
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob
72+
InstallFunction(global, "atob", Base64DecodeCallback);
73+
InstallFunction(global, "btoa", Base64EncodeCallback);
74+
7075
// Fast-path since idle checks generally are expensive.
7176
InstallFunction(global, "isPlayerMinimized", IsPlayerMinimizedCallback);
7277

0 commit comments

Comments
 (0)