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.

Page.java 49KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466
  1. /*
  2. * Copyright 2000-2021 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.server;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import java.net.URI;
  20. import java.net.URISyntaxException;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.EventObject;
  24. import java.util.LinkedHashSet;
  25. import java.util.LinkedList;
  26. import java.util.List;
  27. import com.vaadin.annotations.HtmlImport;
  28. import com.vaadin.annotations.StyleSheet;
  29. import com.vaadin.event.EventRouter;
  30. import com.vaadin.event.SerializableEventListener;
  31. import com.vaadin.shared.Registration;
  32. import com.vaadin.shared.ui.BorderStyle;
  33. import com.vaadin.shared.ui.ui.PageClientRpc;
  34. import com.vaadin.shared.ui.ui.PageState;
  35. import com.vaadin.shared.ui.ui.UIConstants;
  36. import com.vaadin.shared.ui.ui.UIState;
  37. import com.vaadin.shared.util.SharedUtil;
  38. import com.vaadin.ui.Dependency;
  39. import com.vaadin.ui.JavaScript;
  40. import com.vaadin.ui.LegacyWindow;
  41. import com.vaadin.ui.Link;
  42. import com.vaadin.ui.Notification;
  43. import com.vaadin.ui.UI;
  44. import com.vaadin.util.ReflectTools;
  45. public class Page implements Serializable {
  46. /**
  47. * Listener that gets notified when the size of the browser window
  48. * containing the uI has changed.
  49. *
  50. * @see #addBrowserWindowResizeListener(BrowserWindowResizeListener)
  51. */
  52. @FunctionalInterface
  53. public interface BrowserWindowResizeListener extends SerializableEventListener {
  54. /**
  55. * Invoked when the browser window containing a UI has been resized.
  56. *
  57. * @param event
  58. * a browser window resize event
  59. */
  60. public void browserWindowResized(BrowserWindowResizeEvent event);
  61. }
  62. /**
  63. * Event that is fired when a browser window containing a uI is resized.
  64. */
  65. public static class BrowserWindowResizeEvent extends EventObject {
  66. private final int width;
  67. private final int height;
  68. /**
  69. * Creates a new event.
  70. *
  71. * @param source
  72. * the uI for which the browser window has been resized
  73. * @param width
  74. * the new width of the browser window
  75. * @param height
  76. * the new height of the browser window
  77. */
  78. public BrowserWindowResizeEvent(Page source, int width, int height) {
  79. super(source);
  80. this.width = width;
  81. this.height = height;
  82. }
  83. @Override
  84. public Page getSource() {
  85. return (Page) super.getSource();
  86. }
  87. /**
  88. * Gets the new browser window height.
  89. *
  90. * @return an integer with the new pixel height of the browser window
  91. */
  92. public int getHeight() {
  93. return height;
  94. }
  95. /**
  96. * Gets the new browser window width.
  97. *
  98. * @return an integer with the new pixel width of the browser window
  99. */
  100. public int getWidth() {
  101. return width;
  102. }
  103. }
  104. /**
  105. * Private class for storing properties related to opening resources.
  106. */
  107. private class OpenResource implements Serializable {
  108. /**
  109. * The resource to open
  110. */
  111. private final Resource resource;
  112. /**
  113. * The name of the target window
  114. */
  115. private final String name;
  116. /**
  117. * The width of the target window
  118. */
  119. private final int width;
  120. /**
  121. * The height of the target window
  122. */
  123. private final int height;
  124. /**
  125. * The border style of the target window
  126. */
  127. private final BorderStyle border;
  128. private final boolean tryToOpenAsPopup;
  129. /**
  130. * Creates a new open resource.
  131. *
  132. * @param url
  133. * The URL to open
  134. * @param name
  135. * The name of the target window
  136. * @param width
  137. * The width of the target window
  138. * @param height
  139. * The height of the target window
  140. * @param border
  141. * The border style of the target window
  142. * @param tryToOpenAsPopup
  143. * Should try to open as a pop-up
  144. */
  145. private OpenResource(String url, String name, int width, int height,
  146. BorderStyle border, boolean tryToOpenAsPopup) {
  147. this(new ExternalResource(url), name, width, height, border,
  148. tryToOpenAsPopup);
  149. }
  150. /**
  151. * Creates a new open resource.
  152. *
  153. * @param resource
  154. * The resource to open
  155. * @param name
  156. * The name of the target window
  157. * @param width
  158. * The width of the target window
  159. * @param height
  160. * The height of the target window
  161. * @param border
  162. * The border style of the target window
  163. * @param tryToOpenAsPopup
  164. * Should try to open as a pop-up
  165. */
  166. private OpenResource(Resource resource, String name, int width,
  167. int height, BorderStyle border, boolean tryToOpenAsPopup) {
  168. this.resource = resource;
  169. this.name = name;
  170. this.width = width;
  171. this.height = height;
  172. this.border = border;
  173. this.tryToOpenAsPopup = tryToOpenAsPopup;
  174. }
  175. /**
  176. * Paints the open request. Should be painted inside the window.
  177. *
  178. * @param target
  179. * the paint target
  180. * @throws PaintException
  181. * if the paint operation fails
  182. */
  183. private void paintContent(PaintTarget target) throws PaintException {
  184. target.startTag("open");
  185. target.addAttribute("src", resource);
  186. if (name != null && !name.isEmpty()) {
  187. target.addAttribute("name", name);
  188. }
  189. if (!tryToOpenAsPopup) {
  190. target.addAttribute("popup", tryToOpenAsPopup);
  191. }
  192. if (width >= 0) {
  193. target.addAttribute("width", width);
  194. }
  195. if (height >= 0) {
  196. target.addAttribute("height", height);
  197. }
  198. switch (border) {
  199. case MINIMAL:
  200. target.addAttribute("border", "minimal");
  201. break;
  202. case NONE:
  203. target.addAttribute("border", "none");
  204. break;
  205. }
  206. target.endTag("open");
  207. }
  208. }
  209. private static final Method BROWSER_RESIZE_METHOD = ReflectTools.findMethod(
  210. BrowserWindowResizeListener.class, "browserWindowResized",
  211. BrowserWindowResizeEvent.class);
  212. /**
  213. * @deprecated As of 7.0, use {@link BorderStyle#NONE} instead.
  214. */
  215. @Deprecated
  216. public static final BorderStyle BORDER_NONE = BorderStyle.NONE;
  217. /**
  218. * @deprecated As of 7.0, use {@link BorderStyle#MINIMAL} instead.
  219. */
  220. @Deprecated
  221. public static final BorderStyle BORDER_MINIMAL = BorderStyle.MINIMAL;
  222. /**
  223. * @deprecated As of 7.0, use {@link BorderStyle#DEFAULT} instead.
  224. */
  225. @Deprecated
  226. public static final BorderStyle BORDER_DEFAULT = BorderStyle.DEFAULT;
  227. /**
  228. * Listener that that gets notified when the URI fragment of the page
  229. * changes.
  230. *
  231. * @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
  232. * @deprecated Use {@link PopStateListener} instead
  233. */
  234. @Deprecated
  235. @FunctionalInterface
  236. public interface UriFragmentChangedListener extends SerializableEventListener {
  237. /**
  238. * Event handler method invoked when the URI fragment of the page
  239. * changes. Please note that the initial URI fragment has already been
  240. * set when a new UI is initialized, so there will not be any initial
  241. * event for listeners added during {@link UI#init(VaadinRequest)}.
  242. *
  243. * @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
  244. *
  245. * @param event
  246. * the URI fragment changed event
  247. */
  248. public void uriFragmentChanged(UriFragmentChangedEvent event);
  249. }
  250. private static final Method URI_FRAGMENT_CHANGED_METHOD = ReflectTools
  251. .findMethod(Page.UriFragmentChangedListener.class,
  252. "uriFragmentChanged", UriFragmentChangedEvent.class);
  253. /**
  254. * Listener that that gets notified when the URI of the page changes due to
  255. * back/forward functionality of the browser.
  256. *
  257. * @see Page#addPopStateListener(PopStateListener)
  258. * @since 8.0
  259. */
  260. @FunctionalInterface
  261. public interface PopStateListener extends SerializableEventListener {
  262. /**
  263. * Event handler method invoked when the URI fragment of the page
  264. * changes. Please note that the initial URI fragment has already been
  265. * set when a new UI is initialized, so there will not be any initial
  266. * event for listeners added during {@link UI#init(VaadinRequest)}.
  267. *
  268. * @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
  269. *
  270. * @param event
  271. * the URI fragment changed event
  272. */
  273. public void uriChanged(PopStateEvent event);
  274. }
  275. private static final Method URI_CHANGED_METHOD = ReflectTools.findMethod(
  276. Page.PopStateListener.class, "uriChanged", PopStateEvent.class);
  277. /**
  278. * Resources to be opened automatically on next repaint. The list is
  279. * automatically cleared when it has been sent to the client.
  280. */
  281. private final LinkedList<OpenResource> openList = new LinkedList<>();
  282. /**
  283. * Event fired when the URI fragment of a <code>Page</code> changes.
  284. *
  285. * @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
  286. */
  287. public static class UriFragmentChangedEvent extends EventObject {
  288. /**
  289. * The new URI fragment
  290. */
  291. private final String uriFragment;
  292. /**
  293. * Creates a new instance of UriFragmentReader change event.
  294. *
  295. * @param source
  296. * the Source of the event.
  297. * @param uriFragment
  298. * the new uriFragment
  299. */
  300. public UriFragmentChangedEvent(Page source, String uriFragment) {
  301. super(source);
  302. this.uriFragment = uriFragment;
  303. }
  304. /**
  305. * Gets the page in which the fragment has changed.
  306. *
  307. * @return the page in which the fragment has changed
  308. */
  309. public Page getPage() {
  310. return (Page) getSource();
  311. }
  312. /**
  313. * Get the new URI fragment.
  314. *
  315. * @return the new fragment
  316. */
  317. public String getUriFragment() {
  318. return uriFragment;
  319. }
  320. }
  321. /**
  322. * Event fired when the URI of a <code>Page</code> changes (aka HTML 5
  323. * popstate event) on the client side due to browsers back/forward
  324. * functionality.
  325. *
  326. * @see Page#addPopStateListener(PopStateListener)
  327. * @since 8.0
  328. */
  329. public static class PopStateEvent extends EventObject {
  330. /**
  331. * The new URI as String
  332. */
  333. private final String uri;
  334. /**
  335. * Creates a new instance of PopstateEvent.
  336. *
  337. * @param source
  338. * the Source of the event.
  339. * @param uri
  340. * the new uri
  341. */
  342. public PopStateEvent(Page source, String uri) {
  343. super(source);
  344. this.uri = uri;
  345. }
  346. /**
  347. * Gets the page in which the uri has changed.
  348. *
  349. * @return the page in which the uri has changed
  350. */
  351. public Page getPage() {
  352. return (Page) getSource();
  353. }
  354. /**
  355. * Get the new URI.
  356. *
  357. * @return the new uri
  358. */
  359. public String getUri() {
  360. return uri;
  361. }
  362. }
  363. @FunctionalInterface
  364. private static interface InjectedStyle extends Serializable {
  365. public void paint(int id, PaintTarget target) throws PaintException;
  366. }
  367. private static class InjectedStyleString implements InjectedStyle {
  368. private final String css;
  369. public InjectedStyleString(String css) {
  370. this.css = css;
  371. }
  372. @Override
  373. public void paint(int id, PaintTarget target) throws PaintException {
  374. target.startTag("css-string");
  375. target.addAttribute("id", id);
  376. target.addText(css);
  377. target.endTag("css-string");
  378. }
  379. @Override
  380. public boolean equals(Object obj) {
  381. if (obj == this) {
  382. return true;
  383. } else if (obj instanceof InjectedStyleString) {
  384. InjectedStyleString that = (InjectedStyleString) obj;
  385. return css.equals(that.css);
  386. } else {
  387. return false;
  388. }
  389. }
  390. @Override
  391. public int hashCode() {
  392. return css.hashCode();
  393. }
  394. }
  395. private static class InjectedStyleResource implements InjectedStyle {
  396. private final Resource resource;
  397. public InjectedStyleResource(Resource resource) {
  398. this.resource = resource;
  399. }
  400. @Override
  401. public void paint(int id, PaintTarget target) throws PaintException {
  402. target.startTag("css-resource");
  403. target.addAttribute("id", id);
  404. target.addAttribute("url", resource);
  405. target.endTag("css-resource");
  406. }
  407. @Override
  408. public boolean equals(Object obj) {
  409. if (obj == this) {
  410. return true;
  411. } else if (obj instanceof InjectedStyleResource) {
  412. InjectedStyleResource that = (InjectedStyleResource) obj;
  413. return resource.equals(that.resource);
  414. } else {
  415. return false;
  416. }
  417. }
  418. @Override
  419. public int hashCode() {
  420. return resource.hashCode();
  421. }
  422. }
  423. /**
  424. * Contains dynamically injected styles injected in the HTML document at
  425. * runtime.
  426. *
  427. * @since 7.1
  428. */
  429. public static class Styles implements Serializable {
  430. // For internal use only, visibility is package for enabling testing
  431. LinkedHashSet<InjectedStyle> injectedStyles = new LinkedHashSet<>();
  432. // For internal use only, visibility is package for enabling testing
  433. LinkedHashSet<InjectedStyle> pendingInjections = new LinkedHashSet<>();
  434. private final UI ui;
  435. private Styles(UI ui) {
  436. this.ui = ui;
  437. }
  438. /**
  439. * Injects a raw CSS string into the page.
  440. *
  441. * @param css
  442. * The CSS to inject
  443. */
  444. public void add(String css) {
  445. if (css == null) {
  446. throw new IllegalArgumentException(
  447. "Cannot inject null CSS string");
  448. }
  449. InjectedStyleString injectedStyleString = new InjectedStyleString(
  450. css);
  451. if (!injectedStyles.contains(injectedStyleString)
  452. && pendingInjections.add(injectedStyleString)) {
  453. ui.markAsDirty();
  454. }
  455. }
  456. /**
  457. * Injects a CSS resource into the page.
  458. *
  459. * @param resource
  460. * The resource to inject.
  461. */
  462. public void add(Resource resource) {
  463. if (resource == null) {
  464. throw new IllegalArgumentException(
  465. "Cannot inject null resource");
  466. }
  467. InjectedStyleResource injection = new InjectedStyleResource(
  468. resource);
  469. if (!injectedStyles.contains(injection)
  470. && pendingInjections.add(injection)) {
  471. ui.markAsDirty();
  472. }
  473. }
  474. private void paint(PaintTarget target) throws PaintException {
  475. // If full repaint repaint all injections
  476. if (target.isFullRepaint()) {
  477. injectedStyles.addAll(pendingInjections);
  478. pendingInjections = injectedStyles;
  479. injectedStyles = new LinkedHashSet<>();
  480. }
  481. if (!pendingInjections.isEmpty()) {
  482. target.startTag("css-injections");
  483. for (InjectedStyle pending : pendingInjections) {
  484. int id = injectedStyles.size();
  485. pending.paint(id, target);
  486. injectedStyles.add(pending);
  487. }
  488. pendingInjections.clear();
  489. target.endTag("css-injections");
  490. }
  491. }
  492. }
  493. private EventRouter eventRouter;
  494. private final UI uI;
  495. private int browserWindowWidth = -1;
  496. private int browserWindowHeight = -1;
  497. private JavaScript javaScript;
  498. private Styles styles;
  499. /**
  500. * The current browser location.
  501. */
  502. private URI location;
  503. private final PageState state;
  504. private String windowName;
  505. private String newPushState;
  506. private String newReplaceState;
  507. private List<Dependency> pendingDependencies;
  508. public Page(UI uI, PageState state) {
  509. this.uI = uI;
  510. this.state = state;
  511. }
  512. private Registration addListener(Class<?> eventType, SerializableEventListener listener,
  513. Method method) {
  514. if (!hasEventRouter()) {
  515. eventRouter = new EventRouter();
  516. }
  517. return eventRouter.addListener(eventType, listener, method);
  518. }
  519. private void removeListener(Class<?> eventType, SerializableEventListener listener,
  520. Method method) {
  521. if (hasEventRouter()) {
  522. eventRouter.removeListener(eventType, listener, method);
  523. }
  524. }
  525. /**
  526. * Adds a listener that gets notified every time the URI fragment of this
  527. * page is changed. Please note that the initial URI fragment has already
  528. * been set when a new UI is initialized, so there will not be any initial
  529. * event for listeners added during {@link UI#init(VaadinRequest)}.
  530. *
  531. * @see #getUriFragment()
  532. * @see #setUriFragment(String)
  533. * @see Registration
  534. *
  535. * @param listener
  536. * the URI fragment listener to add
  537. * @return a registration object for removing the listener
  538. * @deprecated Use {@link Page#addPopStateListener(PopStateListener)}
  539. * instead
  540. * @since 8.0
  541. */
  542. @Deprecated
  543. public Registration addUriFragmentChangedListener(
  544. Page.UriFragmentChangedListener listener) {
  545. return addListener(UriFragmentChangedEvent.class, listener,
  546. URI_FRAGMENT_CHANGED_METHOD);
  547. }
  548. /**
  549. * Adds a listener that gets notified every time the URI of this page is
  550. * changed due to back/forward functionality of the browser.
  551. * <p>
  552. * Note that one only gets notified when the back/forward button affects
  553. * history changes with-in same UI, created by
  554. * {@link Page#pushState(String)} or {@link Page#replaceState(String)}
  555. * functions.
  556. *
  557. * @see #getLocation()
  558. * @see Registration
  559. *
  560. * @param listener
  561. * the Popstate listener to add
  562. * @return a registration object for removing the listener
  563. * @since 8.0
  564. */
  565. public Registration addPopStateListener(Page.PopStateListener listener) {
  566. return addListener(PopStateEvent.class, listener, URI_CHANGED_METHOD);
  567. }
  568. /**
  569. * Removes a URI fragment listener that was previously added to this page.
  570. *
  571. * @param listener
  572. * the URI fragment listener to remove
  573. *
  574. * @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
  575. *
  576. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  577. * registration object returned from
  578. * {@link #addUriFragmentChangedListener(UriFragmentChangedListener)}.
  579. */
  580. @Deprecated
  581. public void removeUriFragmentChangedListener(
  582. Page.UriFragmentChangedListener listener) {
  583. removeListener(UriFragmentChangedEvent.class, listener,
  584. URI_FRAGMENT_CHANGED_METHOD);
  585. }
  586. /**
  587. * Sets the fragment part in the current location URI. Optionally fires a
  588. * {@link UriFragmentChangedEvent}.
  589. * <p>
  590. * The fragment is the optional last component of a URI, prefixed with a
  591. * hash sign ("#").
  592. * <p>
  593. * Passing an empty string as <code>newFragment</code> sets an empty
  594. * fragment (a trailing "#" in the URI.) Passing <code>null</code> if there
  595. * is already a non-null fragment will leave a trailing # in the URI since
  596. * removing it would cause the browser to reload the page. This is not fully
  597. * consistent with the semantics of {@link java.net.URI}.
  598. *
  599. * @param newUriFragment
  600. * The new fragment.
  601. * @param fireEvents
  602. * true to fire event
  603. *
  604. * @see #getUriFragment()
  605. * @see #setLocation(URI)
  606. * @see UriFragmentChangedEvent
  607. * @see Page.UriFragmentChangedListener
  608. *
  609. */
  610. public void setUriFragment(String newUriFragment, boolean fireEvents) {
  611. String oldUriFragment = location.getFragment();
  612. if (newUriFragment == null && getUriFragment() != null) {
  613. // Can't completely remove the fragment once it has been set, will
  614. // instead set it to the empty string
  615. newUriFragment = "";
  616. }
  617. if (newUriFragment == oldUriFragment || (newUriFragment != null
  618. && newUriFragment.equals(oldUriFragment))) {
  619. return;
  620. }
  621. try {
  622. location = new URI(location.getScheme(),
  623. location.getSchemeSpecificPart(), newUriFragment);
  624. pushState(location);
  625. } catch (URISyntaxException e) {
  626. // This should not actually happen as the fragment syntax is not
  627. // constrained
  628. throw new RuntimeException(e);
  629. }
  630. if (fireEvents) {
  631. fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
  632. }
  633. }
  634. private void fireEvent(EventObject event) {
  635. if (hasEventRouter()) {
  636. eventRouter.fireEvent(event);
  637. }
  638. }
  639. /**
  640. * Sets URI fragment. This method fires a {@link UriFragmentChangedEvent}
  641. *
  642. * @param newUriFragment
  643. * id of the new fragment
  644. * @see UriFragmentChangedEvent
  645. * @see Page.UriFragmentChangedListener
  646. */
  647. public void setUriFragment(String newUriFragment) {
  648. setUriFragment(newUriFragment, true);
  649. }
  650. /**
  651. * Gets the currently set URI fragment.
  652. * <p>
  653. * Returns <code>null</code> if there is no fragment and an empty string if
  654. * there is an empty fragment.
  655. * <p>
  656. * To listen to changes in fragment, hook a
  657. * {@link Page.UriFragmentChangedListener}.
  658. *
  659. * @return the current fragment in browser location URI.
  660. *
  661. * @see #getLocation()
  662. * @see #setUriFragment(String)
  663. * @see #addUriFragmentChangedListener(UriFragmentChangedListener)
  664. */
  665. public String getUriFragment() {
  666. return location.getFragment();
  667. }
  668. public void init(VaadinRequest request) {
  669. // NOTE: UI.refresh makes assumptions about the semantics of this
  670. // method.
  671. // It should be kept in sync if this method is changed.
  672. // Extract special parameter sent by vaadinBootstrap.js
  673. String location = request.getParameter("v-loc");
  674. String clientWidth = request.getParameter("v-cw");
  675. String clientHeight = request.getParameter("v-ch");
  676. windowName = request.getParameter("v-wn");
  677. if (location != null) {
  678. try {
  679. this.location = new URI(location);
  680. } catch (URISyntaxException e) {
  681. throw new RuntimeException(
  682. "Invalid location URI received from client", e);
  683. }
  684. }
  685. if (clientWidth != null && clientHeight != null) {
  686. try {
  687. browserWindowWidth = Integer.parseInt(clientWidth);
  688. browserWindowHeight = Integer.parseInt(clientHeight);
  689. } catch (NumberFormatException e) {
  690. throw new RuntimeException(
  691. "Invalid window size received from client", e);
  692. }
  693. }
  694. }
  695. public WebBrowser getWebBrowser() {
  696. return uI.getSession().getBrowser();
  697. }
  698. /**
  699. * Gets the window.name value of the browser window of this page.
  700. *
  701. * @since 7.2
  702. *
  703. * @return the window name, <code>null</code> if the name is not known
  704. */
  705. public String getWindowName() {
  706. return windowName;
  707. }
  708. /**
  709. * For internal use only. Updates the internal state with the given values.
  710. * Does not resize the Page or browser window.
  711. *
  712. * @deprecated As of 7.2, use
  713. * {@link #updateBrowserWindowSize(int, int, boolean)} instead.
  714. *
  715. * @param width
  716. * the new browser window width
  717. * @param height
  718. * the new browse window height
  719. */
  720. @Deprecated
  721. public void updateBrowserWindowSize(int width, int height) {
  722. updateBrowserWindowSize(width, height, true);
  723. }
  724. /**
  725. * For internal use only. Updates the internal state with the given values.
  726. * Does not resize the Page or browser window.
  727. *
  728. * @since 7.2
  729. *
  730. * @param width
  731. * the new browser window width
  732. * @param height
  733. * the new browser window height
  734. * @param fireEvents
  735. * whether to fire {@link BrowserWindowResizeEvent} if the size
  736. * changes
  737. */
  738. public void updateBrowserWindowSize(int width, int height,
  739. boolean fireEvents) {
  740. boolean sizeChanged = false;
  741. if (width != browserWindowWidth) {
  742. browserWindowWidth = width;
  743. sizeChanged = true;
  744. }
  745. if (height != browserWindowHeight) {
  746. browserWindowHeight = height;
  747. sizeChanged = true;
  748. }
  749. if (fireEvents && sizeChanged) {
  750. fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
  751. browserWindowHeight));
  752. }
  753. }
  754. /**
  755. * Adds a new {@link BrowserWindowResizeListener} to this UI. The listener
  756. * will be notified whenever the browser window within which this UI resides
  757. * is resized.
  758. * <p>
  759. * In most cases, the UI should be in lazy resize mode when using browser
  760. * window resize listeners. Otherwise, a large number of events can be
  761. * received while a resize is being performed. Use
  762. * {@link UI#setResizeLazy(boolean)}.
  763. * </p>
  764. *
  765. * @param resizeListener
  766. * the listener to add
  767. * @return a registration object for removing the listener
  768. *
  769. * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent)
  770. * @see UI#setResizeLazy(boolean)
  771. * @see Registration
  772. * @since 8.0
  773. */
  774. public Registration addBrowserWindowResizeListener(
  775. BrowserWindowResizeListener resizeListener) {
  776. Registration registration = addListener(BrowserWindowResizeEvent.class,
  777. resizeListener, BROWSER_RESIZE_METHOD);
  778. getState(true).hasResizeListeners = true;
  779. return () -> {
  780. registration.remove();
  781. getState(true).hasResizeListeners = hasEventRouter()
  782. && eventRouter.hasListeners(BrowserWindowResizeEvent.class);
  783. };
  784. }
  785. /**
  786. * Removes a {@link BrowserWindowResizeListener} from this UI. The listener
  787. * will no longer be notified when the browser window is resized.
  788. *
  789. * @param resizeListener
  790. * the listener to remove
  791. *
  792. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  793. * registration object returned from
  794. * {@link #addBrowserWindowResizeListener(BrowserWindowResizeListener)}
  795. * .
  796. */
  797. @Deprecated
  798. public void removeBrowserWindowResizeListener(
  799. BrowserWindowResizeListener resizeListener) {
  800. removeListener(BrowserWindowResizeEvent.class, resizeListener,
  801. BROWSER_RESIZE_METHOD);
  802. getState(true).hasResizeListeners = hasEventRouter()
  803. && eventRouter.hasListeners(BrowserWindowResizeEvent.class);
  804. }
  805. /**
  806. * Gets the last known height of the browser window in which this UI
  807. * resides.
  808. *
  809. * @return the browser window height in pixels
  810. */
  811. public int getBrowserWindowHeight() {
  812. return browserWindowHeight;
  813. }
  814. /**
  815. * Gets the last known width of the browser window in which this uI resides.
  816. *
  817. * @return the browser window width in pixels
  818. */
  819. public int getBrowserWindowWidth() {
  820. return browserWindowWidth;
  821. }
  822. public JavaScript getJavaScript() {
  823. if (javaScript == null) {
  824. // Create and attach on first use
  825. javaScript = new JavaScript();
  826. javaScript.extend(uI);
  827. }
  828. return javaScript;
  829. }
  830. /**
  831. * Returns that stylesheet associated with this Page. The stylesheet
  832. * contains additional styles injected at runtime into the HTML document.
  833. *
  834. * @since 7.1
  835. */
  836. public Styles getStyles() {
  837. if (styles == null) {
  838. styles = new Styles(uI);
  839. }
  840. return styles;
  841. }
  842. public void paintContent(PaintTarget target) throws PaintException {
  843. if (!openList.isEmpty()) {
  844. for (OpenResource anOpenList : openList) {
  845. (anOpenList).paintContent(target);
  846. }
  847. openList.clear();
  848. }
  849. if (newPushState != null) {
  850. target.addAttribute(UIConstants.ATTRIBUTE_PUSH_STATE, newPushState);
  851. newPushState = null;
  852. }
  853. if (newReplaceState != null) {
  854. target.addAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE,
  855. newReplaceState);
  856. newReplaceState = null;
  857. }
  858. if (styles != null) {
  859. styles.paint(target);
  860. }
  861. }
  862. /**
  863. * Navigates this page to the given URI. The contents of this page in the
  864. * browser is replaced with whatever is returned for the given URI.
  865. * <p>
  866. * This method should not be used to start downloads, as the client side
  867. * will assume the browser will navigate away when opening the URI. Use one
  868. * of the {@code Page.open} methods or {@code FileDownloader} instead.
  869. *
  870. * @see #open(String, String)
  871. * @see FileDownloader
  872. *
  873. * @param uri
  874. * the URI to show
  875. */
  876. public void setLocation(String uri) {
  877. openList.add(
  878. new OpenResource(uri, "_self", -1, -1, BORDER_DEFAULT, false));
  879. uI.markAsDirty();
  880. }
  881. /**
  882. * Navigates this page to the given URI. The contents of this page in the
  883. * browser is replaced with whatever is returned for the given URI.
  884. * <p>
  885. * This method should not be used to start downloads, as the client side
  886. * will assume the browser will navigate away when opening the URI. Use one
  887. * of the {@code Page.open} methods or {@code FileDownloader} instead.
  888. *
  889. * @see #open(String, String)
  890. * @see FileDownloader
  891. *
  892. * @param uri
  893. * the URI to show
  894. */
  895. public void setLocation(URI uri) {
  896. setLocation(uri.toString());
  897. }
  898. /**
  899. * Returns the location URI of this page, as reported by the browser. Note
  900. * that this may not be consistent with the server URI the application is
  901. * deployed in due to potential proxies, redirections and similar.
  902. *
  903. * @return The browser location URI.
  904. * @throws IllegalStateException
  905. * if the
  906. * {@link DeploymentConfiguration#isSendUrlsAsParameters()} is
  907. * set to {@code false}
  908. */
  909. public URI getLocation() throws IllegalStateException {
  910. if (location == null) {
  911. if (uI.getSession() != null && !uI.getSession().getConfiguration()
  912. .isSendUrlsAsParameters()) {
  913. throw new IllegalStateException("Location is not available as the "
  914. + Constants.SERVLET_PARAMETER_SENDURLSASPARAMETERS
  915. + " parameter is configured as false");
  916. } else if (VaadinSession.getCurrent() == null) {
  917. throw new IllegalStateException("Location is not available as the "
  918. + Constants.SERVLET_PARAMETER_SENDURLSASPARAMETERS
  919. + " parameter state cannot be determined");
  920. } else if (!VaadinSession.getCurrent().getConfiguration()
  921. .isSendUrlsAsParameters()) {
  922. throw new IllegalStateException("Location is not available as the "
  923. + Constants.SERVLET_PARAMETER_SENDURLSASPARAMETERS
  924. + " parameter is configured as false");
  925. }
  926. }
  927. return location;
  928. }
  929. /**
  930. * Updates the browsers URI without causing actual page change. This method
  931. * is useful if you wish implement "deep linking" to your application.
  932. * Calling the method also adds a new entry to clients browser history and
  933. * you can further use {@link PopStateListener} to track the usage of
  934. * back/forward feature in browser.
  935. * <p>
  936. * Note, the current implementation supports setting only one new uri in one
  937. * user interaction.
  938. *
  939. * @param uri
  940. * to be used for pushState operation. The URI is resolved over
  941. * the current location. If the given URI is absolute, it must be
  942. * of same origin as the current URI or the browser will not
  943. * accept the new value.
  944. * @since 8.0
  945. */
  946. public void pushState(String uri) {
  947. newPushState = uri;
  948. uI.markAsDirty();
  949. location = location.resolve(uri);
  950. }
  951. /**
  952. * Updates the browsers URI without causing actual page change. This method
  953. * is useful if you wish implement "deep linking" to your application.
  954. * Calling the method also adds a new entry to clients browser history and
  955. * you can further use {@link PopStateListener} to track the usage of
  956. * back/forward feature in browser.
  957. * <p>
  958. * Note, the current implementation supports setting only one new uri in one
  959. * user interaction.
  960. *
  961. * @param uri
  962. * the URI to be used for pushState operation. The URI is
  963. * resolved over the current location. If the given URI is
  964. * absolute, it must be of same origin as the current URI or the
  965. * browser will not accept the new value.
  966. * @since 8.0
  967. */
  968. public void pushState(URI uri) {
  969. pushState(uri.toString());
  970. }
  971. /**
  972. * Updates the browsers URI without causing actual page change in the same
  973. * way as {@link #pushState(String)}, but does not add new entry to browsers
  974. * history.
  975. *
  976. * @param uri
  977. * the URI to be used for replaceState operation. The URI is
  978. * resolved over the current location. If the given URI is
  979. * absolute, it must be of same origin as the current URI or the
  980. * browser will not accept the new value.
  981. * @since 8.0
  982. */
  983. public void replaceState(String uri) {
  984. newReplaceState = uri;
  985. uI.markAsDirty();
  986. location = location.resolve(uri);
  987. }
  988. /**
  989. * Updates the browsers URI without causing actual page change in the same
  990. * way as {@link #pushState(URI)}, but does not add new entry to browsers
  991. * history.
  992. *
  993. * @param uri
  994. * the URI to be used for replaceState operation. The URI is
  995. * resolved over the current location. If the given URI is
  996. * absolute, it must be of same origin as the current URI or the
  997. * browser will not accept the new value.
  998. * @since 8.0
  999. */
  1000. public void replaceState(URI uri) {
  1001. replaceState(uri.toString());
  1002. }
  1003. /**
  1004. * For internal use only. Used to update the server-side location when the
  1005. * client-side location changes.
  1006. *
  1007. * @deprecated As of 7.2, use {@link #updateLocation(String, boolean)}
  1008. * instead.
  1009. *
  1010. * @param location
  1011. * the new location URI
  1012. */
  1013. @Deprecated
  1014. public void updateLocation(String location) {
  1015. updateLocation(location, true, false);
  1016. }
  1017. /**
  1018. * For internal use only. Used to update the server-side location when the
  1019. * client-side location changes.
  1020. *
  1021. * @since 8.0
  1022. *
  1023. * @param location
  1024. * the new location URI
  1025. * @param fireEvents
  1026. * whether to fire {@link UriFragmentChangedEvent} if the URI
  1027. * fragment changes
  1028. * @param firePopstate
  1029. * whether to fire {@link PopStateEvent}
  1030. */
  1031. public void updateLocation(String location, boolean fireEvents,
  1032. boolean firePopstate) {
  1033. try {
  1034. String oldUriFragment = this.location.getFragment();
  1035. this.location = new URI(location);
  1036. String newUriFragment = this.location.getFragment();
  1037. if (fireEvents
  1038. && !SharedUtil.equals(oldUriFragment, newUriFragment)) {
  1039. fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
  1040. }
  1041. if (firePopstate) {
  1042. fireEvent(new PopStateEvent(this, location));
  1043. }
  1044. } catch (URISyntaxException e) {
  1045. throw new RuntimeException(e);
  1046. }
  1047. }
  1048. /**
  1049. * Opens the given url in a window with the given name. Equivalent to
  1050. * {@link #open(String, String, boolean) open} (url, windowName, true) .
  1051. * <p>
  1052. * The supplied {@code windowName} is used as the target name in a
  1053. * window.open call in the client. This means that special values such as
  1054. * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
  1055. * <code>null</code> window name is also a special case.
  1056. * </p>
  1057. * <p>
  1058. * "", null and "_self" as {@code windowName} all causes the URL to be
  1059. * opened in the current window, replacing any old contents. For
  1060. * downloadable content you should avoid "_self" as "_self" causes the
  1061. * client to skip rendering of any other changes as it considers them
  1062. * irrelevant (the page will be replaced by the response from the URL). This
  1063. * can speed up the opening of a URL, but it might also put the client side
  1064. * into an inconsistent state if the window content is not completely
  1065. * replaced e.g., if the URL is downloaded instead of displayed in the
  1066. * browser.
  1067. * </p>
  1068. * <p>
  1069. * "_blank" as {@code windowName} causes the URL to always be opened in a
  1070. * new window or tab (depends on the browser and browser settings).
  1071. * </p>
  1072. * <p>
  1073. * "_top" and "_parent" as {@code windowName} works as specified by the HTML
  1074. * standard.
  1075. * </p>
  1076. * <p>
  1077. * Any other {@code windowName} will open the URL in a window with that
  1078. * name, either by opening a new window/tab in the browser or by replacing
  1079. * the contents of an existing window with that name.
  1080. * </p>
  1081. * <p>
  1082. * Please note that opening a popup window in this way may be blocked by the
  1083. * browser's popup-blocker because the new browser window is opened when
  1084. * processing a response from the server. To avoid this, you should instead
  1085. * use {@link Link} for opening the window because browsers are more
  1086. * forgiving when the window is opened directly from a client-side click
  1087. * event.
  1088. * </p>
  1089. *
  1090. * @param url
  1091. * the URL to open.
  1092. * @param windowName
  1093. * the name of the window.
  1094. */
  1095. public void open(String url, String windowName) {
  1096. open(url, windowName, true);
  1097. }
  1098. /**
  1099. * Opens the given url in a window with the given name. Equivalent to
  1100. * {@link #open(String, String, boolean) open} (url, windowName, true) .
  1101. * <p>
  1102. * The supplied {@code windowName} is used as the target name in a
  1103. * window.open call in the client. This means that special values such as
  1104. * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
  1105. * <code>null</code> window name is also a special case.
  1106. * </p>
  1107. * <p>
  1108. * "", null and "_self" as {@code windowName} all causes the URL to be
  1109. * opened in the current window, replacing any old contents. For
  1110. * downloadable content you should avoid "_self" as "_self" causes the
  1111. * client to skip rendering of any other changes as it considers them
  1112. * irrelevant (the page will be replaced by the response from the URL). This
  1113. * can speed up the opening of a URL, but it might also put the client side
  1114. * into an inconsistent state if the window content is not completely
  1115. * replaced e.g., if the URL is downloaded instead of displayed in the
  1116. * browser.
  1117. * </p>
  1118. * <p>
  1119. * "_blank" as {@code windowName} causes the URL to always be opened in a
  1120. * new window or tab (depends on the browser and browser settings).
  1121. * </p>
  1122. * <p>
  1123. * "_top" and "_parent" as {@code windowName} works as specified by the HTML
  1124. * standard.
  1125. * </p>
  1126. * <p>
  1127. * Any other {@code windowName} will open the URL in a window with that
  1128. * name, either by opening a new window/tab in the browser or by replacing
  1129. * the contents of an existing window with that name.
  1130. * </p>
  1131. * <p>
  1132. * Please note that opening a popup window in this way may be blocked by the
  1133. * browser's popup-blocker because the new browser window is opened when
  1134. * processing a response from the server. To avoid this, you should instead
  1135. * use {@link Link} for opening the window because browsers are more
  1136. * forgiving when the window is opened directly from a client-side click
  1137. * event.
  1138. * </p>
  1139. *
  1140. * @param url
  1141. * the URL to open.
  1142. * @param windowName
  1143. * the name of the window.
  1144. * @param tryToOpenAsPopup
  1145. * Whether to try to force the resource to be opened in a new
  1146. * window
  1147. */
  1148. public void open(String url, String windowName, boolean tryToOpenAsPopup) {
  1149. openList.add(new OpenResource(url, windowName, -1, -1, BORDER_DEFAULT,
  1150. tryToOpenAsPopup));
  1151. uI.markAsDirty();
  1152. }
  1153. /**
  1154. * Opens the given URL in a window with the given size, border and name. For
  1155. * more information on the meaning of {@code windowName}, see
  1156. * {@link #open(String, String)}.
  1157. * <p>
  1158. * Please note that opening a popup window in this way may be blocked by the
  1159. * browser's popup-blocker because the new browser window is opened when
  1160. * processing a response from the server. To avoid this, you should instead
  1161. * use {@link Link} for opening the window because browsers are more
  1162. * forgiving when the window is opened directly from a client-side click
  1163. * event.
  1164. * </p>
  1165. *
  1166. * @param url
  1167. * the URL to open.
  1168. * @param windowName
  1169. * the name of the window.
  1170. * @param width
  1171. * the width of the window in pixels
  1172. * @param height
  1173. * the height of the window in pixels
  1174. * @param border
  1175. * the border style of the window.
  1176. */
  1177. public void open(String url, String windowName, int width, int height,
  1178. BorderStyle border) {
  1179. openList.add(
  1180. new OpenResource(url, windowName, width, height, border, true));
  1181. uI.markAsDirty();
  1182. }
  1183. /**
  1184. * @deprecated As of 7.0, only retained to maintain compatibility with
  1185. * LegacyWindow.open methods. See documentation for
  1186. * {@link LegacyWindow#open(Resource, String, int, int, BorderStyle)}
  1187. * for discussion about replacing API.
  1188. */
  1189. @Deprecated
  1190. public void open(Resource resource, String windowName, int width,
  1191. int height, BorderStyle border) {
  1192. openList.add(new OpenResource(resource, windowName, width, height,
  1193. border, true));
  1194. uI.markAsDirty();
  1195. }
  1196. /**
  1197. * @deprecated As of 7.0, only retained to maintain compatibility with
  1198. * LegacyWindow.open methods. See documentation for
  1199. * {@link LegacyWindow#open(Resource, String, boolean)} for
  1200. * discussion about replacing API.
  1201. */
  1202. @Deprecated
  1203. public void open(Resource resource, String windowName,
  1204. boolean tryToOpenAsPopup) {
  1205. openList.add(new OpenResource(resource, windowName, -1, -1,
  1206. BORDER_DEFAULT, tryToOpenAsPopup));
  1207. uI.markAsDirty();
  1208. }
  1209. /**
  1210. * Shows a notification message.
  1211. *
  1212. * @see Notification
  1213. *
  1214. * @param notification
  1215. * The notification message to show
  1216. *
  1217. * @deprecated As of 7.0, use Notification.show(Page) instead.
  1218. */
  1219. @Deprecated
  1220. public void showNotification(Notification notification) {
  1221. notification.show(this);
  1222. }
  1223. /**
  1224. * Gets the Page to which the current uI belongs. This is automatically
  1225. * defined when processing requests to the server. In other cases, (e.g.
  1226. * from background threads), the current uI is not automatically defined.
  1227. *
  1228. * @see UI#getCurrent()
  1229. *
  1230. * @return the current page instance if available, otherwise
  1231. * <code>null</code>
  1232. */
  1233. public static Page getCurrent() {
  1234. UI currentUI = UI.getCurrent();
  1235. if (currentUI == null) {
  1236. return null;
  1237. }
  1238. return currentUI.getPage();
  1239. }
  1240. /**
  1241. * Sets the page title. The page title is displayed by the browser e.g. as
  1242. * the title of the browser window or as the title of the tab.
  1243. * <p>
  1244. * If this value is set to null, the previously set page title will be left
  1245. * as-is. Set to empty string to clear the title.
  1246. *
  1247. * @param title
  1248. * the page title to set
  1249. */
  1250. public void setTitle(String title) {
  1251. getState(true).title = title;
  1252. }
  1253. /**
  1254. * Reloads the page in the browser.
  1255. */
  1256. public void reload() {
  1257. uI.getRpcProxy(PageClientRpc.class).reload();
  1258. }
  1259. /**
  1260. * Returns the page state.
  1261. * <p>
  1262. * The page state is transmitted to UIConnector together with
  1263. * {@link UIState} rather than as an individual entity.
  1264. * </p>
  1265. * <p>
  1266. * The state should be considered an internal detail of Page. Classes
  1267. * outside of Page should not access it directly but only through public
  1268. * APIs provided by Page.
  1269. * </p>
  1270. *
  1271. * @since 7.1
  1272. * @param markAsDirty
  1273. * true to mark the state as dirty
  1274. * @return PageState object that can be read in any case and modified if
  1275. * markAsDirty is true
  1276. */
  1277. protected PageState getState(boolean markAsDirty) {
  1278. if (markAsDirty) {
  1279. uI.markAsDirty();
  1280. }
  1281. return state;
  1282. }
  1283. private boolean hasEventRouter() {
  1284. return eventRouter != null;
  1285. }
  1286. /**
  1287. * Add a dependency that should be added to the current page.
  1288. * <p>
  1289. * These dependencies are always added before the dependencies included by
  1290. * using the annotations {@link HtmlImport}, {@link JavaScript} and
  1291. * {@link StyleSheet} during the same request.
  1292. * <p>
  1293. * Please note that these dependencies are always sent to the client side
  1294. * and not filtered out by any {@link DependencyFilter}.
  1295. *
  1296. * @param dependency
  1297. * the dependency to add
  1298. * @since 8.1
  1299. */
  1300. public void addDependency(Dependency dependency) {
  1301. if (pendingDependencies == null) {
  1302. pendingDependencies = new ArrayList<>();
  1303. }
  1304. pendingDependencies.add(dependency);
  1305. }
  1306. /**
  1307. * Returns all pending dependencies.
  1308. * <p>
  1309. * For internal use only, calling this method will clear the pending
  1310. * dependencies.
  1311. *
  1312. * @return the pending dependencies to the current page
  1313. * @since 8.1
  1314. */
  1315. public Collection<Dependency> getPendingDependencies() {
  1316. List<Dependency> copy = new ArrayList<>();
  1317. if (pendingDependencies != null) {
  1318. copy.addAll(pendingDependencies);
  1319. }
  1320. pendingDependencies = null;
  1321. return copy;
  1322. }
  1323. /**
  1324. * Returns the {@link UI} of this {@link Page}.
  1325. *
  1326. * @return the {@link UI} of this {@link Page}.
  1327. *
  1328. * @since 8.2
  1329. */
  1330. public UI getUI() {
  1331. return uI;
  1332. }
  1333. }