You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UI.java 68KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import static java.nio.charset.StandardCharsets.UTF_8;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.IOException;
  20. import java.lang.reflect.Method;
  21. import java.net.URI;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.Collections;
  25. import java.util.Iterator;
  26. import java.util.LinkedHashMap;
  27. import java.util.LinkedHashSet;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.Map.Entry;
  31. import java.util.concurrent.Future;
  32. import java.util.logging.Level;
  33. import java.util.logging.Logger;
  34. import com.vaadin.annotations.PreserveOnRefresh;
  35. import com.vaadin.event.Action;
  36. import com.vaadin.event.Action.Handler;
  37. import com.vaadin.event.ActionManager;
  38. import com.vaadin.event.ConnectorEventListener;
  39. import com.vaadin.event.MouseEvents.ClickEvent;
  40. import com.vaadin.event.MouseEvents.ClickListener;
  41. import com.vaadin.event.UIEvents.PollEvent;
  42. import com.vaadin.event.UIEvents.PollListener;
  43. import com.vaadin.event.UIEvents.PollNotifier;
  44. import com.vaadin.navigator.Navigator;
  45. import com.vaadin.navigator.PushStateNavigation;
  46. import com.vaadin.server.ClientConnector;
  47. import com.vaadin.server.ComponentSizeValidator;
  48. import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
  49. import com.vaadin.server.DefaultErrorHandler;
  50. import com.vaadin.server.ErrorHandler;
  51. import com.vaadin.server.ErrorHandlingRunnable;
  52. import com.vaadin.server.LocaleService;
  53. import com.vaadin.server.Page;
  54. import com.vaadin.server.PaintException;
  55. import com.vaadin.server.PaintTarget;
  56. import com.vaadin.server.UIProvider;
  57. import com.vaadin.server.VaadinRequest;
  58. import com.vaadin.server.VaadinService;
  59. import com.vaadin.server.VaadinServlet;
  60. import com.vaadin.server.VaadinSession;
  61. import com.vaadin.server.VaadinSession.State;
  62. import com.vaadin.server.communication.PushConnection;
  63. import com.vaadin.shared.ApplicationConstants;
  64. import com.vaadin.shared.Connector;
  65. import com.vaadin.shared.EventId;
  66. import com.vaadin.shared.MouseEventDetails;
  67. import com.vaadin.shared.Registration;
  68. import com.vaadin.shared.communication.PushMode;
  69. import com.vaadin.shared.ui.WindowOrderRpc;
  70. import com.vaadin.shared.ui.ui.DebugWindowClientRpc;
  71. import com.vaadin.shared.ui.ui.DebugWindowServerRpc;
  72. import com.vaadin.shared.ui.ui.PageClientRpc;
  73. import com.vaadin.shared.ui.ui.ScrollClientRpc;
  74. import com.vaadin.shared.ui.ui.UIClientRpc;
  75. import com.vaadin.shared.ui.ui.UIConstants;
  76. import com.vaadin.shared.ui.ui.UIServerRpc;
  77. import com.vaadin.shared.ui.ui.UIState;
  78. import com.vaadin.ui.Component.Focusable;
  79. import com.vaadin.ui.Dependency.Type;
  80. import com.vaadin.ui.Window.WindowOrderChangeListener;
  81. import com.vaadin.ui.declarative.Design;
  82. import com.vaadin.ui.dnd.DragSourceExtension;
  83. import com.vaadin.ui.dnd.DropTargetExtension;
  84. import com.vaadin.util.ConnectorHelper;
  85. import com.vaadin.util.CurrentInstance;
  86. import com.vaadin.util.ReflectTools;
  87. /**
  88. * The topmost component in any component hierarchy. There is one UI for every
  89. * Vaadin instance in a browser window. A UI may either represent an entire
  90. * browser window (or tab) or some part of a html page where a Vaadin
  91. * application is embedded.
  92. * <p>
  93. * The UI is the server side entry point for various client side features that
  94. * are not represented as components added to a layout, e.g notifications, sub
  95. * windows, and executing javascript in the browser.
  96. * </p>
  97. * <p>
  98. * When a new UI instance is needed, typically because the user opens a URL in a
  99. * browser window which points to e.g. {@link VaadinServlet}, all
  100. * {@link UIProvider}s registered to the current {@link VaadinSession} are
  101. * queried for the UI class that should be used. The selection is by default
  102. * based on the <code>UI</code> init parameter from web.xml.
  103. * </p>
  104. * <p>
  105. * After a UI has been created by the application, it is initialized using
  106. * {@link #init(VaadinRequest)}. This method is intended to be overridden by the
  107. * developer to add components to the user interface and initialize
  108. * non-component functionality. The component hierarchy must be initialized by
  109. * passing a {@link Component} with the main layout or other content of the view
  110. * to {@link #setContent(Component)} or to the constructor of the UI.
  111. * </p>
  112. *
  113. * @see #init(VaadinRequest)
  114. * @see UIProvider
  115. *
  116. * @since 7.0
  117. */
  118. public abstract class UI extends AbstractSingleComponentContainer
  119. implements Action.Container, Action.Notifier, PollNotifier,
  120. LegacyComponent, Focusable {
  121. /**
  122. * The application to which this UI belongs
  123. */
  124. private volatile VaadinSession session;
  125. /**
  126. * List of windows in this UI.
  127. */
  128. private final LinkedHashSet<Window> windows = new LinkedHashSet<>();
  129. /**
  130. * The component that should be scrolled into view after the next repaint.
  131. * Null if nothing should be scrolled into view.
  132. */
  133. private Component scrollIntoView;
  134. /**
  135. * The id of this UI, used to find the server side instance of the UI form
  136. * which a request originates. A negative value indicates that the UI id has
  137. * not yet been assigned by the Application.
  138. *
  139. * @see VaadinSession#getNextUIid()
  140. */
  141. private int uiId = -1;
  142. /**
  143. * Keeps track of the Actions added to this component, and manages the
  144. * painting and handling as well.
  145. */
  146. protected ActionManager actionManager;
  147. private ConnectorTracker connectorTracker = new ConnectorTracker(this);
  148. private Page page = new Page(this, getState(false).pageState);
  149. private LoadingIndicatorConfiguration loadingIndicatorConfiguration = new LoadingIndicatorConfigurationImpl(
  150. this);
  151. /**
  152. * Scroll Y position.
  153. */
  154. private int scrollTop = 0;
  155. /**
  156. * Scroll X position
  157. */
  158. private int scrollLeft = 0;
  159. private UIServerRpc rpc = new UIServerRpc() {
  160. @Override
  161. public void click(MouseEventDetails mouseDetails) {
  162. fireEvent(new ClickEvent(UI.this, mouseDetails));
  163. }
  164. @Override
  165. public void resize(int viewWidth, int viewHeight, int windowWidth,
  166. int windowHeight) {
  167. // TODO We're not doing anything with the view dimensions
  168. getPage().updateBrowserWindowSize(windowWidth, windowHeight, true);
  169. }
  170. @Override
  171. public void scroll(int scrollTop, int scrollLeft) {
  172. UI.this.scrollTop = scrollTop;
  173. UI.this.scrollLeft = scrollLeft;
  174. }
  175. @Override
  176. public void poll() {
  177. fireEvent(new PollEvent(UI.this));
  178. }
  179. @Override
  180. public void popstate(String uri) {
  181. getPage().updateLocation(uri, true, true);
  182. }
  183. };
  184. private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() {
  185. @Override
  186. public void showServerDebugInfo(Connector connector) {
  187. String info = ConnectorHelper
  188. .getDebugInformation((ClientConnector) connector);
  189. getLogger().info(info);
  190. }
  191. @Override
  192. public void analyzeLayouts() {
  193. // TODO Move to client side
  194. List<InvalidLayout> invalidSizes = ComponentSizeValidator
  195. .validateLayouts(UI.this);
  196. StringBuilder json = new StringBuilder();
  197. json.append("{\"invalidLayouts\":");
  198. json.append('[');
  199. if (invalidSizes != null) {
  200. boolean first = true;
  201. for (InvalidLayout invalidSize : invalidSizes) {
  202. if (!first) {
  203. json.append(',');
  204. } else {
  205. first = false;
  206. }
  207. invalidSize.reportErrors(json, System.err);
  208. }
  209. }
  210. json.append("]}");
  211. getRpcProxy(DebugWindowClientRpc.class)
  212. .reportLayoutProblems(json.toString());
  213. }
  214. @Override
  215. public void showServerDesign(Connector connector) {
  216. if (!(connector instanceof Component)) {
  217. getLogger().severe("Tried to output declarative design for "
  218. + connector + ", which is not a component");
  219. return;
  220. }
  221. if (connector instanceof UI) {
  222. // We want to see the content of the UI, so we can add it to
  223. // another UI or component container
  224. connector = ((UI) connector).getContent();
  225. }
  226. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  227. try {
  228. Design.write((Component) connector, baos);
  229. getLogger().info("Design for " + connector
  230. + " requested from debug window:\n"
  231. + baos.toString(UTF_8.name()));
  232. } catch (IOException e) {
  233. getLogger().log(Level.WARNING,
  234. "Error producing design for " + connector, e);
  235. }
  236. }
  237. };
  238. private WindowOrderRpc windowOrderRpc = new WindowOrderRpc() {
  239. @Override
  240. public void windowOrderChanged(Map<Integer, Connector> windowOrders) {
  241. Map<Integer, Window> orders = new LinkedHashMap<>();
  242. for (Entry<Integer, Connector> entry : windowOrders.entrySet()) {
  243. if (entry.getValue() instanceof Window) {
  244. orders.put(entry.getKey(), (Window) entry.getValue());
  245. }
  246. }
  247. fireWindowOrder(orders);
  248. }
  249. };
  250. /**
  251. * Timestamp keeping track of the last heartbeat of this UI. Updated to the
  252. * current time whenever the application receives a heartbeat or UIDL
  253. * request from the client for this UI.
  254. */
  255. private long lastHeartbeatTimestamp = System.currentTimeMillis();
  256. private boolean closing = false;
  257. private TooltipConfiguration tooltipConfiguration = new TooltipConfigurationImpl(
  258. this);
  259. private PushConfiguration pushConfiguration = new PushConfigurationImpl(
  260. this);
  261. private ReconnectDialogConfiguration reconnectDialogConfiguration = new ReconnectDialogConfigurationImpl(
  262. this);
  263. private NotificationConfiguration notificationConfiguration = new NotificationConfigurationImpl(
  264. this);
  265. /**
  266. * Tracks which message from the client should come next. First message from
  267. * the client has id 0.
  268. */
  269. private int lastProcessedClientToServerId = -1;
  270. /**
  271. * Stores the extension of the active drag source component
  272. */
  273. private DragSourceExtension<? extends AbstractComponent> activeDragSource;
  274. /**
  275. * Creates a new empty UI without a caption. The content of the UI must be
  276. * set by calling {@link #setContent(Component)} before using the UI.
  277. */
  278. public UI() {
  279. this(null);
  280. }
  281. /**
  282. * Creates a new UI with the given component (often a layout) as its
  283. * content.
  284. *
  285. * @param content
  286. * the component to use as this UIs content.
  287. *
  288. * @see #setContent(Component)
  289. */
  290. public UI(Component content) {
  291. registerRpc(rpc);
  292. registerRpc(debugRpc);
  293. registerRpc(windowOrderRpc);
  294. setSizeFull();
  295. setContent(content);
  296. }
  297. @Override
  298. protected UIState getState() {
  299. return (UIState) super.getState();
  300. }
  301. @Override
  302. protected UIState getState(boolean markAsDirty) {
  303. return (UIState) super.getState(markAsDirty);
  304. }
  305. @Override
  306. public Class<? extends UIState> getStateType() {
  307. // This is a workaround for a problem with creating the correct state
  308. // object during build
  309. return UIState.class;
  310. }
  311. /**
  312. * Overridden to return a value instead of referring to the parent.
  313. *
  314. * @return this UI
  315. *
  316. * @see com.vaadin.ui.AbstractComponent#getUI()
  317. */
  318. @Override
  319. public UI getUI() {
  320. return this;
  321. }
  322. /**
  323. * Gets the application object to which the component is attached.
  324. *
  325. * <p>
  326. * The method will return {@code null} if the component is not currently
  327. * attached to an application.
  328. * </p>
  329. *
  330. * <p>
  331. * Getting a null value is often a problem in constructors of regular
  332. * components and in the initializers of custom composite components. A
  333. * standard workaround is to use {@link VaadinSession#getCurrent()} to
  334. * retrieve the application instance that the current request relates to.
  335. * Another way is to move the problematic initialization to
  336. * {@link #attach()}, as described in the documentation of the method.
  337. * </p>
  338. *
  339. * @return the parent application of the component or <code>null</code>.
  340. * @see #attach()
  341. */
  342. @Override
  343. public VaadinSession getSession() {
  344. return session;
  345. }
  346. @Override
  347. public void paintContent(PaintTarget target) throws PaintException {
  348. page.paintContent(target);
  349. if (scrollIntoView != null) {
  350. target.addAttribute("scrollTo", scrollIntoView);
  351. scrollIntoView = null;
  352. }
  353. if (pendingFocus != null) {
  354. // ensure focused component is still attached to this main window
  355. if (equals(pendingFocus.getUI()) || (pendingFocus.getUI() != null
  356. && equals(pendingFocus.getUI().getParent()))) {
  357. target.addAttribute("focused", pendingFocus);
  358. }
  359. pendingFocus = null;
  360. }
  361. if (actionManager != null) {
  362. actionManager.paintActions(null, target);
  363. }
  364. if (isResizeLazy()) {
  365. target.addAttribute(UIConstants.RESIZE_LAZY, true);
  366. }
  367. }
  368. /**
  369. * Fire a click event to all click listeners.
  370. *
  371. * @param object
  372. * The raw "value" of the variable change from the client side.
  373. */
  374. private void fireClick(Map<String, Object> parameters) {
  375. MouseEventDetails mouseDetails = MouseEventDetails
  376. .deSerialize((String) parameters.get("mouseDetails"));
  377. fireEvent(new ClickEvent(this, mouseDetails));
  378. }
  379. /**
  380. * Fire a window order event.
  381. *
  382. * @param windows
  383. * The windows with their orders whose order has been updated.
  384. */
  385. private void fireWindowOrder(Map<Integer, Window> windows) {
  386. for (Entry<Integer, Window> entry : windows.entrySet()) {
  387. entry.getValue().fireWindowOrderChange(entry.getKey());
  388. }
  389. fireEvent(new WindowOrderUpdateEvent(this, windows.values()));
  390. }
  391. @Override
  392. @SuppressWarnings("unchecked")
  393. public void changeVariables(Object source, Map<String, Object> variables) {
  394. if (variables.containsKey(EventId.CLICK_EVENT_IDENTIFIER)) {
  395. fireClick((Map<String, Object>) variables
  396. .get(EventId.CLICK_EVENT_IDENTIFIER));
  397. }
  398. // Actions
  399. if (actionManager != null) {
  400. actionManager.handleActions(variables, this);
  401. }
  402. }
  403. /*
  404. * (non-Javadoc)
  405. *
  406. * @see com.vaadin.ui.HasComponents#iterator()
  407. */
  408. @Override
  409. public Iterator<Component> iterator() {
  410. // TODO could directly create some kind of combined iterator instead of
  411. // creating a new ArrayList
  412. List<Component> components = new ArrayList<>();
  413. if (getContent() != null) {
  414. components.add(getContent());
  415. }
  416. components.addAll(windows);
  417. return Collections.unmodifiableCollection(components).iterator();
  418. }
  419. /*
  420. * (non-Javadoc)
  421. *
  422. * @see com.vaadin.ui.ComponentContainer#getComponentCount()
  423. */
  424. @Override
  425. public int getComponentCount() {
  426. return windows.size() + (getContent() == null ? 0 : 1);
  427. }
  428. /**
  429. * Sets the session to which this UI is assigned.
  430. * <p>
  431. * This method is for internal use by the framework. To explicitly close a
  432. * UI, see {@link #close()}.
  433. * </p>
  434. *
  435. * @param session
  436. * the session to set
  437. *
  438. * @throws IllegalStateException
  439. * if the session has already been set
  440. *
  441. * @see #getSession()
  442. */
  443. public void setSession(VaadinSession session) {
  444. if (session == null && this.session == null) {
  445. throw new IllegalStateException(
  446. "Session should never be set to null when UI.session is already null");
  447. } else if (session != null && this.session != null) {
  448. throw new IllegalStateException(
  449. "Session has already been set. Old session: "
  450. + getSessionDetails(this.session)
  451. + ". New session: " + getSessionDetails(session)
  452. + ".");
  453. } else {
  454. if (session == null) {
  455. try {
  456. detach();
  457. } catch (Exception e) {
  458. getLogger().log(Level.WARNING,
  459. "Error while detaching UI from session", e);
  460. }
  461. // Disable push when the UI is detached. Otherwise the
  462. // push connection and possibly VaadinSession will live
  463. // on.
  464. getPushConfiguration().setPushMode(PushMode.DISABLED);
  465. new Thread(new Runnable() {
  466. @Override
  467. public void run() {
  468. // This intentionally does disconnect without locking
  469. // the VaadinSession to avoid deadlocks where the server
  470. // uses a lock for the websocket connection
  471. // See https://dev.vaadin.com/ticket/18436
  472. // The underlying problem is
  473. // https://dev.vaadin.com/ticket/16919
  474. setPushConnection(null);
  475. }
  476. }).start();
  477. }
  478. this.session = session;
  479. }
  480. if (session != null) {
  481. attach();
  482. }
  483. }
  484. private static String getSessionDetails(VaadinSession session) {
  485. if (session == null) {
  486. return null;
  487. } else {
  488. return session + " for " + session.getService().getServiceName();
  489. }
  490. }
  491. /**
  492. * Gets the id of the UI, used to identify this UI within its application
  493. * when processing requests. The UI id should be present in every request to
  494. * the server that originates from this UI.
  495. * {@link VaadinService#findUI(VaadinRequest)} uses this id to find the
  496. * route to which the request belongs.
  497. * <p>
  498. * This method is not intended to be overridden. If it is overridden, care
  499. * should be taken since this method might be called in situations where
  500. * {@link UI#getCurrent()} does not return this UI.
  501. *
  502. * @return the id of this UI
  503. */
  504. public int getUIId() {
  505. return uiId;
  506. }
  507. /**
  508. * Adds a window as a subwindow inside this UI. To open a new browser window
  509. * or tab, you should instead use a {@link UIProvider}.
  510. *
  511. * @param window
  512. * @throws IllegalArgumentException
  513. * if the window is already added to an application
  514. * @throws NullPointerException
  515. * if the given <code>Window</code> is <code>null</code>.
  516. */
  517. public void addWindow(Window window)
  518. throws IllegalArgumentException, NullPointerException {
  519. if (window == null) {
  520. throw new NullPointerException("Argument must not be null");
  521. }
  522. if (window.isAttached()) {
  523. throw new IllegalArgumentException(
  524. "Window is already attached to an application.");
  525. }
  526. attachWindow(window);
  527. }
  528. /**
  529. * Helper method to attach a window.
  530. *
  531. * @param w
  532. * the window to add
  533. */
  534. private void attachWindow(Window w) {
  535. windows.add(w);
  536. w.setParent(this);
  537. fireComponentAttachEvent(w);
  538. markAsDirty();
  539. }
  540. /**
  541. * Remove the given subwindow from this UI.
  542. *
  543. * Since Vaadin 6.5, {@link Window.CloseListener}s are called also when
  544. * explicitly removing a window by calling this method.
  545. *
  546. * Since Vaadin 6.5, returns a boolean indicating if the window was removed
  547. * or not.
  548. *
  549. * @param window
  550. * Window to be removed.
  551. * @return true if the subwindow was removed, false otherwise
  552. */
  553. public boolean removeWindow(Window window) {
  554. if (!windows.remove(window)) {
  555. // Window window is not a subwindow of this UI.
  556. return false;
  557. }
  558. window.setParent(null);
  559. markAsDirty();
  560. window.fireClose();
  561. fireComponentDetachEvent(window);
  562. fireWindowOrder(Collections.singletonMap(-1, window));
  563. return true;
  564. }
  565. /**
  566. * Gets all the windows added to this UI.
  567. *
  568. * @return an unmodifiable collection of windows
  569. */
  570. public Collection<Window> getWindows() {
  571. return Collections.unmodifiableCollection(windows);
  572. }
  573. @Override
  574. public void focus() {
  575. super.focus();
  576. }
  577. /**
  578. * Component that should be focused after the next repaint. Null if no focus
  579. * change should take place.
  580. */
  581. private Focusable pendingFocus;
  582. private boolean resizeLazy = false;
  583. private Navigator navigator;
  584. private PushConnection pushConnection = null;
  585. private LocaleService localeService = new LocaleService(this,
  586. getState(false).localeServiceState);
  587. private String embedId;
  588. private String uiPathInfo;
  589. private String uiRootPath;
  590. private boolean mobileHtml5DndPolyfillLoaded;
  591. /**
  592. * This method is used by Component.Focusable objects to request focus to
  593. * themselves. Focus renders must be handled at window level (instead of
  594. * Component.Focusable) due we want the last focused component to be focused
  595. * in client too. Not the one that is rendered last (the case we'd get if
  596. * implemented in Focusable only).
  597. *
  598. * To focus component from Vaadin application, use Focusable.focus(). See
  599. * {@link Focusable}.
  600. *
  601. * @param focusable
  602. * to be focused on next paint
  603. */
  604. public void setFocusedComponent(Focusable focusable) {
  605. pendingFocus = focusable;
  606. markAsDirty();
  607. }
  608. /**
  609. * Scrolls any component between the component and UI to a suitable position
  610. * so the component is visible to the user. The given component must belong
  611. * to this UI.
  612. *
  613. * @param component
  614. * the component to be scrolled into view
  615. * @throws IllegalArgumentException
  616. * if {@code component} does not belong to this UI
  617. */
  618. public void scrollIntoView(Component component)
  619. throws IllegalArgumentException {
  620. if (component.getUI() != this) {
  621. throw new IllegalArgumentException(
  622. "The component where to scroll must belong to this UI.");
  623. }
  624. scrollIntoView = component;
  625. markAsDirty();
  626. }
  627. /**
  628. * Internal initialization method, should not be overridden. This method is
  629. * not declared as final because that would break compatibility with e.g.
  630. * CDI.
  631. *
  632. * @param request
  633. * the initialization request
  634. * @param uiId
  635. * the id of the new ui
  636. * @param embedId
  637. * the embed id of this UI, or <code>null</code> if no id is
  638. * known
  639. *
  640. * @see #getUIId()
  641. * @see #getEmbedId()
  642. */
  643. public void doInit(VaadinRequest request, int uiId, String embedId) {
  644. if (this.uiId != -1) {
  645. String message = "This UI instance is already initialized (as UI id "
  646. + this.uiId
  647. + ") and can therefore not be initialized again (as UI id "
  648. + uiId + "). ";
  649. if (getSession() != null
  650. && !getSession().equals(VaadinSession.getCurrent())) {
  651. message += "Furthermore, it is already attached to another VaadinSession. ";
  652. }
  653. message += "Please make sure you are not accidentally reusing an old UI instance.";
  654. throw new IllegalStateException(message);
  655. }
  656. this.uiId = uiId;
  657. this.embedId = embedId;
  658. // Actual theme - used for finding CustomLayout templates
  659. setTheme(request.getParameter("theme"));
  660. getPage().init(request);
  661. String uiPathInfo = (String) request
  662. .getAttribute(ApplicationConstants.UI_ROOT_PATH);
  663. if (uiPathInfo != null) {
  664. setUiPathInfo(uiPathInfo);
  665. }
  666. if (getSession() != null && getSession().getConfiguration() != null
  667. && getSession().getConfiguration().isSendUrlsAsParameters()
  668. && getPage().getLocation() != null) {
  669. // By default the root is the URL from client
  670. String uiRootPath = getPage().getLocation().getPath();
  671. if (uiPathInfo != null && uiRootPath.contains(uiPathInfo)) {
  672. // String everything from the URL after uiPathInfo
  673. // This will remove the navigation state from the URL
  674. uiRootPath = uiRootPath.substring(0,
  675. uiRootPath.indexOf(uiPathInfo) + uiPathInfo.length());
  676. } else if (request.getPathInfo() != null) {
  677. // uiRootPath does not match the uiPathInfo
  678. // This can happen for example when embedding a Vaadin UI
  679. String pathInfo = request.getPathInfo();
  680. if (uiRootPath.endsWith(pathInfo)) {
  681. uiRootPath = uiRootPath.substring(0,
  682. uiRootPath.length() - pathInfo.length());
  683. }
  684. }
  685. // Store the URL as the UI Root Path
  686. setUiRootPath(uiRootPath);
  687. }
  688. // Call the init overridden by the application developer
  689. init(request);
  690. Navigator navigator = getNavigator();
  691. if (navigator != null) {
  692. // Kickstart navigation if a navigator was attached in init()
  693. navigator.navigateTo(navigator.getState());
  694. }
  695. }
  696. private void setUiRootPath(String uiRootPath) {
  697. this.uiRootPath = uiRootPath;
  698. }
  699. /**
  700. * Gets the part of path (from browser's URL) that points to this UI.
  701. * Basically the same as the value from {@link Page#getLocation()}, but
  702. * without possible view identifiers or path parameters.
  703. *
  704. * @return the part of path (from browser's URL) that points to this UI,
  705. * without possible view identifiers or path parameters
  706. *
  707. * @since 8.2
  708. */
  709. public String getUiRootPath() {
  710. return uiRootPath;
  711. }
  712. private void setUiPathInfo(String uiPathInfo) {
  713. this.uiPathInfo = uiPathInfo;
  714. }
  715. /**
  716. * Gets the path info part of the request that is used to detect the UI.
  717. * This is defined during UI init by certain {@link UIProvider UIProviders}
  718. * that map different UIs to different URIs, like Vaadin Spring. This
  719. * information is used by the {@link Navigator} when the {@link UI} is
  720. * annotated with {@link PushStateNavigation}.
  721. * <p>
  722. * For example if the UI is accessed through
  723. * {@code http://example.com/MyUI/mainview/parameter=1} the path info would
  724. * be {@code /MyUI}.
  725. *
  726. * @return the path info part of the request; {@code null} if no request
  727. * from client has been processed
  728. *
  729. * @since 8.2
  730. */
  731. public String getUiPathInfo() {
  732. return uiPathInfo;
  733. }
  734. /**
  735. * Initializes this UI. This method is intended to be overridden by
  736. * subclasses to build the view and configure non-component functionality.
  737. * Performing the initialization in a constructor is not suggested as the
  738. * state of the UI is not properly set up when the constructor is invoked.
  739. * <p>
  740. * The {@link VaadinRequest} can be used to get information about the
  741. * request that caused this UI to be created.
  742. * </p>
  743. *
  744. * @param request
  745. * the Vaadin request that caused this UI to be created
  746. */
  747. protected abstract void init(VaadinRequest request);
  748. /**
  749. * Internal reinitialization method, should not be overridden.
  750. *
  751. * @since 7.2
  752. * @param request
  753. * the request that caused this UI to be reloaded
  754. */
  755. public void doRefresh(VaadinRequest request) {
  756. // This is a horrible hack. We want to have the most recent location and
  757. // browser window size available in refresh(), but we want to call
  758. // listeners, if any, only after refresh(). So we momentarily assign the
  759. // old values back before setting the new values again to ensure the
  760. // events are properly fired.
  761. Page page = getPage();
  762. URI oldLocation = page.getLocation();
  763. int oldWidth = page.getBrowserWindowWidth();
  764. int oldHeight = page.getBrowserWindowHeight();
  765. page.init(request);
  766. // Reset heartbeat timeout to avoid surprise if it's almost expired
  767. setLastHeartbeatTimestamp(System.currentTimeMillis());
  768. refresh(request);
  769. URI newLocation = page.getLocation();
  770. int newWidth = page.getBrowserWindowWidth();
  771. int newHeight = page.getBrowserWindowHeight();
  772. page.updateLocation(oldLocation.toString(), false, false);
  773. page.updateBrowserWindowSize(oldWidth, oldHeight, false);
  774. page.updateLocation(newLocation.toString(), true, false);
  775. page.updateBrowserWindowSize(newWidth, newHeight, true);
  776. }
  777. /**
  778. * Reinitializes this UI after a browser refresh if the UI is set to be
  779. * preserved on refresh, typically using the {@link PreserveOnRefresh}
  780. * annotation. This method is intended to be overridden by subclasses if
  781. * needed; the default implementation is empty.
  782. * <p>
  783. * The {@link VaadinRequest} can be used to get information about the
  784. * request that caused this UI to be reloaded.
  785. *
  786. * @since 7.2
  787. * @param request
  788. * the request that caused this UI to be reloaded
  789. */
  790. protected void refresh(VaadinRequest request) {
  791. }
  792. /**
  793. * Sets the thread local for the current UI. This method is used by the
  794. * framework to set the current application whenever a new request is
  795. * processed and it is cleared when the request has been processed.
  796. * <p>
  797. * The application developer can also use this method to define the current
  798. * UI outside the normal request handling, e.g. when initiating custom
  799. * background threads.
  800. * <p>
  801. * The UI is stored using a weak reference to avoid leaking memory in case
  802. * it is not explicitly cleared.
  803. *
  804. * @param ui
  805. * the UI to register as the current UI
  806. *
  807. * @see #getCurrent()
  808. * @see ThreadLocal
  809. */
  810. public static void setCurrent(UI ui) {
  811. CurrentInstance.set(UI.class, ui);
  812. }
  813. /**
  814. * Gets the currently used UI. The current UI is automatically defined when
  815. * processing requests to the server. In other cases, (e.g. from background
  816. * threads), the current UI is not automatically defined.
  817. * <p>
  818. * The UI is stored using a weak reference to avoid leaking memory in case
  819. * it is not explicitly cleared.
  820. *
  821. * @return the current UI instance if available, otherwise <code>null</code>
  822. *
  823. * @see #setCurrent(UI)
  824. */
  825. public static UI getCurrent() {
  826. return CurrentInstance.get(UI.class);
  827. }
  828. /**
  829. * Set top offset to which the UI should scroll to.
  830. *
  831. * @param scrollTop
  832. */
  833. public void setScrollTop(int scrollTop) {
  834. if (scrollTop < 0) {
  835. throw new IllegalArgumentException(
  836. "Scroll offset must be at least 0");
  837. }
  838. if (this.scrollTop != scrollTop) {
  839. this.scrollTop = scrollTop;
  840. getRpcProxy(ScrollClientRpc.class).setScrollTop(scrollTop);
  841. }
  842. }
  843. public int getScrollTop() {
  844. return scrollTop;
  845. }
  846. /**
  847. * Set left offset to which the UI should scroll to.
  848. *
  849. * @param scrollLeft
  850. */
  851. public void setScrollLeft(int scrollLeft) {
  852. if (scrollLeft < 0) {
  853. throw new IllegalArgumentException(
  854. "Scroll offset must be at least 0");
  855. }
  856. if (this.scrollLeft != scrollLeft) {
  857. this.scrollLeft = scrollLeft;
  858. getRpcProxy(ScrollClientRpc.class).setScrollLeft(scrollLeft);
  859. }
  860. }
  861. public int getScrollLeft() {
  862. return scrollLeft;
  863. }
  864. @Override
  865. protected ActionManager getActionManager() {
  866. if (actionManager == null) {
  867. actionManager = new ActionManager(this);
  868. }
  869. return actionManager;
  870. }
  871. @Override
  872. public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
  873. T action) {
  874. getActionManager().addAction(action);
  875. }
  876. @Override
  877. public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
  878. T action) {
  879. if (actionManager != null) {
  880. actionManager.removeAction(action);
  881. }
  882. }
  883. @Override
  884. public void addActionHandler(Handler actionHandler) {
  885. getActionManager().addActionHandler(actionHandler);
  886. }
  887. @Override
  888. public void removeActionHandler(Handler actionHandler) {
  889. if (actionManager != null) {
  890. actionManager.removeActionHandler(actionHandler);
  891. }
  892. }
  893. /**
  894. * Should resize operations be lazy, i.e. should there be a delay before
  895. * layout sizes are recalculated and resize events are sent to the server.
  896. * Speeds up resize operations in slow UIs with the penalty of slightly
  897. * decreased usability.
  898. * <p>
  899. * Default value: <code>false</code>
  900. * </p>
  901. * <p>
  902. * When there are active window resize listeners, lazy resize mode should be
  903. * used to avoid a large number of events during resize.
  904. * </p>
  905. *
  906. * @param resizeLazy
  907. * true to use a delay before recalculating sizes, false to
  908. * calculate immediately.
  909. */
  910. public void setResizeLazy(boolean resizeLazy) {
  911. this.resizeLazy = resizeLazy;
  912. markAsDirty();
  913. }
  914. /**
  915. * Checks whether lazy resize is enabled.
  916. *
  917. * @return <code>true</code> if lazy resize is enabled, <code>false</code>
  918. * if lazy resize is not enabled
  919. */
  920. public boolean isResizeLazy() {
  921. return resizeLazy;
  922. }
  923. /**
  924. * Add a click listener to the UI. The listener is called whenever the user
  925. * clicks inside the UI. Also when the click targets a component inside the
  926. * UI, provided the targeted component does not prevent the click event from
  927. * propagating.
  928. *
  929. * @see Registration
  930. *
  931. * @param listener
  932. * The listener to add, not null
  933. * @return a registration object for removing the listener
  934. * @since 8.0
  935. */
  936. public Registration addClickListener(ClickListener listener) {
  937. return addListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  938. listener, ClickListener.clickMethod);
  939. }
  940. /**
  941. * Remove a click listener from the UI. The listener should earlier have
  942. * been added using {@link #addListener(ClickListener)}.
  943. *
  944. * @param listener
  945. * The listener to remove
  946. *
  947. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  948. * registration object returned from
  949. * {@link #removeClickListener(ClickListener)}.
  950. */
  951. @Deprecated
  952. public void removeClickListener(ClickListener listener) {
  953. removeListener(EventId.CLICK_EVENT_IDENTIFIER, ClickEvent.class,
  954. listener);
  955. }
  956. @Override
  957. public boolean isConnectorEnabled() {
  958. // TODO How can a UI be invisible? What does it mean?
  959. return isVisible() && isEnabled();
  960. }
  961. public ConnectorTracker getConnectorTracker() {
  962. return connectorTracker;
  963. }
  964. public Page getPage() {
  965. return page;
  966. }
  967. /**
  968. * Returns the navigator attached to this UI or null if there is no
  969. * navigator.
  970. *
  971. * @return
  972. */
  973. public Navigator getNavigator() {
  974. return navigator;
  975. }
  976. /**
  977. * For internal use only.
  978. *
  979. * @param navigator
  980. */
  981. public void setNavigator(Navigator navigator) {
  982. this.navigator = navigator;
  983. }
  984. /**
  985. * Setting the caption of a UI is not supported. To set the title of the
  986. * HTML page, use Page.setTitle
  987. *
  988. * @deprecated As of 7.0, use {@link Page#setTitle(String)}
  989. */
  990. @Override
  991. @Deprecated
  992. public void setCaption(String caption) {
  993. throw new UnsupportedOperationException(
  994. "You can not set the title of a UI. To set the title of the HTML page, use Page.setTitle");
  995. }
  996. /**
  997. * Shows a notification message on the middle of the UI. The message
  998. * automatically disappears ("humanized message").
  999. *
  1000. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  1001. * rendered as html.
  1002. *
  1003. * @see #showNotification(Notification)
  1004. * @see Notification
  1005. *
  1006. * @param caption
  1007. * The message
  1008. *
  1009. * @deprecated As of 7.0, use Notification.show instead but be aware that
  1010. * Notification.show does not allow HTML.
  1011. */
  1012. @Deprecated
  1013. public void showNotification(String caption) {
  1014. Notification notification = new Notification(caption);
  1015. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1016. getPage().showNotification(notification);
  1017. }
  1018. /**
  1019. * Shows a notification message the UI. The position and behavior of the
  1020. * message depends on the type, which is one of the basic types defined in
  1021. * {@link Notification}, for instance Notification.TYPE_WARNING_MESSAGE.
  1022. *
  1023. * Care should be taken to to avoid XSS vulnerabilities as the caption is
  1024. * rendered as html.
  1025. *
  1026. * @see #showNotification(Notification)
  1027. * @see Notification
  1028. *
  1029. * @param caption
  1030. * The message
  1031. * @param type
  1032. * The message type
  1033. *
  1034. * @deprecated As of 7.0, use Notification.show instead but be aware that
  1035. * Notification.show does not allow HTML.
  1036. */
  1037. @Deprecated
  1038. public void showNotification(String caption, Notification.Type type) {
  1039. Notification notification = new Notification(caption, type);
  1040. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1041. getPage().showNotification(notification);
  1042. }
  1043. /**
  1044. * Shows a notification consisting of a bigger caption and a smaller
  1045. * description on the middle of the UI. The message automatically disappears
  1046. * ("humanized message").
  1047. *
  1048. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  1049. * description are rendered as html.
  1050. *
  1051. * @see #showNotification(Notification)
  1052. * @see Notification
  1053. *
  1054. * @param caption
  1055. * The caption of the message
  1056. * @param description
  1057. * The message description
  1058. *
  1059. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  1060. * be aware that HTML by default not allowed.
  1061. */
  1062. @Deprecated
  1063. public void showNotification(String caption, String description) {
  1064. Notification notification = new Notification(caption, description);
  1065. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1066. getPage().showNotification(notification);
  1067. }
  1068. /**
  1069. * Shows a notification consisting of a bigger caption and a smaller
  1070. * description. The position and behavior of the message depends on the
  1071. * type, which is one of the basic types defined in {@link Notification} ,
  1072. * for instance Notification.TYPE_WARNING_MESSAGE.
  1073. *
  1074. * Care should be taken to to avoid XSS vulnerabilities as the caption and
  1075. * description are rendered as html.
  1076. *
  1077. * @see #showNotification(Notification)
  1078. * @see Notification
  1079. *
  1080. * @param caption
  1081. * The caption of the message
  1082. * @param description
  1083. * The message description
  1084. * @param type
  1085. * The message type
  1086. *
  1087. * @deprecated As of 7.0, use new Notification(...).show(Page) instead but
  1088. * be aware that HTML by default not allowed.
  1089. */
  1090. @Deprecated
  1091. public void showNotification(String caption, String description,
  1092. Notification.Type type) {
  1093. Notification notification = new Notification(caption, description,
  1094. type);
  1095. notification.setHtmlContentAllowed(true);// Backwards compatibility
  1096. getPage().showNotification(notification);
  1097. }
  1098. /**
  1099. * Shows a notification consisting of a bigger caption and a smaller
  1100. * description. The position and behavior of the message depends on the
  1101. * type, which is one of the basic types defined in {@link Notification} ,
  1102. * for instance Notification.TYPE_WARNING_MESSAGE.
  1103. *
  1104. * Care should be taken to avoid XSS vulnerabilities if html content is
  1105. * allowed.
  1106. *
  1107. * @see #showNotification(Notification)
  1108. * @see Notification
  1109. *
  1110. * @param caption
  1111. * The message caption
  1112. * @param description
  1113. * The message description
  1114. * @param type
  1115. * The type of message
  1116. * @param htmlContentAllowed
  1117. * Whether html in the caption and description should be
  1118. * displayed as html or as plain text
  1119. *
  1120. * @deprecated As of 7.0, use new Notification(...).show(Page).
  1121. */
  1122. @Deprecated
  1123. public void showNotification(String caption, String description,
  1124. Notification.Type type, boolean htmlContentAllowed) {
  1125. getPage().showNotification(new Notification(caption, description, type,
  1126. htmlContentAllowed));
  1127. }
  1128. /**
  1129. * Shows a notification message.
  1130. *
  1131. * @see Notification
  1132. * @see #showNotification(String)
  1133. * @see #showNotification(String, int)
  1134. * @see #showNotification(String, String)
  1135. * @see #showNotification(String, String, int)
  1136. *
  1137. * @param notification
  1138. * The notification message to show
  1139. *
  1140. * @deprecated As of 7.0, use Notification.show instead
  1141. */
  1142. @Deprecated
  1143. public void showNotification(Notification notification) {
  1144. getPage().showNotification(notification);
  1145. }
  1146. /**
  1147. * Returns the timestamp of the last received heartbeat for this UI.
  1148. * <p>
  1149. * This method is not intended to be overridden. If it is overridden, care
  1150. * should be taken since this method might be called in situations where
  1151. * {@link UI#getCurrent()} does not return this UI.
  1152. *
  1153. * @see VaadinService#closeInactiveUIs(VaadinSession)
  1154. *
  1155. * @return The time the last heartbeat request occurred, in milliseconds
  1156. * since the epoch.
  1157. */
  1158. public long getLastHeartbeatTimestamp() {
  1159. return lastHeartbeatTimestamp;
  1160. }
  1161. /**
  1162. * Sets the last heartbeat request timestamp for this UI. Called by the
  1163. * framework whenever the application receives a valid heartbeat request for
  1164. * this UI.
  1165. * <p>
  1166. * This method is not intended to be overridden. If it is overridden, care
  1167. * should be taken since this method might be called in situations where
  1168. * {@link UI#getCurrent()} does not return this UI.
  1169. *
  1170. * @param lastHeartbeat
  1171. * The time the last heartbeat request occurred, in milliseconds
  1172. * since the epoch.
  1173. */
  1174. public void setLastHeartbeatTimestamp(long lastHeartbeat) {
  1175. lastHeartbeatTimestamp = lastHeartbeat;
  1176. }
  1177. /**
  1178. * Gets the theme currently in use by this UI.
  1179. *
  1180. * @return the theme name
  1181. */
  1182. public String getTheme() {
  1183. return getState(false).theme;
  1184. }
  1185. /**
  1186. * Sets the theme currently in use by this UI
  1187. * <p>
  1188. * Calling this method will remove the old theme (CSS file) from the
  1189. * application and add the new theme.
  1190. * <p>
  1191. * Note that this method is NOT SAFE to call in a portal environment or
  1192. * other environment where there are multiple UIs on the same page. The old
  1193. * CSS file will be removed even if there are other UIs on the page which
  1194. * are still using it.
  1195. *
  1196. * @since 7.3
  1197. * @param theme
  1198. * The new theme name
  1199. */
  1200. public void setTheme(String theme) {
  1201. if (theme == null) {
  1202. getState().theme = null;
  1203. } else {
  1204. getState().theme = VaadinServlet.stripSpecialChars(theme);
  1205. }
  1206. }
  1207. /**
  1208. * Marks this UI to be {@link #detach() detached} from the session at the
  1209. * end of the current request, or the next request if there is no current
  1210. * request (if called from a background thread, for instance.)
  1211. * <p>
  1212. * The UI is detached after the response is sent, so in the current request
  1213. * it can still update the client side normally. However, after the response
  1214. * any new requests from the client side to this UI will cause an error, so
  1215. * usually the client should be asked, for instance, to reload the page
  1216. * (serving a fresh UI instance), to close the page, or to navigate
  1217. * somewhere else.
  1218. * <p>
  1219. * Note that this method is strictly for users to explicitly signal the
  1220. * framework that the UI should be detached. Overriding it is not a reliable
  1221. * way to catch UIs that are to be detached. Instead, {@code UI.detach()}
  1222. * should be overridden or a {@link DetachListener} used.
  1223. */
  1224. public void close() {
  1225. closing = true;
  1226. boolean sessionExpired = (session == null
  1227. || session.getState() != State.OPEN);
  1228. getRpcProxy(UIClientRpc.class).uiClosed(sessionExpired);
  1229. if (getPushConnection() != null) {
  1230. // Push the Rpc to the client. The connection will be closed when
  1231. // the UI is detached and cleaned up.
  1232. // Can't use UI.push() directly since it checks for a valid session
  1233. if (session != null) {
  1234. session.getService().runPendingAccessTasks(session);
  1235. }
  1236. getPushConnection().push();
  1237. }
  1238. }
  1239. /**
  1240. * Returns whether this UI is marked as closed and is to be detached.
  1241. * <p>
  1242. * This method is not intended to be overridden. If it is overridden, care
  1243. * should be taken since this method might be called in situations where
  1244. * {@link UI#getCurrent()} does not return this UI.
  1245. *
  1246. * @see #close()
  1247. *
  1248. * @return whether this UI is closing.
  1249. */
  1250. public boolean isClosing() {
  1251. return closing;
  1252. }
  1253. /**
  1254. * Called after the UI is added to the session. A UI instance is attached
  1255. * exactly once, before its {@link #init(VaadinRequest) init} method is
  1256. * called.
  1257. *
  1258. * @see Component#attach
  1259. */
  1260. @Override
  1261. public void attach() {
  1262. super.attach();
  1263. getLocaleService().addLocale(getLocale());
  1264. }
  1265. /**
  1266. * Called before the UI is removed from the session. A UI instance is
  1267. * detached exactly once, either:
  1268. * <ul>
  1269. * <li>after it is explicitly {@link #close() closed}.
  1270. * <li>when its session is closed or expires
  1271. * <li>after three missed heartbeat requests.
  1272. * </ul>
  1273. * <p>
  1274. * Note that when a UI is detached, any changes made in the {@code detach}
  1275. * methods of any children or {@link DetachListener}s that would be
  1276. * communicated to the client are silently ignored.
  1277. */
  1278. @Override
  1279. public void detach() {
  1280. super.detach();
  1281. }
  1282. /*
  1283. * (non-Javadoc)
  1284. *
  1285. * @see
  1286. * com.vaadin.ui.AbstractSingleComponentContainer#setContent(com.vaadin.
  1287. * ui.Component)
  1288. */
  1289. @Override
  1290. public void setContent(Component content) {
  1291. if (content instanceof Window) {
  1292. throw new IllegalArgumentException(
  1293. "A Window cannot be added using setContent. Use addWindow(Window window) instead");
  1294. }
  1295. super.setContent(content);
  1296. }
  1297. @Override
  1298. public void setTabIndex(int tabIndex) {
  1299. getState().tabIndex = tabIndex;
  1300. }
  1301. @Override
  1302. public int getTabIndex() {
  1303. return getState(false).tabIndex;
  1304. }
  1305. /**
  1306. * Locks the session of this UI and runs the provided Runnable right away.
  1307. * <p>
  1308. * It is generally recommended to use {@link #access(Runnable)} instead of
  1309. * this method for accessing a session from a different thread as
  1310. * {@link #access(Runnable)} can be used while holding the lock of another
  1311. * session. To avoid causing deadlocks, this methods throws an exception if
  1312. * it is detected than another session is also locked by the current thread.
  1313. * </p>
  1314. * <p>
  1315. * This method behaves differently than {@link #access(Runnable)} in some
  1316. * situations:
  1317. * <ul>
  1318. * <li>If the current thread is currently holding the lock of the session,
  1319. * {@link #accessSynchronously(Runnable)} runs the task right away whereas
  1320. * {@link #access(Runnable)} defers the task to a later point in time.</li>
  1321. * <li>If some other thread is currently holding the lock for the session,
  1322. * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
  1323. * to be available whereas {@link #access(Runnable)} defers the task to a
  1324. * later point in time.</li>
  1325. * </ul>
  1326. * </p>
  1327. *
  1328. * @since 7.1
  1329. *
  1330. * @param runnable
  1331. * the runnable which accesses the UI
  1332. * @throws UIDetachedException
  1333. * if the UI is not attached to a session (and locking can
  1334. * therefore not be done)
  1335. * @throws IllegalStateException
  1336. * if the current thread holds the lock for another session
  1337. *
  1338. * @see #access(Runnable)
  1339. * @see VaadinSession#accessSynchronously(Runnable)
  1340. */
  1341. public void accessSynchronously(Runnable runnable)
  1342. throws UIDetachedException {
  1343. Map<Class<?>, CurrentInstance> old = null;
  1344. VaadinSession session = getSession();
  1345. if (session == null) {
  1346. throw new UIDetachedException();
  1347. }
  1348. VaadinService.verifyNoOtherSessionLocked(session);
  1349. session.lock();
  1350. try {
  1351. if (getSession() == null) {
  1352. // UI was detached after fetching the session but before we
  1353. // acquired the lock.
  1354. throw new UIDetachedException();
  1355. }
  1356. old = CurrentInstance.setCurrent(this);
  1357. runnable.run();
  1358. } finally {
  1359. session.unlock();
  1360. if (old != null) {
  1361. CurrentInstance.restoreInstances(old);
  1362. }
  1363. }
  1364. }
  1365. /**
  1366. * Provides exclusive access to this UI from outside a request handling
  1367. * thread.
  1368. * <p>
  1369. * The given runnable is executed while holding the session lock to ensure
  1370. * exclusive access to this UI. If the session is not locked, the lock will
  1371. * be acquired and the runnable is run right away. If the session is
  1372. * currently locked, the runnable will be run before that lock is released.
  1373. * </p>
  1374. * <p>
  1375. * RPC handlers for components inside this UI do not need to use this method
  1376. * as the session is automatically locked by the framework during RPC
  1377. * handling.
  1378. * </p>
  1379. * <p>
  1380. * Please note that the runnable might be invoked on a different thread or
  1381. * later on the current thread, which means that custom thread locals might
  1382. * not have the expected values when the command is executed.
  1383. * {@link UI#getCurrent()}, {@link VaadinSession#getCurrent()} and
  1384. * {@link VaadinService#getCurrent()} are set according to this UI before
  1385. * executing the command. Other standard CurrentInstance values such as
  1386. * {@link VaadinService#getCurrentRequest()} and
  1387. * {@link VaadinService#getCurrentResponse()} will not be defined.
  1388. * </p>
  1389. * <p>
  1390. * The returned future can be used to check for task completion and to
  1391. * cancel the task.
  1392. * </p>
  1393. *
  1394. * @see #getCurrent()
  1395. * @see #accessSynchronously(Runnable)
  1396. * @see VaadinSession#access(Runnable)
  1397. * @see VaadinSession#lock()
  1398. *
  1399. * @since 7.1
  1400. *
  1401. * @param runnable
  1402. * the runnable which accesses the UI
  1403. * @throws UIDetachedException
  1404. * if the UI is not attached to a session (and locking can
  1405. * therefore not be done)
  1406. * @return a future that can be used to check for task completion and to
  1407. * cancel the task
  1408. */
  1409. public Future<Void> access(final Runnable runnable) {
  1410. VaadinSession session = getSession();
  1411. if (session == null) {
  1412. throw new UIDetachedException();
  1413. }
  1414. return session.access(new ErrorHandlingRunnable() {
  1415. @Override
  1416. public void run() {
  1417. accessSynchronously(runnable);
  1418. }
  1419. @Override
  1420. public void handleError(Exception exception) {
  1421. try {
  1422. if (runnable instanceof ErrorHandlingRunnable) {
  1423. ErrorHandlingRunnable errorHandlingRunnable = (ErrorHandlingRunnable) runnable;
  1424. errorHandlingRunnable.handleError(exception);
  1425. } else {
  1426. ConnectorErrorEvent errorEvent = new ConnectorErrorEvent(
  1427. UI.this, exception);
  1428. ErrorHandler errorHandler = com.vaadin.server.ErrorEvent
  1429. .findErrorHandler(UI.this);
  1430. if (errorHandler == null) {
  1431. errorHandler = new DefaultErrorHandler();
  1432. }
  1433. errorHandler.error(errorEvent);
  1434. }
  1435. } catch (Exception e) {
  1436. getLogger().log(Level.SEVERE, e.getMessage(), e);
  1437. }
  1438. }
  1439. });
  1440. }
  1441. /**
  1442. * Retrieves the object used for configuring tooltips.
  1443. *
  1444. * @return The instance used for tooltip configuration
  1445. */
  1446. public TooltipConfiguration getTooltipConfiguration() {
  1447. return tooltipConfiguration;
  1448. }
  1449. /**
  1450. * Retrieves the object used for configuring notifications.
  1451. *
  1452. * @return The instance used for notification configuration
  1453. */
  1454. public NotificationConfiguration getNotificationConfiguration() {
  1455. return notificationConfiguration;
  1456. }
  1457. /**
  1458. * Retrieves the object used for configuring the loading indicator.
  1459. *
  1460. * @return The instance used for configuring the loading indicator
  1461. */
  1462. public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() {
  1463. return loadingIndicatorConfiguration;
  1464. }
  1465. /**
  1466. * Pushes the pending changes and client RPC invocations of this UI to the
  1467. * client-side.
  1468. * <p>
  1469. * If push is enabled, but the push connection is not currently open, the
  1470. * push will be done when the connection is established.
  1471. * <p>
  1472. * As with all UI methods, the session must be locked when calling this
  1473. * method. It is also recommended that {@link UI#getCurrent()} is set up to
  1474. * return this UI since writing the response may invoke logic in any
  1475. * attached component or extension. The recommended way of fulfilling these
  1476. * conditions is to use {@link #access(Runnable)}.
  1477. *
  1478. * @throws IllegalStateException
  1479. * if push is disabled.
  1480. * @throws UIDetachedException
  1481. * if this UI is not attached to a session.
  1482. *
  1483. * @see #getPushConfiguration()
  1484. *
  1485. * @since 7.1
  1486. */
  1487. public void push() {
  1488. VaadinSession session = getSession();
  1489. if (session == null) {
  1490. throw new UIDetachedException("Cannot push a detached UI");
  1491. }
  1492. assert session.hasLock();
  1493. if (!getPushConfiguration().getPushMode().isEnabled()) {
  1494. throw new IllegalStateException("Push not enabled");
  1495. }
  1496. assert pushConnection != null;
  1497. /*
  1498. * Purge the pending access queue as it might mark a connector as dirty
  1499. * when the push would otherwise be ignored because there are no changes
  1500. * to push.
  1501. */
  1502. session.getService().runPendingAccessTasks(session);
  1503. if (!getConnectorTracker().hasDirtyConnectors()) {
  1504. // Do not push if there is nothing to push
  1505. return;
  1506. }
  1507. pushConnection.push();
  1508. }
  1509. /**
  1510. * Returns the internal push connection object used by this UI. This method
  1511. * should only be called by the framework.
  1512. * <p>
  1513. * This method is not intended to be overridden. If it is overridden, care
  1514. * should be taken since this method might be called in situations where
  1515. * {@link UI#getCurrent()} does not return this UI.
  1516. *
  1517. * @return the push connection used by this UI, or {@code null} if push is
  1518. * not available.
  1519. */
  1520. public PushConnection getPushConnection() {
  1521. assert !(getPushConfiguration().getPushMode().isEnabled()
  1522. && pushConnection == null);
  1523. return pushConnection;
  1524. }
  1525. /**
  1526. * Sets the internal push connection object used by this UI. This method
  1527. * should only be called by the framework.
  1528. * <p>
  1529. * The {@code pushConnection} argument must be non-null if and only if
  1530. * {@code getPushConfiguration().getPushMode().isEnabled()}.
  1531. *
  1532. * @param pushConnection
  1533. * the push connection to use for this UI
  1534. */
  1535. public void setPushConnection(PushConnection pushConnection) {
  1536. // If pushMode is disabled then there should never be a pushConnection;
  1537. // if enabled there should always be
  1538. assert (pushConnection == null)
  1539. ^ getPushConfiguration().getPushMode().isEnabled();
  1540. if (pushConnection == this.pushConnection) {
  1541. return;
  1542. }
  1543. if (this.pushConnection != null && this.pushConnection.isConnected()) {
  1544. this.pushConnection.disconnect();
  1545. }
  1546. this.pushConnection = pushConnection;
  1547. }
  1548. /**
  1549. * Sets the interval with which the UI should poll the server to see if
  1550. * there are any changes. Polling is disabled by default.
  1551. * <p>
  1552. * Note that it is possible to enable push and polling at the same time but
  1553. * it should not be done to avoid excessive server traffic.
  1554. * </p>
  1555. * <p>
  1556. * Add-on developers should note that this method is only meant for the
  1557. * application developer. An add-on should not set the poll interval
  1558. * directly, rather instruct the user to set it.
  1559. * </p>
  1560. *
  1561. * @param intervalInMillis
  1562. * The interval (in ms) with which the UI should poll the server
  1563. * or -1 to disable polling
  1564. */
  1565. public void setPollInterval(int intervalInMillis) {
  1566. getState().pollInterval = intervalInMillis;
  1567. }
  1568. /**
  1569. * Returns the interval with which the UI polls the server.
  1570. *
  1571. * @return The interval (in ms) with which the UI polls the server or -1 if
  1572. * polling is disabled
  1573. */
  1574. public int getPollInterval() {
  1575. return getState(false).pollInterval;
  1576. }
  1577. @Override
  1578. public Registration addPollListener(PollListener listener) {
  1579. return addListener(EventId.POLL, PollEvent.class, listener,
  1580. PollListener.POLL_METHOD);
  1581. }
  1582. @Override
  1583. @Deprecated
  1584. public void removePollListener(PollListener listener) {
  1585. removeListener(EventId.POLL, PollEvent.class, listener);
  1586. }
  1587. /**
  1588. * Retrieves the object used for configuring the push channel.
  1589. *
  1590. * @since 7.1
  1591. * @return The instance used for push configuration
  1592. */
  1593. public PushConfiguration getPushConfiguration() {
  1594. return pushConfiguration;
  1595. }
  1596. /**
  1597. * Retrieves the object used for configuring the reconnect dialog.
  1598. *
  1599. * @since 7.6
  1600. * @return The instance used for reconnect dialog configuration
  1601. */
  1602. public ReconnectDialogConfiguration getReconnectDialogConfiguration() {
  1603. return reconnectDialogConfiguration;
  1604. }
  1605. /**
  1606. * Get the label that is added to the container element, where tooltip,
  1607. * notification and dialogs are added to.
  1608. *
  1609. * @return the label of the container
  1610. */
  1611. public String getOverlayContainerLabel() {
  1612. return getState(false).overlayContainerLabel;
  1613. }
  1614. /**
  1615. * Sets the label that is added to the container element, where tooltip,
  1616. * notifications and dialogs are added to.
  1617. * <p>
  1618. * This is helpful for users of assistive devices, as this element is
  1619. * reachable for them.
  1620. * </p>
  1621. *
  1622. * @param overlayContainerLabel
  1623. * label to use for the container
  1624. */
  1625. public void setOverlayContainerLabel(String overlayContainerLabel) {
  1626. getState().overlayContainerLabel = overlayContainerLabel;
  1627. }
  1628. /**
  1629. * Returns the locale service which handles transmission of Locale data to
  1630. * the client.
  1631. *
  1632. * @since 7.1
  1633. * @return The LocaleService for this UI
  1634. */
  1635. public LocaleService getLocaleService() {
  1636. return localeService;
  1637. }
  1638. private static Logger getLogger() {
  1639. return Logger.getLogger(UI.class.getName());
  1640. }
  1641. /**
  1642. * Gets a string the uniquely distinguishes this UI instance based on where
  1643. * it is embedded. The embed identifier is based on the
  1644. * <code>window.name</code> DOM attribute of the browser window where the UI
  1645. * is displayed and the id of the div element where the UI is embedded.
  1646. *
  1647. * @since 7.2
  1648. * @return the embed id for this UI, or <code>null</code> if no id known
  1649. */
  1650. public String getEmbedId() {
  1651. return embedId;
  1652. }
  1653. /**
  1654. * Gets the last processed server message id.
  1655. *
  1656. * Used internally for communication tracking.
  1657. *
  1658. * @return lastProcessedServerMessageId the id of the last processed server
  1659. * message
  1660. * @since 7.6
  1661. */
  1662. public int getLastProcessedClientToServerId() {
  1663. return lastProcessedClientToServerId;
  1664. }
  1665. /**
  1666. * Sets the last processed server message id.
  1667. *
  1668. * Used internally for communication tracking.
  1669. *
  1670. * @param lastProcessedClientToServerId
  1671. * the id of the last processed server message
  1672. * @since 7.6
  1673. */
  1674. public void setLastProcessedClientToServerId(
  1675. int lastProcessedClientToServerId) {
  1676. this.lastProcessedClientToServerId = lastProcessedClientToServerId;
  1677. }
  1678. /**
  1679. * Adds a WindowOrderUpdateListener to the UI.
  1680. * <p>
  1681. * The WindowOrderUpdateEvent is fired when the order positions of windows
  1682. * are updated. It can happen when some window (this or other) is brought to
  1683. * front or detached.
  1684. * <p>
  1685. * The other way to listen window position for specific window is
  1686. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1687. *
  1688. * @see Window#addWindowOrderChangeListener(WindowOrderChangeListener)
  1689. *
  1690. * @param listener
  1691. * the WindowModeChangeListener to add.
  1692. * @since 8.0
  1693. *
  1694. * @return a registration object for removing the listener
  1695. */
  1696. public Registration addWindowOrderUpdateListener(
  1697. WindowOrderUpdateListener listener) {
  1698. addListener(EventId.WINDOW_ORDER, WindowOrderUpdateEvent.class,
  1699. listener, WindowOrderUpdateListener.windowOrderUpdateMethod);
  1700. return () -> removeListener(EventId.WINDOW_ORDER,
  1701. WindowOrderUpdateEvent.class, listener);
  1702. }
  1703. /**
  1704. * Sets the drag source of an active HTML5 drag event.
  1705. *
  1706. * @param extension
  1707. * Extension of the drag source component.
  1708. * @see DragSourceExtension
  1709. * @since 8.1
  1710. */
  1711. public void setActiveDragSource(
  1712. DragSourceExtension<? extends AbstractComponent> extension) {
  1713. activeDragSource = extension;
  1714. }
  1715. /**
  1716. * Gets the drag source of an active HTML5 drag event.
  1717. *
  1718. * @return Extension of the drag source component if the drag event is
  1719. * active and originated from this UI, {@literal null} otherwise.
  1720. * @see DragSourceExtension
  1721. * @since 8.1
  1722. */
  1723. public DragSourceExtension<? extends AbstractComponent> getActiveDragSource() {
  1724. return activeDragSource;
  1725. }
  1726. /**
  1727. * Returns whether HTML5 DnD extensions {@link DragSourceExtension} and
  1728. * {@link DropTargetExtension} and alike should be enabled for mobile
  1729. * devices.
  1730. * <p>
  1731. * By default, it is disabled.
  1732. *
  1733. * @return {@code true} if enabled, {@code false} if not
  1734. * @since 8.1
  1735. * @see #setMobileHtml5DndEnabled(boolean)
  1736. */
  1737. public boolean isMobileHtml5DndEnabled() {
  1738. return getState(false).enableMobileHTML5DnD;
  1739. }
  1740. /**
  1741. * Enable or disable HTML5 DnD for mobile devices.
  1742. * <p>
  1743. * Usually you should enable the support in the {@link #init(VaadinRequest)}
  1744. * method. By default, it is disabled. This operation is NOOP when the user
  1745. * is not on a mobile device.
  1746. * <p>
  1747. * Changing this will effect all {@link DragSourceExtension} and
  1748. * {@link DropTargetExtension} (and subclasses) that have not yet been
  1749. * attached to the UI on the client side.
  1750. * <p>
  1751. * <em>NOTE: When disabling this after it has been enabled, it will not
  1752. * affect {@link DragSourceExtension} and {@link DropTargetExtension} (and
  1753. * subclasses) that have been previously added. Those extensions should be
  1754. * explicitly removed to make sure user cannot perform DnD operations
  1755. * anymore.</em>
  1756. *
  1757. * @param enabled
  1758. * {@code true} if enabled, {@code false} if not
  1759. * @since 8.1
  1760. */
  1761. public void setMobileHtml5DndEnabled(boolean enabled) {
  1762. if (getState(false).enableMobileHTML5DnD != enabled) {
  1763. getState().enableMobileHTML5DnD = enabled;
  1764. if (isMobileHtml5DndEnabled()) {
  1765. loadMobileHtml5DndPolyfill();
  1766. }
  1767. }
  1768. }
  1769. /**
  1770. * Load and initialize the mobile drag-drop-polyfill if needed and not yet
  1771. * done so.
  1772. */
  1773. private void loadMobileHtml5DndPolyfill() {
  1774. if (mobileHtml5DndPolyfillLoaded) {
  1775. return;
  1776. }
  1777. if (!getPage().getWebBrowser().isTouchDevice()) {
  1778. return;
  1779. }
  1780. mobileHtml5DndPolyfillLoaded = true;
  1781. String vaadinLocation = getSession().getService().getStaticFileLocation(
  1782. VaadinService.getCurrentRequest()) + "/VAADIN/";
  1783. getPage().addDependency(new Dependency(Type.JAVASCRIPT,
  1784. vaadinLocation + ApplicationConstants.MOBILE_DND_POLYFILL_JS));
  1785. getRpcProxy(PageClientRpc.class).initializeMobileHtml5DndPolyfill();
  1786. }
  1787. /**
  1788. * Event which is fired when the ordering of the windows is updated.
  1789. * <p>
  1790. * The other way to listen window position for specific window is
  1791. * {@link Window#addWindowOrderChangeListener(WindowOrderChangeListener)}
  1792. *
  1793. * @see Window.WindowOrderChangeEvent
  1794. *
  1795. * @author Vaadin Ltd
  1796. * @since 8.0
  1797. *
  1798. */
  1799. public static class WindowOrderUpdateEvent extends Component.Event {
  1800. private final Collection<Window> windows;
  1801. public WindowOrderUpdateEvent(Component source,
  1802. Collection<Window> windows) {
  1803. super(source);
  1804. this.windows = windows;
  1805. }
  1806. /**
  1807. * Gets the windows in the order they appear in the UI: top most window
  1808. * is first, bottom one last.
  1809. *
  1810. * @return the windows collection
  1811. */
  1812. public Collection<Window> getWindows() {
  1813. return windows;
  1814. }
  1815. }
  1816. /**
  1817. * An interface used for listening to Windows order update events.
  1818. *
  1819. * @since 8.0
  1820. *
  1821. * @see Window.WindowOrderChangeEvent
  1822. */
  1823. @FunctionalInterface
  1824. public interface WindowOrderUpdateListener extends ConnectorEventListener {
  1825. public static final Method windowOrderUpdateMethod = ReflectTools
  1826. .findMethod(WindowOrderUpdateListener.class,
  1827. "windowOrderUpdated", WindowOrderUpdateEvent.class);
  1828. /**
  1829. * Called when the windows order positions are changed. Use
  1830. * {@link WindowOrderUpdateEvent#getWindows()} to get a reference to the
  1831. * {@link Window}s whose order positions are updated. Use
  1832. * {@link Window#getOrderPosition()} to get window position for specific
  1833. * window.
  1834. *
  1835. * @param event
  1836. */
  1837. public void windowOrderUpdated(WindowOrderUpdateEvent event);
  1838. }
  1839. }