1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host;
16
17 import java.util.Set;
18
19 import org.htmlunit.Page;
20 import org.htmlunit.WebWindow;
21 import org.htmlunit.corejs.javascript.Context;
22 import org.htmlunit.corejs.javascript.Function;
23 import org.htmlunit.corejs.javascript.FunctionObject;
24 import org.htmlunit.corejs.javascript.Scriptable;
25 import org.htmlunit.corejs.javascript.TopLevel;
26 import org.htmlunit.corejs.javascript.VarScope;
27 import org.htmlunit.javascript.AbstractJavaScriptEngine;
28 import org.htmlunit.javascript.HtmlUnitContextFactory;
29 import org.htmlunit.javascript.JavaScriptEngine;
30 import org.htmlunit.javascript.PostponedAction;
31 import org.htmlunit.javascript.configuration.JsxClass;
32 import org.htmlunit.javascript.configuration.JsxConstructor;
33 import org.htmlunit.javascript.configuration.JsxFunction;
34 import org.htmlunit.javascript.configuration.JsxGetter;
35 import org.htmlunit.javascript.configuration.JsxSetter;
36 import org.htmlunit.javascript.host.event.Event;
37 import org.htmlunit.javascript.host.event.EventTarget;
38 import org.htmlunit.javascript.host.event.MessageEvent;
39 import org.htmlunit.util.UrlUtils;
40
41
42
43
44
45
46 @JsxClass
47 public class BroadcastChannel extends EventTarget {
48
49 private String name_;
50
51
52
53
54
55
56
57
58
59
60 @JsxConstructor
61 public static BroadcastChannel jsConstructor(final Context cx, final VarScope scope,
62 final Object[] args, final Function ctorObj, final boolean inNewExpr) {
63 if (args.length < 1 || JavaScriptEngine.isUndefined(args[0])) {
64 throw JavaScriptEngine.typeError("BroadcastChannel constructor requires a channel name argument");
65 }
66
67 final BroadcastChannel broadcastChannel = new BroadcastChannel();
68 final Window window = getWindow(ctorObj);
69 broadcastChannel.setParentScope(getTopLevelScope(scope));
70 broadcastChannel.setPrototype(((FunctionObject) ctorObj).getClassPrototype());
71
72 broadcastChannel.name_ = JavaScriptEngine.toString(args[0]);
73
74 final Set<BroadcastChannel> broadcastChannels = window.getWebWindow().getWebClient().getBroadcastChannels();
75 synchronized (broadcastChannels) {
76 broadcastChannels.add(broadcastChannel);
77 }
78
79 return broadcastChannel;
80 }
81
82
83
84
85
86 @JsxGetter
87 public String getName() {
88 return name_;
89 }
90
91
92
93
94
95 @JsxGetter
96 public Function getOnmessage() {
97 return getEventListenersContainer().getEventHandler(Event.TYPE_MESSAGE);
98 }
99
100
101
102
103
104 @JsxSetter
105 public void setOnmessage(final Object onmessage) {
106 getEventListenersContainer().setEventHandler(Event.TYPE_MESSAGE, onmessage);
107 }
108
109
110
111
112
113 @JsxGetter
114 public Function getOnmessageerror() {
115 return getEventListenersContainer().getEventHandler("messageerror");
116 }
117
118
119
120
121
122 @JsxSetter
123 public void setOnmessageerror(final Object onmessageerror) {
124 getEventListenersContainer().setEventHandler("messageerror", onmessageerror);
125 }
126
127
128
129
130
131
132 @JsxFunction
133 public void postMessage(final Object message) {
134 if (name_ == null) {
135 return;
136 }
137
138 final Window w = getWindow();
139 final WebWindow webWindow = w.getWebWindow();
140 final Page page = webWindow.getEnclosedPage();
141 final java.net.URL currentURL = page.getUrl();
142 final String origin = currentURL.getProtocol() + "://" + currentURL.getHost() + ':' + currentURL.getPort();
143
144 final AbstractJavaScriptEngine<?> jsEngine = webWindow.getWebClient().getJavaScriptEngine();
145
146 final Set<BroadcastChannel> broadcastChannels = webWindow.getWebClient().getBroadcastChannels();
147
148 synchronized (broadcastChannels) {
149 for (final BroadcastChannel channel : broadcastChannels) {
150 if (channel != this && name_.equals(channel.name_)) {
151 final Window channelWindow = channel.getWindow();
152 final Page channelPage = channelWindow.getWebWindow().getEnclosedPage();
153
154 if (UrlUtils.isSameOrigin(currentURL, channelPage.getUrl())) {
155 final TopLevel scope = getTopLevelScope(channelWindow.getParentScope());
156 final Scriptable ports = JavaScriptEngine.newArray(scope, 0);
157
158 final MessageEvent event = new MessageEvent();
159 event.initMessageEvent(Event.TYPE_MESSAGE, false, false, message, origin, "", null, ports);
160 event.setParentScope(scope);
161 event.setPrototype(channelWindow.getPrototype(event.getClass()));
162
163 final PostponedAction action =
164 new PostponedAction(channelPage, "BroadcastChannel.postMessage") {
165 @Override
166 public void execute() {
167 final HtmlUnitContextFactory cf = jsEngine.getContextFactory();
168 cf.call(cx -> channel.dispatchEvent(event));
169 }
170 };
171 jsEngine.addPostponedAction(action);
172 }
173 }
174 }
175 }
176 }
177
178
179
180
181
182 @JsxFunction
183 public void close() {
184 final Set<BroadcastChannel> broadcastChannels =
185 getWindow().getWebWindow().getWebClient().getBroadcastChannels();
186 synchronized (broadcastChannels) {
187 broadcastChannels.remove(this);
188 }
189 name_ = null;
190 }
191 }