Skip to content

Commit

Permalink
Merge pull request #1932 from GoogleChrome/bg-sync-array-buffer
Browse files Browse the repository at this point in the history
Bg sync array buffer
  • Loading branch information
philipwalton committed Mar 5, 2019
2 parents bd5f411 + b51b6f8 commit ea248e7
Show file tree
Hide file tree
Showing 19 changed files with 443 additions and 453 deletions.
27 changes: 15 additions & 12 deletions .eslintrc.js
Expand Up @@ -24,11 +24,11 @@ module.exports = {
'jsdoc/check-types': 2,
'jsdoc/newline-after-description': 2,
'max-len': [2, {
code: 80,
tabWidth: 2,
ignoreComments: true,
ignoreUrls: true,
ignorePattern: '^\\s*import',
'code': 80,
'tabWidth': 2,
'ignoreComments': true,
'ignorePattern': '^\\s*import',
'ignoreUrls': true,
}],
},
plugins: [
Expand All @@ -40,6 +40,9 @@ module.exports = {
env: {
mocha: true,
},
globals: {
expectError: false,
},
rules: {
'max-len': 0,
'require-jsdoc': 0,
Expand Down Expand Up @@ -69,7 +72,7 @@ module.exports = {
}, {
files: [
'gulp-tasks/**/*.js',
'infra/**/*.js'
'infra/**/*.js',
],
rules: {
'valid-jsdoc': 0,
Expand All @@ -80,8 +83,8 @@ module.exports = {
'infra/testing/**/*',
],
env: {
'mocha': true
}
'mocha': true,
},
}, {
files: [
'test/workbox-build/static/**/*.js',
Expand All @@ -97,8 +100,8 @@ module.exports = {
rules: {
'max-len': 0,
},
}
, {
},
{
files: [
'packages/workbox-sw/**/*',
],
Expand All @@ -123,7 +126,7 @@ module.exports = {
'header',
],
rules: {
'header/header': [2, 'block', {pattern: 'Copyright \\d{4} Google LLC'}]
}
'header/header': [2, 'block', {pattern: 'Copyright \\d{4} Google LLC'}],
},
}],
};
100 changes: 100 additions & 0 deletions infra/testing/sw-env-mocks/Body.js
@@ -0,0 +1,100 @@
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/

const Blob = require('./Blob');


// https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
// Note, the original post uses Uint16Array and allocates 2 bytes per character
// but native implementations of methods that convert strings to ArrayBuffers
// don't do that for ASCII strings, so we'll stick to str.length and only use
// ASCII in tests until we switch our test infrastructure to run in real
// browsers.
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}

function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}

// https://fetch.spec.whatwg.org/#body
class Body {
async arrayBuffer() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;

if (typeof this._body === 'undefined') {
return new ArrayBuffer();
}
if (typeof this._body === 'string') {
return str2ab(this._body);
}
if (this._body instanceof ArrayBuffer) {
return this._body;
}
if (this._body instanceof Blob) {
// `_text` is non-standard, but easier for the mocks.
return str2ab(this._body._text);
}
}
}

async blob() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;

if (typeof this._body === 'undefined') {
return new Blob();
}
if (typeof this._body === 'string') {
return new Blob([this._body]);
}
if (this._body instanceof ArrayBuffer) {
// `_text` is non-standard, but easier for the mocks.
return new Blob([ab2str(this._body)]);
}
if (this._body instanceof Blob) {
return this._body;
}
}
}

async text() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;

if (typeof this._body === 'undefined') {
return new '';
}
if (typeof this._body === 'string') {
return this._body;
}
if (this._body instanceof ArrayBuffer) {
return ab2str(this._body);
}
if (this._body instanceof Blob) {
// `_text` is non-standard, but easier for the mocks.
return this._body._text;
}
}
}
}

module.exports = Body;
34 changes: 9 additions & 25 deletions infra/testing/sw-env-mocks/Request.js
Expand Up @@ -6,13 +6,16 @@
https://opensource.org/licenses/MIT.
*/

const Blob = require('./Blob');
const Body = require('./Body');
const Headers = require('./Headers');


// Stub missing/broken Request API methods in `service-worker-mock`.
// https://fetch.spec.whatwg.org/#request-class
class Request {
class Request extends Body {
constructor(urlOrRequest, options = {}) {
super();

let url = urlOrRequest;
if (urlOrRequest instanceof Request) {
url = urlOrRequest.url;
Expand All @@ -29,15 +32,15 @@ class Request {
throw new TypeError(`Invalid url: ${urlOrRequest}`);
}

this.url = url;
this.url = new URL(url, location).href;
this.method = options.method || 'GET';
this.mode = options.mode || 'cors';
// See https://fetch.spec.whatwg.org/#concept-request-credentials-mode
this.credentials = options.credentials || (this.mode === 'navigate' ?
'include' : 'omit');
this.credentials = options.credentials ||
(this.mode === 'navigate' ? 'include' : 'omit');
this.headers = new Headers(options.headers);

this._body = new Blob('body' in options ? [options.body] : []);
this._body = options.body;
}

clone() {
Expand All @@ -48,25 +51,6 @@ class Request {
return new Request(this.url, Object.assign({body: this._body}, this));
}
}

async blob() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;
return this._body;
}
}

async text() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;
// Limitation: this assumes the stored Blob is text-based.
return this._body._text;
}
}
}

module.exports = Request;
29 changes: 7 additions & 22 deletions infra/testing/sw-env-mocks/Response.js
Expand Up @@ -6,14 +6,18 @@
https://opensource.org/licenses/MIT.
*/

const Blob = require('./Blob');
const Body = require('./Body');
const Headers = require('./Headers');


// Stub missing/broken Response API methods in `service-worker-mock`.
// https://fetch.spec.whatwg.org/#response-class
class Response {
class Response extends Body {
constructor(body, options = {}) {
this._body = new Blob([body]);
super();

this._body = body;

this.status = typeof options.status === 'number' ? options.status : 200;
this.ok = this.status >= 200 && this.status < 300;
this.statusText = options.statusText || 'OK';
Expand All @@ -32,25 +36,6 @@ class Response {
return new Response(this._body, this);
}
}

async blob() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;
return this._body;
}
}

async text() {
if (this.bodyUsed) {
throw new TypeError('Already read');
} else {
this.bodyUsed = true;
// Limitionation: this assumes the stored Blob is text-based.
return this._body._text;
}
}
}

module.exports = Response;
9 changes: 6 additions & 3 deletions packages/workbox-background-sync/Queue.mjs
Expand Up @@ -10,11 +10,14 @@ import {WorkboxError} from 'workbox-core/_private/WorkboxError.mjs';
import {logger} from 'workbox-core/_private/logger.mjs';
import {assert} from 'workbox-core/_private/assert.mjs';
import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.mjs';
import {QueueStore} from './models/QueueStore.mjs';
import StorableRequest from './models/StorableRequest.mjs';
import {TAG_PREFIX, MAX_RETENTION_TIME} from './utils/constants.mjs';
import {QueueStore} from './lib/QueueStore.mjs';
import {StorableRequest} from './lib/StorableRequest.mjs';
import './_version.mjs';


const TAG_PREFIX = 'workbox-background-sync';
const MAX_RETENTION_TIME = 60 * 24 * 7; // 7 days in minutes

const queueNames = new Set();

/**
Expand Down
Expand Up @@ -8,13 +8,14 @@

import {assert} from 'workbox-core/_private/assert.mjs';
import {DBWrapper} from 'workbox-core/_private/DBWrapper.mjs';
import {migrateDb} from 'workbox-core/_private/migrateDb.mjs';
import {DB_NAME, DB_VERSION, OBJECT_STORE_NAME, INDEXED_PROP}
from '../utils/constants.mjs';

import '../_version.mjs';


const DB_VERSION = 3;
const DB_NAME = 'workbox-background-sync';
const OBJECT_STORE_NAME = 'requests';
const INDEXED_PROP = 'queueName';

/**
* A class to manage storing requests from a Queue in IndexedbDB,
* indexed by their queue name for easier access.
Expand Down Expand Up @@ -155,53 +156,15 @@ export class QueueStore {
*/
_upgradeDb(event) {
const db = event.target.result;
const txn = event.target.transaction;
let oldEntries = [];

migrateDb(event, {
v1: (next) => {
// When migrating from version 0, this will not exist.
if (db.objectStoreNames.contains(OBJECT_STORE_NAME)) {
// Get any existing entries in the v1 requests store
// and then delete it.
const objStore = txn.objectStore(OBJECT_STORE_NAME);
objStore.openCursor().onsuccess = ({target}) => {
const cursor = target.result;
if (cursor) {
oldEntries.push(cursor.value);
cursor.continue();
} else {
db.deleteObjectStore(OBJECT_STORE_NAME);
next();
}
};
} else {
next();
}
},
v2: (next) => {
// Creates v2 of the requests store and adds back any existing
// entries in the new format.
const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
autoIncrement: true,
keyPath: 'id',
});
objStore.createIndex(INDEXED_PROP, INDEXED_PROP, {unique: false});

if (oldEntries.length) {
for (const {queueName, storableRequest} of oldEntries) {
// Move the timestamp from `storableRequest` to the top level.
const timestamp = storableRequest.timestamp;

// Reformat the storable request data
const requestData = Object.assign(
storableRequest.requestInit, {url: storableRequest.url});

objStore.add({queueName, timestamp, requestData});
}
}
next();
},

if (event.oldVersion > 0 && event.oldVersion < DB_VERSION) {
db.deleteObjectStore(OBJECT_STORE_NAME);
}

const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
autoIncrement: true,
keyPath: 'id',
});
objStore.createIndex(INDEXED_PROP, INDEXED_PROP, {unique: false});
}
}

0 comments on commit ea248e7

Please sign in to comment.