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

11 jaren geleden
11 jaren geleden
11 jaren geleden
11 jaren geleden
11 jaren geleden
11 jaren geleden
11 jaren geleden
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. }