Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/LiveDevelopment/BrowserScripts/LivePreviewTransportRemote.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,37 @@
}
}

function createLRU(max = 100) {
const map = new Map();

return {
set: function (key, value = true) {
if (map.has(key)) {
map.delete(key); // refresh order
}
map.set(key, value);
if (map.size > max) {
const oldestKey = map.keys().next().value;
map.delete(oldestKey);
}
},
has: function (key) {
if (!map.has(key)) {
return false;
}
const val = map.get(key);
map.delete(key); // refresh order
map.set(key, val);
return true;
},
size: function() {
return map.size;
}
};
}

const processedMessageIDs = createLRU(1000);

const clientID = "" + Math.round( Math.random()*1000000000);

const worker = new Worker(TRANSPORT_CONFIG.LIVE_DEV_REMOTE_WORKER_SCRIPTS_FILE_NAME);
Expand Down Expand Up @@ -218,7 +249,12 @@
case 'MESSAGE_FROM_PHOENIX':
if (self._callbacks && self._callbacks.message) {
const clientIDs = event.data.clientIDs,
message = event.data.message;
message = event.data.message,
messageID = event.data.messageID;
if(messageID && processedMessageIDs.has(messageID)){
return; // we have already processed this message.
}
processedMessageIDs.set(messageID, true);
if(clientIDs.includes(clientID) || clientIDs.length === 0){
// clientIDs.length = 0 if the message is intended for all clients
self._callbacks.message(message);
Expand Down Expand Up @@ -264,6 +300,7 @@
_postLivePreviewMessage({
type: 'BROWSER_MESSAGE',
clientID: clientID,
messageID: crypto.randomUUID(),
message: msgStr
});
},
Expand Down
10 changes: 5 additions & 5 deletions src/LiveDevelopment/BrowserScripts/pageLoaderWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*/


let _livePreviewNavigationChannel;
let _livePreviewBroadcastChannel;
let _livePreviewWebSocket, _livePreviewWebSocketOpen = false;
let livePreviewDebugModeEnabled = false;
function _debugLog(...args) {
Expand Down Expand Up @@ -106,8 +106,8 @@ let messageQueue = [];
function _sendMessage(message) {
if(_livePreviewWebSocket && _livePreviewWebSocketOpen) {
_livePreviewWebSocket.send(mergeMetadataAndArrayBuffer(message));
} else if(_livePreviewNavigationChannel){
_livePreviewNavigationChannel.postMessage(message);
} else if(_livePreviewBroadcastChannel){
_livePreviewBroadcastChannel.postMessage(message);
} else {
livePreviewDebugModeEnabled && console.warn("No Channels available for live preview worker messaging," +
" queueing request, waiting for channel..");
Expand Down Expand Up @@ -138,8 +138,8 @@ function _setupHearbeatMessenger(clientID) {
}

function _setupBroadcastChannel(broadcastChannel, clientID) {
_livePreviewNavigationChannel=new BroadcastChannel(broadcastChannel);
_livePreviewNavigationChannel.onmessage = (event) => {
_livePreviewBroadcastChannel=new BroadcastChannel(broadcastChannel);
_livePreviewBroadcastChannel.onmessage = (event) => {
const type = event.data.type;
switch (type) {
case 'TAB_ONLINE': break; // do nothing. This is a loopback message from another live preview tab
Expand Down
24 changes: 21 additions & 3 deletions src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@ define(function (require, exports, module) {
}
}

const processedMessageIDs = new Phoenix.libs.LRUCache({
max: 1000
// we dont need to set a ttl here as message ids are unique throughout lifetime. And old ids will
// start getting evited from the cache. the message ids are only an issue within a fraction of a seconds when
// a series of messages are sent in quick succession. Eg. user click on a div and there are 3 tabs and due to
// the reflection bug, we almost immediately get 3 messages with the same id. So that will be in this cache
// for a fraction of a second. so a size of 1000 should be more than enough.
});

/**
* @private
* Handles a message received from the remote protocol handler via the transport.
Expand All @@ -203,12 +212,21 @@ define(function (require, exports, module) {
* TODO: we should probably have a way of returning the results from all clients, not just the first?
*
* @param {number} clientId ID of the client that sent the message
* @param {string} msg The message that was sent, in JSON string format
* @param {string} msgStr The message that was sent, in JSON string format
* @param {string} messageID The messageID uniquely identifying a message. in browsers, since we use broadcast
* channels, we get reflections echoes when there are multiple tabs open. Ideally those reflections need to
* be fixed, but that was too complex to fix, so we just reply on the message id to guarantee that a message is
* only processed once and not from any reflections.
*/
function _receive(clientId, msgStr) {
function _receive(clientId, msgStr, messageID) {
var msg = JSON.parse(msgStr),
event = msg.method || "event",
deferred;
if(messageID && processedMessageIDs.has(messageID)){
return; // this message is already processed.
} else if (messageID) {
processedMessageIDs.set(messageID, true);
}
if (msg.livePreviewEditEnabled) {
LivePreviewEdit.handleLivePreviewEditOperation(msg);
}
Expand Down Expand Up @@ -305,7 +323,7 @@ define(function (require, exports, module) {
_connect(msg[0], msg[1]);
})
.on("message.livedev", function (event, msg) {
_receive(msg[0], msg[1]);
_receive(msg[0], msg[1], msg[2]);
})
.on("close.livedev", function (event, msg) {
_close(msg[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ define(function (require, exports, module) {
_transportBridge && _transportBridge.messageToLivePreviewTabs({
type: 'MESSAGE_FROM_PHOENIX',
clientIDs,
message
message,
messageID: crypto.randomUUID()
});
transportMessagesSendCount ++;
transportMessagesSendSizeB = transportMessagesSendSizeB + message.length;
Expand All @@ -135,7 +136,7 @@ define(function (require, exports, module) {
window.logger.livePreview.log(
"Live Preview: Phoenix received event from Browser preview tab/iframe: ", event.data);
const message = event.data.message.message || "";
exports.trigger('message', [event.data.message.clientID, message]);
exports.trigger('message', [event.data.message.clientID, message, event.data.message.messageID]);
transportMessagesRecvSizeB = transportMessagesRecvSizeB + message.length;
transportMessagesRecvCount++;
}
Expand Down
8 changes: 4 additions & 4 deletions src/live-preview-loader.html
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
const LOADER_BROADCAST_ID = `live-preview-loader-${controllingPhoenixInstanceID}`;
const navigatorChannel = new BroadcastChannel(LOADER_BROADCAST_ID);
const LIVE_PREVIEW_MESSENGER_CHANNEL = `live-preview-messenger-${controllingPhoenixInstanceID}`;
const livePreviewChannel = new BroadcastChannel(LIVE_PREVIEW_MESSENGER_CHANNEL);
const phcodeBroadcastMessageChannel = new BroadcastChannel(LIVE_PREVIEW_MESSENGER_CHANNEL);
navigatorChannel.onmessage = (event) => {
_debugLog("Live Preview loader channel: Browser received event from Phoenix: ", JSON.stringify(event.data));
const type = event.data.type;
Expand Down Expand Up @@ -224,7 +224,7 @@
type: 'GET_INITIAL_URL',
pageLoaderID: pageLoaderID
});
livePreviewChannel.onmessage = (event) => {
phcodeBroadcastMessageChannel.onmessage = (event) => {
_debugLog("Live Preview message channel: Browser received event from Phoenix: ", JSON.stringify(event.data));
if(event.data.pageLoaderID && event.data.pageLoaderID !== pageLoaderID){
// this message is not for this page loader window.
Expand All @@ -241,7 +241,7 @@
}

// this is for phoenix to process, pass it on
livePreviewChannel.postMessage({
phcodeBroadcastMessageChannel.postMessage({
pageLoaderID: pageLoaderID,
data: event.data
});
Expand Down Expand Up @@ -322,4 +322,4 @@
sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals allow-pointer-lock allow-downloads">
</iframe>
</body>
</html>
</html>
Loading