1 /* 2 * ShinyDisconnectNotifier.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.studio.client.shiny; 16 17 import org.rstudio.core.client.Debug; 18 import org.rstudio.core.client.StringUtil; 19 20 public class ShinyDisconnectNotifier 21 { 22 public interface ShinyDisconnectSource 23 { getShinyUrl()24 public String getShinyUrl(); onShinyDisconnect()25 public void onShinyDisconnect(); 26 } 27 ShinyDisconnectNotifier(ShinyDisconnectSource source)28 public ShinyDisconnectNotifier(ShinyDisconnectSource source) 29 { 30 source_ = source; 31 suppressUrl_ = null; 32 initializeEvents(); 33 } 34 35 /** 36 * Begins suppressing disconnect notifications from the current URL. 37 */ suppress()38 public void suppress() 39 { 40 if (!StringUtil.isNullOrEmpty(suppressUrl_)) 41 { 42 // should never happen in practice; if it does the safest behavior is 43 // to respect the new suppress URL and discard the old one. warn that 44 // we're doing this. 45 Debug.logWarning("Replacing old Shiny disconnect suppress URL: " + suppressUrl_); 46 } 47 suppressUrl_ = source_.getShinyUrl(); 48 } 49 50 /** 51 * Ends suppression of disconnect notifications. 52 */ unsuppress()53 public void unsuppress() 54 { 55 suppressUrl_ = null; 56 } 57 initializeEvents()58 private native void initializeEvents() /*-{ 59 60 var self = this; 61 var callback = $entry(function(event) { 62 63 if (typeof event.data !== "string") 64 return; 65 66 self.@org.rstudio.studio.client.shiny.ShinyDisconnectNotifier::onMessage(*)( 67 event.data, 68 event.origin, 69 event.target.name 70 ); 71 72 }); 73 74 $wnd.addEventListener("message", callback, true); 75 76 }-*/; 77 onMessage(String data, String origin, String name)78 private void onMessage(String data, String origin, String name) 79 { 80 // check to see if this is a 'disconnected' message 81 if (!StringUtil.equals(data, "disconnected")) 82 return; 83 84 // check to see if the message originated from the same origin 85 String url = source_.getShinyUrl(); 86 if (!url.startsWith(origin)) 87 return; 88 89 // if we were suppressing disconnect notifications from this URL, 90 // consume this disconnection and resume 91 if (StringUtil.equals(url, suppressUrl_)) 92 { 93 unsuppress(); 94 return; 95 } 96 97 // respond to Shiny disconnect 98 source_.onShinyDisconnect(); 99 } 100 101 private final ShinyDisconnectSource source_; 102 private String suppressUrl_; 103 } 104