Skip to content

Commit 15cd6a0

Browse files
authored
indexeddb: add checkBlobSupport() (#8804)
Add support to the indexeddb adapter for browsers which do not support storing blobs directly in IndexedDB. In current webkit, this error is seen as: > Error [DataCloneError]: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported. Closes #8803
1 parent 4a05b53 commit 15cd6a0

File tree

9 files changed

+93
-14
lines changed

9 files changed

+93
-14
lines changed

packages/node_modules/pouchdb-adapter-idb/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515

1616
import idbBulkDocs from './bulkDocs';
1717
import idbAllDocs from './allDocs';
18-
import checkBlobSupport from './blobSupport';
18+
import { checkBlobSupport } from 'pouchdb-adapter-utils';
1919
import countDocs from './countDocs';
2020
import {
2121
MISSING_DOC,
@@ -783,7 +783,7 @@ function init(api, opts, callback) {
783783
//
784784
if (!blobSupportPromise) {
785785
// make sure blob support is only checked once
786-
blobSupportPromise = checkBlobSupport(txn);
786+
blobSupportPromise = checkBlobSupport(txn, DETECT_BLOB_SUPPORT_STORE, 'key');
787787
}
788788

789789
blobSupportPromise.then(function (val) {

packages/node_modules/pouchdb-adapter-indexeddb/src/allDocs.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ export default function (txn, metadata, opts, callback) {
134134
}
135135
if (opts.attachments && docData._attachments) {
136136
for (var name in docData._attachments) {
137-
processing.push(processAttachment(name, doc, row.doc, opts.binary));
137+
processing.push(processAttachment(name, doc, row.doc, opts.binary,
138+
metadata.idb_attachment_format));
138139
}
139140
}
140141
}

packages/node_modules/pouchdb-adapter-indexeddb/src/bulkDocs.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import {
1010
} from 'pouchdb-errors';
1111

1212
import {
13-
binaryStringToBlobOrBuffer as binStringToBlobOrBuffer
13+
atob,
14+
binaryStringToBlobOrBuffer as binStringToBlobOrBuffer,
15+
blobOrBufferToBase64 as blufferToBase64,
1416
} from 'pouchdb-binary-utils';
1517

1618
import { parseDoc } from 'pouchdb-adapter-utils';
@@ -322,9 +324,24 @@ export default function (api, req, opts, metadata, dbOpts, idbChanges, callback)
322324
} catch (e) {
323325
return Promise.reject(createError(BAD_ARG, 'Attachment is not a valid base64 string'));
324326
}
325-
attachment.data = binStringToBlobOrBuffer(binData, attachment.content_type);
327+
if (metadata.idb_attachment_format === 'binary') {
328+
attachment.data = binStringToBlobOrBuffer(binData, attachment.content_type);
329+
}
326330
} else {
327331
binData = attachment.data;
332+
if (metadata.idb_attachment_format === 'base64') {
333+
// TODO could run these in parallel, if we cared
334+
return new Promise(resolve => {
335+
blufferToBase64(attachment.data, function (b64) {
336+
attachment.data = b64;
337+
md5(binData, function (result) {
338+
attachment.digest = 'md5-' + result;
339+
attachment.length = binData.size || binData.length || 0;
340+
resolve(attachment);
341+
});
342+
});
343+
});
344+
}
328345
}
329346

330347
return new Promise(function (resolve) {

packages/node_modules/pouchdb-adapter-indexeddb/src/changes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default function (txn, idbChanges, api, dbOpts, opts) {
7272
if (opts.include_docs && opts.attachments && doc.data._attachments) {
7373
var promises = [];
7474
for (var name in doc.data._attachments) {
75-
var p = processAttachment(name, doc, change.doc, opts.binary);
75+
var p = processAttachment(name, doc, change.doc, opts.binary, api.blobSupport);
7676
// We add the processing promise to 2 arrays, one tracks all
7777
// the promises needed before we fire onChange, the other
7878
// ensure we process all attachments before onComplete

packages/node_modules/pouchdb-adapter-indexeddb/src/getAttachment.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
'use strict';
22

3-
import { btoa, readAsBinaryString } from 'pouchdb-binary-utils';
3+
import {
4+
base64StringToBlobOrBuffer as b64StringToBluffer,
5+
btoa,
6+
readAsBinaryString,
7+
} from 'pouchdb-binary-utils';
48

59
export default function getAttachment(docId, attachId, attachment, opts, cb) {
610
const doc = opts.metadata;
711
const data = doc.attachments[attachment.digest].data;
812

13+
if (typeof data === 'string') {
14+
if (opts.binary) {
15+
cb(null, b64StringToBluffer(data, attachment.content_type));
16+
} else {
17+
cb(null, data);
18+
}
19+
return;
20+
}
21+
922
if (opts.binary) {
1023
return cb(null, data);
1124
} else {

packages/node_modules/pouchdb-adapter-indexeddb/src/setup.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import { uuid } from 'pouchdb-utils';
44

5+
import { checkBlobSupport } from 'pouchdb-adapter-utils';
6+
57
import { META_STORE, DOC_STORE, rawIndexFields, naturalIndexName, correctIndexFields } from './util';
68

79
//
@@ -193,7 +195,21 @@ function openDatabase(openDatabases, api, opts, resolve, reject) {
193195
metadata.db_uuid = uuid();
194196
}
195197

196-
if (changed) {
198+
if (!('idb_attachment_format' in metadata)) {
199+
// There will be trouble if any browser _stops_ supporting blobs.
200+
201+
const createBlobDoc = blob => ({ id:'blob-support', blob });
202+
203+
checkBlobSupport(txn, META_STORE, createBlobDoc).then(blobSupport => {
204+
// Unfortunate that we have to track this in both the metadata and on
205+
// api, but sometimes we have access to one, sometimes the other (and
206+
// sometimes both). We could change function signatures in index.js
207+
// to make this consistent.
208+
api.blobSupport = metadata.idb_attachment_format = blobSupport ? 'binary' : 'base64';
209+
metaStore.put(metadata);
210+
});
211+
} else if (changed) {
212+
api.blobSupport = metadata.idb_attachment_format;
197213
metaStore.put(metadata);
198214
}
199215
};

packages/node_modules/pouchdb-adapter-indexeddb/src/util.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
'use strict';
22

33
import { createError, IDB_ERROR } from 'pouchdb-errors';
4-
import { btoa, readAsBinaryString } from 'pouchdb-binary-utils';
4+
import {
5+
base64StringToBlobOrBuffer as b64StringToBluffer,
6+
btoa,
7+
readAsBinaryString,
8+
} from 'pouchdb-binary-utils';
59
import { sanitise } from './rewrite';
610

711
var DOC_STORE = 'docs';
@@ -17,10 +21,22 @@ function idbError(callback) {
1721
};
1822
}
1923

20-
function processAttachment(name, src, doc, isBinary) {
24+
function processAttachment(name, src, doc, isBinary, attachmentFormat) {
2125

2226
delete doc._attachments[name].stub;
2327

28+
if (attachmentFormat === 'base64') {
29+
if (isBinary) {
30+
const att = src.attachments[doc._attachments[name].digest];
31+
doc._attachments[name].data = b64StringToBluffer(att.data, att.content_type);
32+
} else {
33+
doc._attachments[name].data =
34+
src.attachments[doc._attachments[name].digest].data;
35+
}
36+
delete doc._attachments[name].length;
37+
return Promise.resolve();
38+
}
39+
2440
if (isBinary) {
2541
doc._attachments[name].data =
2642
src.attachments[doc._attachments[name].digest].data;

packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js renamed to packages/node_modules/pouchdb-adapter-utils/src/checkBlobSupport.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { blob as createBlob } from 'pouchdb-binary-utils';
2-
import { DETECT_BLOB_SUPPORT_STORE } from './constants';
32

43
//
54
// Blobs are not supported in all versions of IndexedDB, notably
6-
// Chrome <37 and Android <5. In those versions, storing a blob will throw.
5+
// Chrome <37, Android <5 and (some?) webkit-based browsers.
6+
// In those versions, storing a blob will throw.
7+
//
8+
// Example Webkit error:
9+
// > DataCloneError: Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.
710
//
811
// Various other blob bugs exist in Chrome v37-42 (inclusive).
912
// Detecting them is expensive and confusing to users, and Chrome 37-42
@@ -13,10 +16,21 @@ import { DETECT_BLOB_SUPPORT_STORE } from './constants';
1316
// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
1417
// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
1518
//
16-
function checkBlobSupport(txn) {
19+
function checkBlobSupport(txn, store, docIdOrCreateDoc) {
1720
return new Promise(function (resolve) {
1821
var blob = createBlob(['']);
19-
var req = txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');
22+
23+
let req;
24+
if (typeof docIdOrCreateDoc === 'function') {
25+
// Store may require a specific key path, in which case we can't store the
26+
// blob directly in the store.
27+
const createDoc = docIdOrCreateDoc;
28+
const doc = createDoc(blob);
29+
req = txn.objectStore(store).put(doc);
30+
} else {
31+
const docId = docIdOrCreateDoc;
32+
req = txn.objectStore(store).put(blob, docId);
33+
}
2034

2135
req.onsuccess = function () {
2236
var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);

packages/node_modules/pouchdb-adapter-utils/src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import allDocsKeysQuery from './allDocsKeysQuery';
2+
import checkBlobSupport from './checkBlobSupport';
23
import parseDoc from './parseDoc';
34
import {
45
invalidIdError,
@@ -12,6 +13,7 @@ import updateDoc from './updateDoc';
1213

1314
export {
1415
allDocsKeysQuery,
16+
checkBlobSupport,
1517
invalidIdError,
1618
isDeleted,
1719
isLocalId,

0 commit comments

Comments
 (0)