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

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