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 48KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464
  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. LinkedHashSet<InjectedStyle> injectedStyles = new LinkedHashSet<>();
  431. LinkedHashSet<InjectedStyle> pendingInjections = new LinkedHashSet<>();
  432. private final UI ui;
  433. private Styles(UI ui) {
  434. this.ui = ui;
  435. }
  436. /**
  437. * Injects a raw CSS string into the page.
  438. *
  439. * @param css
  440. * The CSS to inject
  441. */
  442. public void add(String css) {
  443. if (css == null) {
  444. throw new IllegalArgumentException(
  445. "Cannot inject null CSS string");
  446. }
  447. InjectedStyleString injectedStyleString = new InjectedStyleString(
  448. css);
  449. if (!injectedStyles.contains(injectedStyleString)
  450. && pendingInjections.add(injectedStyleString)) {
  451. ui.markAsDirty();
  452. }
  453. }
  454. /**
  455. * Injects a CSS resource into the page.
  456. *
  457. * @param resource
  458. * The resource to inject.
  459. */
  460. public void add(Resource resource) {
  461. if (resource == null) {
  462. throw new IllegalArgumentException(
  463. "Cannot inject null resource");
  464. }
  465. InjectedStyleResource injection = new InjectedStyleResource(
  466. resource);
  467. if (!injectedStyles.contains(injection)
  468. && pendingInjections.add(injection)) {
  469. ui.markAsDirty();
  470. }
  471. }
  472. private void paint(PaintTarget target) throws PaintException {
  473. // If full repaint repaint all injections
  474. if (target.isFullRepaint()) {
  475. injectedStyles.addAll(pendingInjections);
  476. pendingInjections = injectedStyles;
  477. injectedStyles = new LinkedHashSet<>();
  478. }
  479. if (!pendingInjections.isEmpty()) {
  480. target.startTag("css-injections");
  481. for (InjectedStyle pending : pendingInjections) {
  482. int id = injectedStyles.size();
  483. pending.paint(id, target);
  484. injectedStyles.add(pending);
  485. }
  486. pendingInjections.clear();
  487. target.endTag("css-injections");
  488. }
  489. }
  490. }
  491. private EventRouter eventRouter;
  492. private final UI uI;
  493. private int browserWindowWidth = -1;
  494. private int browserWindowHeight = -1;
  495. private JavaScript javaScript;
  496. private Styles styles;
  497. /**
  498. * The current browser location.
  499. */
  500. private URI location;
  501. private final PageState state;
  502. private String windowName;
  503. private String newPushState;
  504. private String newReplaceState;
  505. private List<Dependency> pendingDependencies;
  506. public Page(UI uI, PageState state) {
  507. this.uI = uI;
  508. this.state = state;
  509. }
  510. private Registration addListener(Class<?> eventType, SerializableEventListener listener,
  511. Method method) {
  512. if (!hasEventRouter()) {
  513. eventRouter = new EventRouter();
  514. }
  515. return eventRouter.addListener(eventType, listener, method);
  516. }
  517. private void removeListener(Class<?> eventType, SerializableEventListener listener,
  518. Method method) {
  519. if (hasEventRouter()) {
  520. eventRouter.removeListener(eventType, listener, method);
  521. }
  522. }
  523. /**
  524. * Adds a listener that gets notified every time the URI fragment of this
  525. * page is changed. Please note that the initial URI fragment has already
  526. * been set when a new UI is initialized, so there will not be any initial
  527. * event for listeners added during {@link UI#init(VaadinRequest)}.
  528. *
  529. * @see #getUriFragment()
  530. * @see #setUriFragment(String)
  531. * @see Registration
  532. *
  533. * @param listener
  534. * the URI fragment listener to add
  535. * @return a registration object for removing the listener
  536. * @deprecated Use {@link Page#addPopStateListener(PopStateListener)}
  537. * instead
  538. * @since 8.0
  539. */
  540. @Deprecated
  541. public Registration addUriFragmentChangedListener(
  542. Page.UriFragmentChangedListener listener) {
  543. return addListener(UriFragmentChangedEvent.class, listener,
  544. URI_FRAGMENT_CHANGED_METHOD);
  545. }
  546. /**
  547. * Adds a listener that gets notified every time the URI of this page is
  548. * changed due to back/forward functionality of the browser.
  549. * <p>
  550. * Note that one only gets notified when the back/forward button affects
  551. * history changes with-in same UI, created by
  552. * {@link Page#pushState(String)} or {@link Page#replaceState(String)}
  553. * functions.
  554. *
  555. * @see #getLocation()
  556. * @see Registration
  557. *
  558. * @param listener
  559. * the Popstate listener to add
  560. * @return a registration object for removing the listener
  561. * @since 8.0
  562. */
  563. public Registration addPopStateListener(Page.PopStateListener listener) {
  564. return addListener(PopStateEvent.class, listener, URI_CHANGED_METHOD);
  565. }
  566. /**
  567. * Removes a URI fragment listener that was previously added to this page.
  568. *
  569. * @param listener
  570. * the URI fragment listener to remove
  571. *
  572. * @see Page#addUriFragmentChangedListener(UriFragmentChangedListener)
  573. *
  574. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  575. * registration object returned from
  576. * {@link #addUriFragmentChangedListener(UriFragmentChangedListener)}.
  577. */
  578. @Deprecated
  579. public void removeUriFragmentChangedListener(
  580. Page.UriFragmentChangedListener listener) {
  581. removeListener(UriFragmentChangedEvent.class, listener,
  582. URI_FRAGMENT_CHANGED_METHOD);
  583. }
  584. /**
  585. * Sets the fragment part in the current location URI. Optionally fires a
  586. * {@link UriFragmentChangedEvent}.
  587. * <p>
  588. * The fragment is the optional last component of a URI, prefixed with a
  589. * hash sign ("#").
  590. * <p>
  591. * Passing an empty string as <code>newFragment</code> sets an empty
  592. * fragment (a trailing "#" in the URI.) Passing <code>null</code> if there
  593. * is already a non-null fragment will leave a trailing # in the URI since
  594. * removing it would cause the browser to reload the page. This is not fully
  595. * consistent with the semantics of {@link java.net.URI}.
  596. *
  597. * @param newUriFragment
  598. * The new fragment.
  599. * @param fireEvents
  600. * true to fire event
  601. *
  602. * @see #getUriFragment()
  603. * @see #setLocation(URI)
  604. * @see UriFragmentChangedEvent
  605. * @see Page.UriFragmentChangedListener
  606. *
  607. */
  608. public void setUriFragment(String newUriFragment, boolean fireEvents) {
  609. String oldUriFragment = location.getFragment();
  610. if (newUriFragment == null && getUriFragment() != null) {
  611. // Can't completely remove the fragment once it has been set, will
  612. // instead set it to the empty string
  613. newUriFragment = "";
  614. }
  615. if (newUriFragment == oldUriFragment || (newUriFragment != null
  616. && newUriFragment.equals(oldUriFragment))) {
  617. return;
  618. }
  619. try {
  620. location = new URI(location.getScheme(),
  621. location.getSchemeSpecificPart(), newUriFragment);
  622. pushState(location);
  623. } catch (URISyntaxException e) {
  624. // This should not actually happen as the fragment syntax is not
  625. // constrained
  626. throw new RuntimeException(e);
  627. }
  628. if (fireEvents) {
  629. fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
  630. }
  631. }
  632. private void fireEvent(EventObject event) {
  633. if (hasEventRouter()) {
  634. eventRouter.fireEvent(event);
  635. }
  636. }
  637. /**
  638. * Sets URI fragment. This method fires a {@link UriFragmentChangedEvent}
  639. *
  640. * @param newUriFragment
  641. * id of the new fragment
  642. * @see UriFragmentChangedEvent
  643. * @see Page.UriFragmentChangedListener
  644. */
  645. public void setUriFragment(String newUriFragment) {
  646. setUriFragment(newUriFragment, true);
  647. }
  648. /**
  649. * Gets the currently set URI fragment.
  650. * <p>
  651. * Returns <code>null</code> if there is no fragment and an empty string if
  652. * there is an empty fragment.
  653. * <p>
  654. * To listen to changes in fragment, hook a
  655. * {@link Page.UriFragmentChangedListener}.
  656. *
  657. * @return the current fragment in browser location URI.
  658. *
  659. * @see #getLocation()
  660. * @see #setUriFragment(String)
  661. * @see #addUriFragmentChangedListener(UriFragmentChangedListener)
  662. */
  663. public String getUriFragment() {
  664. return location.getFragment();
  665. }
  666. public void init(VaadinRequest request) {
  667. // NOTE: UI.refresh makes assumptions about the semantics of this
  668. // method.
  669. // It should be kept in sync if this method is changed.
  670. // Extract special parameter sent by vaadinBootstrap.js
  671. String location = request.getParameter("v-loc");
  672. String clientWidth = request.getParameter("v-cw");
  673. String clientHeight = request.getParameter("v-ch");
  674. windowName = request.getParameter("v-wn");
  675. if (location != null) {
  676. try {
  677. this.location = new URI(location);
  678. } catch (URISyntaxException e) {
  679. throw new RuntimeException(
  680. "Invalid location URI received from client", e);
  681. }
  682. }
  683. if (clientWidth != null && clientHeight != null) {
  684. try {
  685. browserWindowWidth = Integer.parseInt(clientWidth);
  686. browserWindowHeight = Integer.parseInt(clientHeight);
  687. } catch (NumberFormatException e) {
  688. throw new RuntimeException(
  689. "Invalid window size received from client", e);
  690. }
  691. }
  692. }
  693. public WebBrowser getWebBrowser() {
  694. return uI.getSession().getBrowser();
  695. }
  696. /**
  697. * Gets the window.name value of the browser window of this page.
  698. *
  699. * @since 7.2
  700. *
  701. * @return the window name, <code>null</code> if the name is not known
  702. */
  703. public String getWindowName() {
  704. return windowName;
  705. }
  706. /**
  707. * For internal use only. Updates the internal state with the given values.
  708. * Does not resize the Page or browser window.
  709. *
  710. * @deprecated As of 7.2, use
  711. * {@link #updateBrowserWindowSize(int, int, boolean)} instead.
  712. *
  713. * @param width
  714. * the new browser window width
  715. * @param height
  716. * the new browse window height
  717. */
  718. @Deprecated
  719. public void updateBrowserWindowSize(int width, int height) {
  720. updateBrowserWindowSize(width, height, true);
  721. }
  722. /**
  723. * For internal use only. Updates the internal state with the given values.
  724. * Does not resize the Page or browser window.
  725. *
  726. * @since 7.2
  727. *
  728. * @param width
  729. * the new browser window width
  730. * @param height
  731. * the new browser window height
  732. * @param fireEvents
  733. * whether to fire {@link BrowserWindowResizeEvent} if the size
  734. * changes
  735. */
  736. public void updateBrowserWindowSize(int width, int height,
  737. boolean fireEvents) {
  738. boolean sizeChanged = false;
  739. if (width != browserWindowWidth) {
  740. browserWindowWidth = width;
  741. sizeChanged = true;
  742. }
  743. if (height != browserWindowHeight) {
  744. browserWindowHeight = height;
  745. sizeChanged = true;
  746. }
  747. if (fireEvents && sizeChanged) {
  748. fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth,
  749. browserWindowHeight));
  750. }
  751. }
  752. /**
  753. * Adds a new {@link BrowserWindowResizeListener} to this UI. The listener
  754. * will be notified whenever the browser window within which this UI resides
  755. * is resized.
  756. * <p>
  757. * In most cases, the UI should be in lazy resize mode when using browser
  758. * window resize listeners. Otherwise, a large number of events can be
  759. * received while a resize is being performed. Use
  760. * {@link UI#setResizeLazy(boolean)}.
  761. * </p>
  762. *
  763. * @param resizeListener
  764. * the listener to add
  765. * @return a registration object for removing the listener
  766. *
  767. * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent)
  768. * @see UI#setResizeLazy(boolean)
  769. * @see Registration
  770. * @since 8.0
  771. */
  772. public Registration addBrowserWindowResizeListener(
  773. BrowserWindowResizeListener resizeListener) {
  774. Registration registration = addListener(BrowserWindowResizeEvent.class,
  775. resizeListener, BROWSER_RESIZE_METHOD);
  776. getState(true).hasResizeListeners = true;
  777. return () -> {
  778. registration.remove();
  779. getState(true).hasResizeListeners = hasEventRouter()
  780. && eventRouter.hasListeners(BrowserWindowResizeEvent.class);
  781. };
  782. }
  783. /**
  784. * Removes a {@link BrowserWindowResizeListener} from this UI. The listener
  785. * will no longer be notified when the browser window is resized.
  786. *
  787. * @param resizeListener
  788. * the listener to remove
  789. *
  790. * @deprecated As of 8.0, replaced by {@link Registration#remove()} in the
  791. * registration object returned from
  792. * {@link #addBrowserWindowResizeListener(BrowserWindowResizeListener)}
  793. * .
  794. */
  795. @Deprecated
  796. public void removeBrowserWindowResizeListener(
  797. BrowserWindowResizeListener resizeListener) {
  798. removeListener(BrowserWindowResizeEvent.class, resizeListener,
  799. BROWSER_RESIZE_METHOD);
  800. getState(true).hasResizeListeners = hasEventRouter()
  801. && eventRouter.hasListeners(BrowserWindowResizeEvent.class);
  802. }
  803. /**
  804. * Gets the last known height of the browser window in which this UI
  805. * resides.
  806. *
  807. * @return the browser window height in pixels
  808. */
  809. public int getBrowserWindowHeight() {
  810. return browserWindowHeight;
  811. }
  812. /**
  813. * Gets the last known width of the browser window in which this uI resides.
  814. *
  815. * @return the browser window width in pixels
  816. */
  817. public int getBrowserWindowWidth() {
  818. return browserWindowWidth;
  819. }
  820. public JavaScript getJavaScript() {
  821. if (javaScript == null) {
  822. // Create and attach on first use
  823. javaScript = new JavaScript();
  824. javaScript.extend(uI);
  825. }
  826. return javaScript;
  827. }
  828. /**
  829. * Returns that stylesheet associated with this Page. The stylesheet
  830. * contains additional styles injected at runtime into the HTML document.
  831. *
  832. * @since 7.1
  833. */
  834. public Styles getStyles() {
  835. if (styles == null) {
  836. styles = new Styles(uI);
  837. }
  838. return styles;
  839. }
  840. public void paintContent(PaintTarget target) throws PaintException {
  841. if (!openList.isEmpty()) {
  842. for (OpenResource anOpenList : openList) {
  843. (anOpenList).paintContent(target);
  844. }
  845. openList.clear();
  846. }
  847. if (newPushState != null) {
  848. target.addAttribute(UIConstants.ATTRIBUTE_PUSH_STATE, newPushState);
  849. newPushState = null;
  850. }
  851. if (newReplaceState != null) {
  852. target.addAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE,
  853. newReplaceState);
  854. newReplaceState = null;
  855. }
  856. if (styles != null) {
  857. styles.paint(target);
  858. }
  859. }
  860. /**
  861. * Navigates this page to the given URI. The contents of this page in the
  862. * browser is replaced with whatever is returned for the given URI.
  863. * <p>
  864. * This method should not be used to start downloads, as the client side
  865. * will assume the browser will navigate away when opening the URI. Use one
  866. * of the {@code Page.open} methods or {@code FileDownloader} instead.
  867. *
  868. * @see #open(String, String)
  869. * @see FileDownloader
  870. *
  871. * @param uri
  872. * the URI to show
  873. */
  874. public void setLocation(String uri) {
  875. openList.add(
  876. new OpenResource(uri, "_self", -1, -1, BORDER_DEFAULT, false));
  877. uI.markAsDirty();
  878. }
  879. /**
  880. * Navigates this page to the given URI. The contents of this page in the
  881. * browser is replaced with whatever is returned for the given URI.
  882. * <p>
  883. * This method should not be used to start downloads, as the client side
  884. * will assume the browser will navigate away when opening the URI. Use one
  885. * of the {@code Page.open} methods or {@code FileDownloader} instead.
  886. *
  887. * @see #open(String, String)
  888. * @see FileDownloader
  889. *
  890. * @param uri
  891. * the URI to show
  892. */
  893. public void setLocation(URI uri) {
  894. setLocation(uri.toString());
  895. }
  896. /**
  897. * Returns the location URI of this page, as reported by the browser. Note
  898. * that this may not be consistent with the server URI the application is
  899. * deployed in due to potential proxies, redirections and similar.
  900. *
  901. * @return The browser location URI.
  902. * @throws IllegalStateException
  903. * if the
  904. * {@link DeploymentConfiguration#isSendUrlsAsParameters()} is
  905. * set to {@code false}
  906. */
  907. public URI getLocation() throws IllegalStateException {
  908. if (location == null) {
  909. if (uI.getSession() != null && !uI.getSession().getConfiguration()
  910. .isSendUrlsAsParameters()) {
  911. throw new IllegalStateException("Location is not available as the "
  912. + Constants.SERVLET_PARAMETER_SENDURLSASPARAMETERS
  913. + " parameter is configured as false");
  914. } else if (VaadinSession.getCurrent() == null) {
  915. throw new IllegalStateException("Location is not available as the "
  916. + Constants.SERVLET_PARAMETER_SENDURLSASPARAMETERS
  917. + " parameter state cannot be determined");
  918. } else if (!VaadinSession.getCurrent().getConfiguration()
  919. .isSendUrlsAsParameters()) {
  920. throw new IllegalStateException("Location is not available as the "
  921. + Constants.SERVLET_PARAMETER_SENDURLSASPARAMETERS
  922. + " parameter is configured as false");
  923. }
  924. }
  925. return location;
  926. }
  927. /**
  928. * Updates the browsers URI without causing actual page change. This method
  929. * is useful if you wish implement "deep linking" to your application.
  930. * Calling the method also adds a new entry to clients browser history and
  931. * you can further use {@link PopStateListener} to track the usage of
  932. * back/forward feature in browser.
  933. * <p>
  934. * Note, the current implementation supports setting only one new uri in one
  935. * user interaction.
  936. *
  937. * @param uri
  938. * to be used for pushState operation. The URI is resolved over
  939. * the current location. If the given URI is absolute, it must be
  940. * of same origin as the current URI or the browser will not
  941. * accept the new value.
  942. * @since 8.0
  943. */
  944. public void pushState(String uri) {
  945. newPushState = uri;
  946. uI.markAsDirty();
  947. location = location.resolve(uri);
  948. }
  949. /**
  950. * Updates the browsers URI without causing actual page change. This method
  951. * is useful if you wish implement "deep linking" to your application.
  952. * Calling the method also adds a new entry to clients browser history and
  953. * you can further use {@link PopStateListener} to track the usage of
  954. * back/forward feature in browser.
  955. * <p>
  956. * Note, the current implementation supports setting only one new uri in one
  957. * user interaction.
  958. *
  959. * @param uri
  960. * the URI to be used for pushState operation. The URI is
  961. * resolved over the current location. If the given URI is
  962. * absolute, it must be of same origin as the current URI or the
  963. * browser will not accept the new value.
  964. * @since 8.0
  965. */
  966. public void pushState(URI uri) {
  967. pushState(uri.toString());
  968. }
  969. /**
  970. * Updates the browsers URI without causing actual page change in the same
  971. * way as {@link #pushState(String)}, but does not add new entry to browsers
  972. * history.
  973. *
  974. * @param uri
  975. * the URI to be used for replaceState operation. The URI is
  976. * resolved over the current location. If the given URI is
  977. * absolute, it must be of same origin as the current URI or the
  978. * browser will not accept the new value.
  979. * @since 8.0
  980. */
  981. public void replaceState(String uri) {
  982. newReplaceState = uri;
  983. uI.markAsDirty();
  984. location = location.resolve(uri);
  985. }
  986. /**
  987. * Updates the browsers URI without causing actual page change in the same
  988. * way as {@link #pushState(URI)}, but does not add new entry to browsers
  989. * history.
  990. *
  991. * @param uri
  992. * the URI to be used for replaceState operation. The URI is
  993. * resolved over the current location. If the given URI is
  994. * absolute, it must be of same origin as the current URI or the
  995. * browser will not accept the new value.
  996. * @since 8.0
  997. */
  998. public void replaceState(URI uri) {
  999. replaceState(uri.toString());
  1000. }
  1001. /**
  1002. * For internal use only. Used to update the server-side location when the
  1003. * client-side location changes.
  1004. *
  1005. * @deprecated As of 7.2, use {@link #updateLocation(String, boolean)}
  1006. * instead.
  1007. *
  1008. * @param location
  1009. * the new location URI
  1010. */
  1011. @Deprecated
  1012. public void updateLocation(String location) {
  1013. updateLocation(location, true, false);
  1014. }
  1015. /**
  1016. * For internal use only. Used to update the server-side location when the
  1017. * client-side location changes.
  1018. *
  1019. * @since 8.0
  1020. *
  1021. * @param location
  1022. * the new location URI
  1023. * @param fireEvents
  1024. * whether to fire {@link UriFragmentChangedEvent} if the URI
  1025. * fragment changes
  1026. * @param firePopstate
  1027. * whether to fire {@link PopStateEvent}
  1028. */
  1029. public void updateLocation(String location, boolean fireEvents,
  1030. boolean firePopstate) {
  1031. try {
  1032. String oldUriFragment = this.location.getFragment();
  1033. this.location = new URI(location);
  1034. String newUriFragment = this.location.getFragment();
  1035. if (fireEvents
  1036. && !SharedUtil.equals(oldUriFragment, newUriFragment)) {
  1037. fireEvent(new UriFragmentChangedEvent(this, newUriFragment));
  1038. }
  1039. if (firePopstate) {
  1040. fireEvent(new PopStateEvent(this, location));
  1041. }
  1042. } catch (URISyntaxException e) {
  1043. throw new RuntimeException(e);
  1044. }
  1045. }
  1046. /**
  1047. * Opens the given url in a window with the given name. Equivalent to
  1048. * {@link #open(String, String, boolean) open} (url, windowName, true) .
  1049. * <p>
  1050. * The supplied {@code windowName} is used as the target name in a
  1051. * window.open call in the client. This means that special values such as
  1052. * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
  1053. * <code>null</code> window name is also a special case.
  1054. * </p>
  1055. * <p>
  1056. * "", null and "_self" as {@code windowName} all causes the URL to be
  1057. * opened in the current window, replacing any old contents. For
  1058. * downloadable content you should avoid "_self" as "_self" causes the
  1059. * client to skip rendering of any other changes as it considers them
  1060. * irrelevant (the page will be replaced by the response from the URL). This
  1061. * can speed up the opening of a URL, but it might also put the client side
  1062. * into an inconsistent state if the window content is not completely
  1063. * replaced e.g., if the URL is downloaded instead of displayed in the
  1064. * browser.
  1065. * </p>
  1066. * <p>
  1067. * "_blank" as {@code windowName} causes the URL to always be opened in a
  1068. * new window or tab (depends on the browser and browser settings).
  1069. * </p>
  1070. * <p>
  1071. * "_top" and "_parent" as {@code windowName} works as specified by the HTML
  1072. * standard.
  1073. * </p>
  1074. * <p>
  1075. * Any other {@code windowName} will open the URL in a window with that
  1076. * name, either by opening a new window/tab in the browser or by replacing
  1077. * the contents of an existing window with that name.
  1078. * </p>
  1079. * <p>
  1080. * Please note that opening a popup window in this way may be blocked by the
  1081. * browser's popup-blocker because the new browser window is opened when
  1082. * processing a response from the server. To avoid this, you should instead
  1083. * use {@link Link} for opening the window because browsers are more
  1084. * forgiving when the window is opened directly from a client-side click
  1085. * event.
  1086. * </p>
  1087. *
  1088. * @param url
  1089. * the URL to open.
  1090. * @param windowName
  1091. * the name of the window.
  1092. */
  1093. public void open(String url, String windowName) {
  1094. open(url, windowName, true);
  1095. }
  1096. /**
  1097. * Opens the given url in a window with the given name. Equivalent to
  1098. * {@link #open(String, String, boolean) open} (url, windowName, true) .
  1099. * <p>
  1100. * The supplied {@code windowName} is used as the target name in a
  1101. * window.open call in the client. This means that special values such as
  1102. * "_blank", "_self", "_top", "_parent" have special meaning. An empty or
  1103. * <code>null</code> window name is also a special case.
  1104. * </p>
  1105. * <p>
  1106. * "", null and "_self" as {@code windowName} all causes the URL to be
  1107. * opened in the current window, replacing any old contents. For
  1108. * downloadable content you should avoid "_self" as "_self" causes the
  1109. * client to skip rendering of any other changes as it considers them
  1110. * irrelevant (the page will be replaced by the response from the URL). This
  1111. * can speed up the opening of a URL, but it might also put the client side
  1112. * into an inconsistent state if the window content is not completely
  1113. * replaced e.g., if the URL is downloaded instead of displayed in the
  1114. * browser.
  1115. * </p>
  1116. * <p>
  1117. * "_blank" as {@code windowName} causes the URL to always be opened in a
  1118. * new window or tab (depends on the browser and browser settings).
  1119. * </p>
  1120. * <p>
  1121. * "_top" and "_parent" as {@code windowName} works as specified by the HTML
  1122. * standard.
  1123. * </p>
  1124. * <p>
  1125. * Any other {@code windowName} will open the URL in a window with that
  1126. * name, either by opening a new window/tab in the browser or by replacing
  1127. * the contents of an existing window with that name.
  1128. * </p>
  1129. * <p>
  1130. * Please note that opening a popup window in this way may be blocked by the
  1131. * browser's popup-blocker because the new browser window is opened when
  1132. * processing a response from the server. To avoid this, you should instead
  1133. * use {@link Link} for opening the window because browsers are more
  1134. * forgiving when the window is opened directly from a client-side click
  1135. * event.
  1136. * </p>
  1137. *
  1138. * @param url
  1139. * the URL to open.
  1140. * @param windowName
  1141. * the name of the window.
  1142. * @param tryToOpenAsPopup
  1143. * Whether to try to force the resource to be opened in a new
  1144. * window
  1145. */
  1146. public void open(String url, String windowName, boolean tryToOpenAsPopup) {
  1147. openList.add(new OpenResource(url, windowName, -1, -1, BORDER_DEFAULT,
  1148. tryToOpenAsPopup));
  1149. uI.markAsDirty();
  1150. }
  1151. /**
  1152. * Opens the given URL in a window with the given size, border and name. For
  1153. * more information on the meaning of {@code windowName}, see
  1154. * {@link #open(String, String)}.
  1155. * <p>
  1156. * Please note that opening a popup window in this way may be blocked by the
  1157. * browser's popup-blocker because the new browser window is opened when
  1158. * processing a response from the server. To avoid this, you should instead
  1159. * use {@link Link} for opening the window because browsers are more
  1160. * forgiving when the window is opened directly from a client-side click
  1161. * event.
  1162. * </p>
  1163. *
  1164. * @param url
  1165. * the URL to open.
  1166. * @param windowName
  1167. * the name of the window.
  1168. * @param width
  1169. * the width of the window in pixels
  1170. * @param height
  1171. * the height of the window in pixels
  1172. * @param border
  1173. * the border style of the window.
  1174. */
  1175. public void open(String url, String windowName, int width, int height,
  1176. BorderStyle border) {
  1177. openList.add(
  1178. new OpenResource(url, windowName, width, height, border, true));
  1179. uI.markAsDirty();
  1180. }
  1181. /**
  1182. * @deprecated As of 7.0, only retained to maintain compatibility with
  1183. * LegacyWindow.open methods. See documentation for
  1184. * {@link LegacyWindow#open(Resource, String, int, int, BorderStyle)}
  1185. * for discussion about replacing API.
  1186. */
  1187. @Deprecated
  1188. public void open(Resource resource, String windowName, int width,
  1189. int height, BorderStyle border) {
  1190. openList.add(new OpenResource(resource, windowName, width, height,
  1191. border, true));
  1192. uI.markAsDirty();
  1193. }
  1194. /**
  1195. * @deprecated As of 7.0, only retained to maintain compatibility with
  1196. * LegacyWindow.open methods. See documentation for
  1197. * {@link LegacyWindow#open(Resource, String, boolean)} for
  1198. * discussion about replacing API.
  1199. */
  1200. @Deprecated
  1201. public void open(Resource resource, String windowName,
  1202. boolean tryToOpenAsPopup) {
  1203. openList.add(new OpenResource(resource, windowName, -1, -1,
  1204. BORDER_DEFAULT, tryToOpenAsPopup));
  1205. uI.markAsDirty();
  1206. }
  1207. /**
  1208. * Shows a notification message.
  1209. *
  1210. * @see Notification
  1211. *
  1212. * @param notification
  1213. * The notification message to show
  1214. *
  1215. * @deprecated As of 7.0, use Notification.show(Page) instead.
  1216. */
  1217. @Deprecated
  1218. public void showNotification(Notification notification) {
  1219. notification.show(this);
  1220. }
  1221. /**
  1222. * Gets the Page to which the current uI belongs. This is automatically
  1223. * defined when processing requests to the server. In other cases, (e.g.
  1224. * from background threads), the current uI is not automatically defined.
  1225. *
  1226. * @see UI#getCurrent()
  1227. *
  1228. * @return the current page instance if available, otherwise
  1229. * <code>null</code>
  1230. */
  1231. public static Page getCurrent() {
  1232. UI currentUI = UI.getCurrent();
  1233. if (currentUI == null) {
  1234. return null;
  1235. }
  1236. return currentUI.getPage();
  1237. }
  1238. /**
  1239. * Sets the page title. The page title is displayed by the browser e.g. as
  1240. * the title of the browser window or as the title of the tab.
  1241. * <p>
  1242. * If this value is set to null, the previously set page title will be left
  1243. * as-is. Set to empty string to clear the title.
  1244. *
  1245. * @param title
  1246. * the page title to set
  1247. */
  1248. public void setTitle(String title) {
  1249. getState(true).title = title;
  1250. }
  1251. /**
  1252. * Reloads the page in the browser.
  1253. */
  1254. public void reload() {
  1255. uI.getRpcProxy(PageClientRpc.class).reload();
  1256. }
  1257. /**
  1258. * Returns the page state.
  1259. * <p>
  1260. * The page state is transmitted to UIConnector together with
  1261. * {@link UIState} rather than as an individual entity.
  1262. * </p>
  1263. * <p>
  1264. * The state should be considered an internal detail of Page. Classes
  1265. * outside of Page should not access it directly but only through public
  1266. * APIs provided by Page.
  1267. * </p>
  1268. *
  1269. * @since 7.1
  1270. * @param markAsDirty
  1271. * true to mark the state as dirty
  1272. * @return PageState object that can be read in any case and modified if
  1273. * markAsDirty is true
  1274. */
  1275. protected PageState getState(boolean markAsDirty) {
  1276. if (markAsDirty) {
  1277. uI.markAsDirty();
  1278. }
  1279. return state;
  1280. }
  1281. private boolean hasEventRouter() {
  1282. return eventRouter != null;
  1283. }
  1284. /**
  1285. * Add a dependency that should be added to the current page.
  1286. * <p>
  1287. * These dependencies are always added before the dependencies included by
  1288. * using the annotations {@link HtmlImport}, {@link JavaScript} and
  1289. * {@link StyleSheet} during the same request.
  1290. * <p>
  1291. * Please note that these dependencies are always sent to the client side
  1292. * and not filtered out by any {@link DependencyFilter}.
  1293. *
  1294. * @param dependency
  1295. * the dependency to add
  1296. * @since 8.1
  1297. */
  1298. public void addDependency(Dependency dependency) {
  1299. if (pendingDependencies == null) {
  1300. pendingDependencies = new ArrayList<>();
  1301. }
  1302. pendingDependencies.add(dependency);
  1303. }
  1304. /**
  1305. * Returns all pending dependencies.
  1306. * <p>
  1307. * For internal use only, calling this method will clear the pending
  1308. * dependencies.
  1309. *
  1310. * @return the pending dependencies to the current page
  1311. * @since 8.1
  1312. */
  1313. public Collection<Dependency> getPendingDependencies() {
  1314. List<Dependency> copy = new ArrayList<>();
  1315. if (pendingDependencies != null) {
  1316. copy.addAll(pendingDependencies);
  1317. }
  1318. pendingDependencies = null;
  1319. return copy;
  1320. }
  1321. /**
  1322. * Returns the {@link UI} of this {@link Page}.
  1323. *
  1324. * @return the {@link UI} of this {@link Page}.
  1325. *
  1326. * @since 8.2
  1327. */
  1328. public UI getUI() {
  1329. return uI;
  1330. }
  1331. }