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