Skip to content

Commit

Permalink
Fixes servicerworker/client state syncing (#1074)
Browse files Browse the repository at this point in the history
* Fixes servicerworker/client state syncing

Defines the MessageChannel based utility for communicating window <=> window and window <=> worker.

Adds receiver in indexedDB when operating from a service worker to listen to keyChanged events.
On detection, indexedDB is synced and a response is returned to sender whether the key change was processed. It may be that the key change was already detected. This would return false.

Adds a sender when an indexedDB write operation occurs and a service worker is available. The client will send a keyChanged message to the service worker to process the change and blocks on it. On response, the indexedDB write operation will resolve. The operation will resolve on success or failure. This is a best effort approach. If the service worker fails to process, the write operation should still succeed.

Updates obsoleted APIs in web worker.
  • Loading branch information
bojeil-google committed Aug 2, 2018
1 parent c74c3b9 commit 2dab48d
Show file tree
Hide file tree
Showing 12 changed files with 4,782 additions and 8 deletions.
2,703 changes: 2,703 additions & 0 deletions packages/auth/demo/functions/yarn.lock

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions packages/auth/demo/public/web-worker.js
Expand Up @@ -77,11 +77,11 @@ var runWorkerTests = function(googleIdToken) {
firebase.auth().useDeviceLanguage();
return firebase.auth().signInAnonymously();
})
.then(function(user) {
if (!user.uid) {
.then(function(result) {
if (!result.user.uid) {
throw new Error('signInAnonymously unexpectedly failed!');
}
return user.updateProfile({displayName: expectedDisplayName});
return result.user.updateProfile({displayName: expectedDisplayName});
})
.then(function() {
if (firebase.auth().currentUser.displayName != expectedDisplayName) {
Expand All @@ -95,8 +95,8 @@ var runWorkerTests = function(googleIdToken) {
}
return firebase.auth().createUserWithEmailAndPassword(email, pass);
})
.then(function(user) {
if (user.email != email) {
.then(function(result) {
if (result.user.email != email) {
throw new Error(
'createUserWithEmailAndPassword unexpectedly failed!');
}
Expand All @@ -107,11 +107,11 @@ var runWorkerTests = function(googleIdToken) {
}
return firebase.auth().signInWithEmailAndPassword(email, pass);
})
.then(function(user) {
if (user.email != email) {
.then(function(result) {
if (result.user.email != email) {
throw new Error('signInWithEmailAndPassword unexpectedly failed!');
}
return user.delete();
return result.user.delete();
})
.then(function() {
return firebase.auth().signInWithPopup(provider)
Expand Down
81 changes: 81 additions & 0 deletions packages/auth/src/messagechannel/defines.js
@@ -0,0 +1,81 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview Defines the MessageChannel common utilities and enums.
*/

goog.provide('fireauth.messagechannel.Error');
goog.provide('fireauth.messagechannel.Status');
goog.provide('fireauth.messagechannel.TimeoutDuration');
goog.provide('fireauth.messagechannel.utils');


/**
* Enum for the messagechannel error messages. These errors are not meant to be
* user facing.
* @enum {string}
*/
fireauth.messagechannel.Error = {
CONNECTION_UNAVAILABLE: 'connection_unavailable',
INVALID_RESPONSE: 'invalid_response',
TIMEOUT: 'timeout',
UNKNOWN: 'unknown_error',
UNSUPPORTED_EVENT: 'unsupported_event'
};


/**
* Enum for the message channel request status labels.
* @enum {string}
*/
fireauth.messagechannel.Status = {
ACK: 'ack',
DONE: 'done'
};


/**
* Enum for the timeout durations in milliseconds for different contexts.
* @enum {number}
*/
fireauth.messagechannel.TimeoutDuration = {
ACK: 20,
COMPLETION: 500
};


/**
* @param {?string=} opt_prefix An optional prefix string to prepend to ID.
* @param {?number=} opt_digits An optional number of digits used for event ID.
* @return {string} The generated event ID used to identify a generic event.
*/
fireauth.messagechannel.utils.generateEventId =
function(opt_prefix, opt_digits) {
// 0, null and undefined will default to 20.
var digits = opt_digits || 20;
return opt_prefix ? opt_prefix : '' +
Math.floor(Math.random() * Math.pow(10, digits)).toString();
};


/**
* @return {?MessageChannel} The initialized MessageChannel instance if
* supported.
*/
fireauth.messagechannel.utils.initializeMessageChannel = function() {
return typeof MessageChannel !== 'undefined' ? new MessageChannel() : null;
};
130 changes: 130 additions & 0 deletions packages/auth/src/messagechannel/postmessager.js
@@ -0,0 +1,130 @@
/**
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview Defines the PostMessager interface needed for the
* `fireauth.messagechannel.Sender`, in addition to 2 types of implementations.
*/

goog.provide('fireauth.messagechannel.PostMessager');
goog.provide('fireauth.messagechannel.WindowPostMessager');
goog.provide('fireauth.messagechannel.WorkerClientPostMessager');


/**
* This is the interface defining the postMessage format of a window which
* takes an additional second parameter for target origin.
*
* @typedef {{
* postMessage: function(*, string, !Array<!Transferable>)
* }}
*/
fireauth.messagechannel.Window;


/**
* This is the interface defining the postMessage format of a worker or
* ServiceWorkerClient, etc. which just takes a message and a list of
* Transferables.
*
* @typedef {{
* postMessage: function(*, !Array<!Transferable>)
* }}
*/
fireauth.messagechannel.WorkerClient;


/**
* Defines a common interface to postMessage data to a specified PostMessager.
* @interface
*/
fireauth.messagechannel.PostMessager = function() {};


/**
* Sends a message to the specified context.
* @param {*} message The message to send.
* @param {!Array<!Transferable>} transfer The list of `Transferable` objects
* that are transferred with the message. The ownsership fo these objects is
* given to the destination side and they are no longer usable on the
* sending side.
*/
fireauth.messagechannel.PostMessager.prototype.postMessage =
function(message, transfer) {};



/**
* Defines the implementation for postMessaging to a window context.
* @param {!fireauth.messagechannel.Window} win The window PostMessager.
* @param {?string=} opt_targetOrigin The target origin.
* @constructor
* @implements {fireauth.messagechannel.PostMessager}
*/
fireauth.messagechannel.WindowPostMessager = function(win, opt_targetOrigin) {
/**
* @const @private {!fireauth.messagechannel.Window} The window PostMessager.
*/
this.win_ = win;
/** @const @private {string} The postMessage target origin. */
this.targetOrigin_ = opt_targetOrigin || '*';
};


/**
* Sends a message to the specified window context.
* @param {*} message The message to send.
* @param {!Array<!Transferable>} transfer The list of `Transferable` objects
* that are transferred with the message. The ownsership fo these objects is
* given to the destination side and they are no longer usable on the
* sending side.
* @override
*/
fireauth.messagechannel.WindowPostMessager.prototype.postMessage =
function(message, transfer) {
this.win_.postMessage(message, this.targetOrigin_, transfer);
};


/**
* Defines the implementation for postMessaging to a worker/client context.
* @param {!fireauth.messagechannel.WorkerClient} worker The worker/client
* PostMessager.
* @constructor
* @implements {fireauth.messagechannel.PostMessager}
*/
fireauth.messagechannel.WorkerClientPostMessager = function(worker) {
/**
* @const @private {!fireauth.messagechannel.WorkerClient} The worker/client
* PostMessager.
*/
this.worker_ = worker;
};


/**
* Sends a message to the specified worker/client context.
* @param {*} message The message to send.
* @param {!Array<!Transferable>} transfer The list of `Transferable` objects
* that are transferred with the message. The ownsership fo these objects is
* given to the destination side and they are no longer usable on the
* sending side.
* @override
*/
fireauth.messagechannel.WorkerClientPostMessager.prototype.postMessage =
function(message, transfer) {
this.worker_.postMessage(message, transfer);
};

0 comments on commit 2dab48d

Please sign in to comment.