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