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.

ApplicationConfiguration.java 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  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.client;
  17. import java.util.ArrayList;
  18. import java.util.HashMap;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.logging.Handler;
  23. import java.util.logging.Level;
  24. import java.util.logging.Logger;
  25. import com.google.gwt.core.client.EntryPoint;
  26. import com.google.gwt.core.client.GWT;
  27. import com.google.gwt.core.client.JavaScriptObject;
  28. import com.google.gwt.core.client.JsArrayString;
  29. import com.google.gwt.core.client.RunAsyncCallback;
  30. import com.google.gwt.core.client.Scheduler;
  31. import com.google.gwt.core.client.ScriptInjector;
  32. import com.google.gwt.dom.client.Element;
  33. import com.google.gwt.logging.client.LogConfiguration;
  34. import com.google.gwt.user.client.Command;
  35. import com.google.gwt.user.client.Window;
  36. import com.vaadin.client.debug.internal.ErrorNotificationHandler;
  37. import com.vaadin.client.debug.internal.HierarchySection;
  38. import com.vaadin.client.debug.internal.InfoSection;
  39. import com.vaadin.client.debug.internal.LogSection;
  40. import com.vaadin.client.debug.internal.NetworkSection;
  41. import com.vaadin.client.debug.internal.ProfilerSection;
  42. import com.vaadin.client.debug.internal.Section;
  43. import com.vaadin.client.debug.internal.TestBenchSection;
  44. import com.vaadin.client.debug.internal.VDebugWindow;
  45. import com.vaadin.client.debug.internal.theme.DebugWindowStyles;
  46. import com.vaadin.client.event.PointerEventSupport;
  47. import com.vaadin.client.metadata.NoDataException;
  48. import com.vaadin.client.metadata.TypeData;
  49. import com.vaadin.client.ui.UnknownComponentConnector;
  50. import com.vaadin.client.ui.UnknownExtensionConnector;
  51. import com.vaadin.client.ui.ui.UIConnector;
  52. import com.vaadin.shared.ApplicationConstants;
  53. import com.vaadin.shared.ui.ui.UIConstants;
  54. public class ApplicationConfiguration implements EntryPoint {
  55. /**
  56. * Helper class for reading configuration options from the bootstap
  57. * javascript
  58. *
  59. * @since 7.0
  60. */
  61. private static class JsoConfiguration extends JavaScriptObject {
  62. protected JsoConfiguration() {
  63. // JSO Constructor
  64. }
  65. /**
  66. * Reads a configuration parameter as a string. Please note that the
  67. * javascript value of the parameter should also be a string, or else an
  68. * undefined exception may be thrown.
  69. *
  70. * @param name
  71. * name of the configuration parameter
  72. * @return value of the configuration parameter, or <code>null</code> if
  73. * not defined
  74. */
  75. private native String getConfigString(String name)
  76. /*-{
  77. var value = this.getConfig(name);
  78. if (value === null || value === undefined) {
  79. return null;
  80. } else {
  81. return value +"";
  82. }
  83. }-*/;
  84. /**
  85. * Reads a configuration parameter as a boolean object. Please note that
  86. * the javascript value of the parameter should also be a boolean, or
  87. * else an undefined exception may be thrown.
  88. *
  89. * @param name
  90. * name of the configuration parameter
  91. * @return boolean value of the configuration parameter, or
  92. * <code>null</code> if no value is defined
  93. */
  94. private native Boolean getConfigBoolean(String name)
  95. /*-{
  96. var value = this.getConfig(name);
  97. if (value === null || value === undefined) {
  98. return null;
  99. } else {
  100. // $entry not needed as function is not exported
  101. return @java.lang.Boolean::valueOf(Z)(value);
  102. }
  103. }-*/;
  104. /**
  105. * Reads a configuration parameter as an integer object. Please note
  106. * that the javascript value of the parameter should also be an integer,
  107. * or else an undefined exception may be thrown.
  108. *
  109. * @param name
  110. * name of the configuration parameter
  111. * @return integer value of the configuration parameter, or
  112. * <code>null</code> if no value is defined
  113. */
  114. private native Integer getConfigInteger(String name)
  115. /*-{
  116. var value = this.getConfig(name);
  117. if (value === null || value === undefined) {
  118. return null;
  119. } else {
  120. // $entry not needed as function is not exported
  121. return @java.lang.Integer::valueOf(I)(value);
  122. }
  123. }-*/;
  124. /**
  125. * Reads a configuration parameter as an {@link ErrorMessage} object.
  126. * Please note that the javascript value of the parameter should also be
  127. * an object with appropriate fields, or else an undefined exception may
  128. * be thrown when calling this method or when calling methods on the
  129. * returned object.
  130. *
  131. * @param name
  132. * name of the configuration parameter
  133. * @return error message with the given name, or <code>null</code> if no
  134. * value is defined
  135. */
  136. private native ErrorMessage getConfigError(String name)
  137. /*-{
  138. return this.getConfig(name);
  139. }-*/;
  140. /**
  141. * Reads a configuration parameter as an {@link Element} object. Please
  142. * note that the javascript value of the parameter should also be an
  143. * Element object, or else an undefined exception may be thrown when
  144. * calling this method or methods on the returned object.
  145. *
  146. * @param name
  147. * name of the configuration parameter
  148. * @return element for the configuration parameter, or <code>null</code>
  149. * if no value is defined
  150. * @since 8.4
  151. */
  152. private native Element getConfigElement(String name)
  153. /*-{
  154. return this.getConfig(name);
  155. }-*/;
  156. /**
  157. * Returns a native javascript object containing version information
  158. * from the server.
  159. *
  160. * @return a javascript object with the version information
  161. */
  162. private native JavaScriptObject getVersionInfoJSObject()
  163. /*-{
  164. return this.getConfig("versionInfo");
  165. }-*/;
  166. /**
  167. * Gets the version of the Vaadin framework used on the server.
  168. *
  169. * @return a string with the version
  170. *
  171. * @see com.vaadin.server.VaadinServlet#VERSION
  172. */
  173. private native String getVaadinVersion()
  174. /*-{
  175. return this.getConfig("versionInfo").vaadinVersion;
  176. }-*/;
  177. /**
  178. * Gets the version of the Atmosphere framework.
  179. *
  180. * @return a string with the version
  181. *
  182. * @see org.atmosphere.util#getRawVersion()
  183. */
  184. private native String getAtmosphereVersion()
  185. /*-{
  186. return this.getConfig("versionInfo").atmosphereVersion;
  187. }-*/;
  188. /**
  189. * Gets the JS version used in the Atmosphere framework.
  190. *
  191. * @return a string with the version
  192. */
  193. private native String getAtmosphereJSVersion()
  194. /*-{
  195. if ($wnd.vaadinPush && $wnd.vaadinPush.atmosphere) {
  196. return $wnd.vaadinPush.atmosphere.version;
  197. } else {
  198. return null;
  199. }
  200. }-*/;
  201. private native String getUIDL()
  202. /*-{
  203. return this.getConfig("uidl");
  204. }-*/;
  205. }
  206. /**
  207. * Wraps a native javascript object containing fields for an error message.
  208. *
  209. * @since 7.0
  210. */
  211. public static final class ErrorMessage extends JavaScriptObject {
  212. protected ErrorMessage() {
  213. // JSO constructor
  214. }
  215. public final native String getCaption()
  216. /*-{
  217. return this.caption;
  218. }-*/;
  219. public final native String getMessage()
  220. /*-{
  221. return this.message;
  222. }-*/;
  223. public final native String getUrl()
  224. /*-{
  225. return this.url;
  226. }-*/;
  227. }
  228. private static WidgetSet widgetSet = GWT.create(WidgetSet.class);
  229. private String id;
  230. /**
  231. * The URL to the VAADIN directory containing themes and widgetsets. Should
  232. * always end with a slash (/).
  233. */
  234. private String vaadinDirUrl;
  235. private String frontendUrl;
  236. private String serviceUrl;
  237. private String contextRootUrl;
  238. private int uiId;
  239. private boolean standalone;
  240. private ErrorMessage communicationError;
  241. private ErrorMessage authorizationError;
  242. private ErrorMessage sessionExpiredError;
  243. private int heartbeatInterval;
  244. private Map<Integer, String> unknownComponents;
  245. private Map<Integer, Class<? extends ServerConnector>> classes = new HashMap<>();
  246. private boolean widgetsetVersionSent = false;
  247. private static boolean moduleLoaded = false;
  248. static// TODO consider to make this hashmap per application
  249. List<Command> callbacks = new LinkedList<>();
  250. private static int dependenciesLoading;
  251. private static List<ApplicationConnection> runningApplications = new ArrayList<>();
  252. private Map<Integer, Integer> componentInheritanceMap = new HashMap<>();
  253. private Map<Integer, String> tagToServerSideClassName = new HashMap<>();
  254. private Element rootElement;
  255. /**
  256. * Checks whether path info in requests to the server-side service should be
  257. * in a request parameter (named <code>v-resourcePath</code>) or appended to
  258. * the end of the service URL.
  259. *
  260. * @see #getServiceUrl()
  261. *
  262. * @return <code>true</code> if path info should be a request parameter;
  263. * <code>false</code> if the path info goes after the service URL
  264. */
  265. public boolean useServiceUrlPathParam() {
  266. return getServiceUrlParameterName() != null;
  267. }
  268. /**
  269. * Return the name of the parameter used to to send data to the service url.
  270. * This method should only be called if {@link #useServiceUrlPathParam()} is
  271. * true.
  272. *
  273. * @since 7.1.6
  274. * @return The parameter name, by default <code>v-resourcePath</code>
  275. */
  276. public String getServiceUrlParameterName() {
  277. return getJsoConfiguration(id).getConfigString(
  278. ApplicationConstants.SERVICE_URL_PARAMETER_NAME);
  279. }
  280. public String getRootPanelId() {
  281. return id;
  282. }
  283. /**
  284. * Gets the URL to the server-side VaadinService. If
  285. * {@link #useServiceUrlPathParam()} return <code>true</code>, the requested
  286. * path info should be in the <code>v-resourcePath</code> query parameter;
  287. * else the path info should be appended to the end of the URL.
  288. *
  289. * @see #useServiceUrlPathParam()
  290. *
  291. * @return the URL to the server-side service as a string
  292. */
  293. public String getServiceUrl() {
  294. return serviceUrl;
  295. }
  296. /**
  297. * Gets the URL to the context root of the web application.
  298. *
  299. * @return the URL to the server-side context root as a string
  300. *
  301. * @since 8.0.3
  302. */
  303. public String getContextRootUrl() {
  304. return contextRootUrl;
  305. }
  306. /**
  307. * @return the theme name used when initializing the application
  308. * @deprecated as of 7.3. Use {@link UIConnector#getActiveTheme()} to get
  309. * the theme currently in use
  310. */
  311. @Deprecated
  312. public String getThemeName() {
  313. return getJsoConfiguration(id).getConfigString("theme");
  314. }
  315. /**
  316. * Gets the URL of the VAADIN directory on the server.
  317. *
  318. * @return the URL of the VAADIN directory
  319. */
  320. public String getVaadinDirUrl() {
  321. return vaadinDirUrl;
  322. }
  323. /**
  324. * Gets the URL of the that the {@literal frontend://} protocol should
  325. * resolve to.
  326. *
  327. * @return the URL of the frontend protocol
  328. * @since 8.1
  329. */
  330. public String getFrontendUrl() {
  331. return frontendUrl;
  332. }
  333. public void setAppId(String appId) {
  334. id = appId;
  335. }
  336. /**
  337. * Gets the initial UIDL from the DOM, if it was provided during the init
  338. * process.
  339. *
  340. * @return
  341. */
  342. public String getUIDL() {
  343. return getJsoConfiguration(id).getUIDL();
  344. }
  345. /**
  346. * @return true if the application is served by std. Vaadin servlet and is
  347. * considered to be the only or main content of the host page.
  348. */
  349. public boolean isStandalone() {
  350. return standalone;
  351. }
  352. /**
  353. * Gets the UI id of the server-side UI associated with this client-side
  354. * instance. The UI id should be included in every request originating from
  355. * this instance in order to associate the request with the right UI
  356. * instance on the server.
  357. *
  358. * @return the UI id
  359. */
  360. public int getUIId() {
  361. return uiId;
  362. }
  363. /**
  364. * @return The interval in seconds between heartbeat requests, or a
  365. * non-positive number if heartbeat is disabled.
  366. */
  367. public int getHeartbeatInterval() {
  368. return heartbeatInterval;
  369. }
  370. public JavaScriptObject getVersionInfoJSObject() {
  371. return getJsoConfiguration(id).getVersionInfoJSObject();
  372. }
  373. public ErrorMessage getCommunicationError() {
  374. return communicationError;
  375. }
  376. public ErrorMessage getAuthorizationError() {
  377. return authorizationError;
  378. }
  379. public ErrorMessage getSessionExpiredError() {
  380. return sessionExpiredError;
  381. }
  382. /**
  383. * Reads the configuration values defined by the bootstrap javascript.
  384. */
  385. private void loadFromDOM() {
  386. JsoConfiguration jsoConfiguration = getJsoConfiguration(id);
  387. serviceUrl = jsoConfiguration
  388. .getConfigString(ApplicationConstants.SERVICE_URL);
  389. if (serviceUrl == null || serviceUrl.isEmpty()) {
  390. /*
  391. * Use the current url without query parameters and fragment as the
  392. * default value.
  393. */
  394. serviceUrl = Window.Location.getHref().replaceFirst("[?#].*", "");
  395. } else {
  396. /*
  397. * Resolve potentially relative URLs to ensure they point to the
  398. * desired locations even if the base URL of the page changes later
  399. * (e.g. with pushState)
  400. */
  401. serviceUrl = WidgetUtil.getAbsoluteUrl(serviceUrl);
  402. }
  403. // Ensure there's an ending slash (to make appending e.g. UIDL work)
  404. if (!useServiceUrlPathParam() && !serviceUrl.endsWith("/")) {
  405. serviceUrl += '/';
  406. }
  407. contextRootUrl = jsoConfiguration
  408. .getConfigString(ApplicationConstants.CONTEXT_ROOT_URL);
  409. vaadinDirUrl = WidgetUtil.getAbsoluteUrl(jsoConfiguration
  410. .getConfigString(ApplicationConstants.VAADIN_DIR_URL));
  411. frontendUrl = WidgetUtil.getAbsoluteUrl(jsoConfiguration
  412. .getConfigString(ApplicationConstants.FRONTEND_URL));
  413. uiId = jsoConfiguration.getConfigInteger(UIConstants.UI_ID_PARAMETER)
  414. .intValue();
  415. // null -> false
  416. standalone = jsoConfiguration
  417. .getConfigBoolean("standalone") == Boolean.TRUE;
  418. heartbeatInterval = jsoConfiguration
  419. .getConfigInteger("heartbeatInterval");
  420. communicationError = jsoConfiguration.getConfigError("comErrMsg");
  421. authorizationError = jsoConfiguration.getConfigError("authErrMsg");
  422. sessionExpiredError = jsoConfiguration.getConfigError("sessExpMsg");
  423. rootElement = jsoConfiguration.getConfigElement("rootElement");
  424. }
  425. /**
  426. * Starts the application with a given id by reading the configuration
  427. * options stored by the bootstrap javascript.
  428. *
  429. * @param applicationId
  430. * id of the application to load, this is also the id of the html
  431. * element into which the application should be rendered.
  432. */
  433. public static void startApplication(final String applicationId) {
  434. Scheduler.get().scheduleDeferred(() -> {
  435. Profiler.enter("ApplicationConfiguration.startApplication");
  436. ApplicationConfiguration appConf = getConfigFromDOM(applicationId);
  437. ApplicationConnection a = GWT.create(ApplicationConnection.class);
  438. a.init(widgetSet, appConf);
  439. runningApplications.add(a);
  440. Profiler.leave("ApplicationConfiguration.startApplication");
  441. a.start();
  442. });
  443. }
  444. public static List<ApplicationConnection> getRunningApplications() {
  445. return runningApplications;
  446. }
  447. /**
  448. * Gets the configuration object for a specific application from the
  449. * bootstrap javascript.
  450. *
  451. * @param appId
  452. * the id of the application to get configuration data for
  453. * @return a native javascript object containing the configuration data
  454. */
  455. private static native JsoConfiguration getJsoConfiguration(String appId)
  456. /*-{
  457. return $wnd.vaadin.getApp(appId);
  458. }-*/;
  459. public static ApplicationConfiguration getConfigFromDOM(String appId) {
  460. ApplicationConfiguration conf = new ApplicationConfiguration();
  461. conf.setAppId(appId);
  462. conf.loadFromDOM();
  463. return conf;
  464. }
  465. public String getServletVersion() {
  466. return getJsoConfiguration(id).getVaadinVersion();
  467. }
  468. /**
  469. * Return Atmosphere version.
  470. *
  471. * @since 7.4
  472. *
  473. * @return Atmosphere version.
  474. */
  475. public String getAtmosphereVersion() {
  476. return getJsoConfiguration(id).getAtmosphereVersion();
  477. }
  478. /**
  479. * Return Atmosphere JS version.
  480. *
  481. * @since 7.4
  482. *
  483. * @return Atmosphere JS version.
  484. */
  485. public String getAtmosphereJSVersion() {
  486. return getJsoConfiguration(id).getAtmosphereJSVersion();
  487. }
  488. public Class<? extends ServerConnector> getConnectorClassByEncodedTag(
  489. int tag) {
  490. Class<? extends ServerConnector> type = classes.get(tag);
  491. if (type == null && !classes.containsKey(tag)) {
  492. // Initialize if not already loaded
  493. Integer currentTag = Integer.valueOf(tag);
  494. while (type == null && currentTag != null) {
  495. String serverSideClassNameForTag = getServerSideClassNameForTag(
  496. currentTag);
  497. if (TypeData.hasIdentifier(serverSideClassNameForTag)) {
  498. try {
  499. type = (Class<? extends ServerConnector>) TypeData
  500. .getClass(serverSideClassNameForTag);
  501. } catch (NoDataException e) {
  502. throw new RuntimeException(e);
  503. }
  504. }
  505. currentTag = getParentTag(currentTag.intValue());
  506. }
  507. if (type == null) {
  508. if (isExtensionType(tag)) {
  509. type = UnknownExtensionConnector.class;
  510. } else {
  511. type = UnknownComponentConnector.class;
  512. }
  513. if (unknownComponents == null) {
  514. unknownComponents = new HashMap<>();
  515. }
  516. unknownComponents.put(tag, getServerSideClassNameForTag(tag));
  517. }
  518. classes.put(tag, type);
  519. }
  520. return type;
  521. }
  522. private boolean isExtensionType(int tag) {
  523. Integer currentTag = Integer.valueOf(tag);
  524. while (currentTag != null) {
  525. String serverSideClassNameForTag = getServerSideClassNameForTag(
  526. currentTag);
  527. if ("com.vaadin.server.AbstractExtension"
  528. .equals(serverSideClassNameForTag)) {
  529. return true;
  530. }
  531. currentTag = getParentTag(currentTag.intValue());
  532. }
  533. return false;
  534. }
  535. public void addComponentInheritanceInfo(ValueMap valueMap) {
  536. JsArrayString keyArray = valueMap.getKeyArray();
  537. for (int i = 0; i < keyArray.length(); i++) {
  538. String key = keyArray.get(i);
  539. int value = valueMap.getInt(key);
  540. componentInheritanceMap.put(Integer.parseInt(key), value);
  541. }
  542. }
  543. public void addComponentMappings(ValueMap valueMap, WidgetSet widgetSet) {
  544. JsArrayString keyArray = valueMap.getKeyArray();
  545. for (int i = 0; i < keyArray.length(); i++) {
  546. String key = keyArray.get(i).intern();
  547. int value = valueMap.getInt(key);
  548. tagToServerSideClassName.put(value, key);
  549. }
  550. for (int i = 0; i < keyArray.length(); i++) {
  551. String key = keyArray.get(i).intern();
  552. int value = valueMap.getInt(key);
  553. widgetSet.ensureConnectorLoaded(value, this);
  554. }
  555. }
  556. /**
  557. * Returns all tags for given class. Tags are used in
  558. * {@link ApplicationConfiguration} to keep track of different classes and
  559. * their hierarchy
  560. *
  561. * @since 7.2
  562. * @param classname
  563. * name of class which tags we want
  564. * @return Integer array of tags pointing to this classname
  565. */
  566. public Integer[] getTagsForServerSideClassName(String classname) {
  567. List<Integer> tags = new ArrayList<>();
  568. for (Map.Entry<Integer, String> entry : tagToServerSideClassName
  569. .entrySet()) {
  570. if (classname.equals(entry.getValue())) {
  571. tags.add(entry.getKey());
  572. }
  573. }
  574. Integer[] out = new Integer[tags.size()];
  575. return tags.toArray(out);
  576. }
  577. public Integer getParentTag(int tag) {
  578. return componentInheritanceMap.get(tag);
  579. }
  580. public String getServerSideClassNameForTag(Integer tag) {
  581. return tagToServerSideClassName.get(tag);
  582. }
  583. String getUnknownServerClassNameByTag(int tag) {
  584. if (unknownComponents != null) {
  585. String className = unknownComponents.get(tag);
  586. if (className == null) {
  587. className = "unknown class with id " + tag;
  588. }
  589. return className;
  590. }
  591. return null;
  592. }
  593. /**
  594. * Runs the given command when all pending dependencies have been loaded, or
  595. * immediately if no dependencies are being loaded.
  596. *
  597. * @since 7.6
  598. * @param command
  599. * the command to run
  600. */
  601. public static void runWhenDependenciesLoaded(Command command) {
  602. if (dependenciesLoading == 0) {
  603. command.execute();
  604. } else {
  605. callbacks.add(command);
  606. }
  607. }
  608. static void startDependencyLoading() {
  609. dependenciesLoading++;
  610. }
  611. static void endDependencyLoading() {
  612. dependenciesLoading--;
  613. if (dependenciesLoading == 0 && !callbacks.isEmpty()) {
  614. for (Command cmd : callbacks) {
  615. cmd.execute();
  616. }
  617. callbacks.clear();
  618. }
  619. }
  620. private boolean vaadinBootstrapLoaded() {
  621. Element window = ScriptInjector.TOP_WINDOW.cast();
  622. return window.getPropertyJSO("vaadin") != null;
  623. }
  624. @Override
  625. public void onModuleLoad() {
  626. // Don't run twice if the module has been inherited several times,
  627. // and don't continue if vaadinBootstrap was not executed.
  628. if (moduleLoaded || !vaadinBootstrapLoaded()) {
  629. getLogger().log(Level.WARNING,
  630. "vaadinBootstrap.js was not loaded, skipping vaadin application configuration.");
  631. return;
  632. }
  633. moduleLoaded = true;
  634. Profiler.initialize();
  635. Profiler.enter("ApplicationConfiguration.onModuleLoad");
  636. BrowserInfo browserInfo = BrowserInfo.get();
  637. // Enable iOS6 cast fix (see #10460)
  638. if (browserInfo.isIOS6() && browserInfo.isWebkit()) {
  639. enableIOS6castFix();
  640. }
  641. // Enable IE prompt fix (#13367)
  642. if (browserInfo.isIE() && browserInfo.getBrowserMajorVersion() >= 10) {
  643. enableIEPromptFix();
  644. }
  645. // Register pointer events (must be done before any events are used)
  646. PointerEventSupport.init();
  647. if (LogConfiguration.loggingIsEnabled()) {
  648. GWT.setUncaughtExceptionHandler(throwable -> {
  649. /*
  650. * If the debug window is not enabled (?debug), this will not
  651. * show anything to normal users. "a1 is not an object" style
  652. * errors helps nobody, especially end user. It does not work
  653. * tells just as much.
  654. */
  655. getLogger().log(Level.SEVERE, throwable.getMessage(),
  656. throwable);
  657. });
  658. if (isProductionMode()) {
  659. // Disable all logging if in production mode
  660. Logger.getLogger("").setLevel(Level.OFF);
  661. }
  662. }
  663. Profiler.leave("ApplicationConfiguration.onModuleLoad");
  664. if (SuperDevMode.enableBasedOnParameter()) {
  665. // Do not start any application as super dev mode will refresh the
  666. // page once done compiling
  667. return;
  668. }
  669. if (isDebugMode()) {
  670. // Load debug window bundle and continue the bootstrap sequence once
  671. // it's loaded
  672. GWT.runAsync(VDebugWindow.class, new RunAsyncCallback() {
  673. @Override
  674. public void onSuccess() {
  675. initDebugWindow();
  676. registerCallback(GWT.getModuleName());
  677. }
  678. @Override
  679. public void onFailure(Throwable reason) {
  680. Window.alert("Failed to load Vaadin debug window");
  681. registerCallback(GWT.getModuleName());
  682. }
  683. });
  684. } else {
  685. // Continue the bootstrap sequence right away
  686. registerCallback(GWT.getModuleName());
  687. }
  688. }
  689. private static void initDebugWindow() {
  690. /*
  691. * XXX Lots of implementation details here right now. This should be
  692. * cleared up when an API for extending the debug window is implemented.
  693. */
  694. VDebugWindow window = VDebugWindow.get();
  695. if (LogConfiguration.loggingIsEnabled()) {
  696. window.addSection((Section) GWT.create(LogSection.class));
  697. }
  698. window.addSection((Section) GWT.create(InfoSection.class));
  699. window.addSection((Section) GWT.create(HierarchySection.class));
  700. window.addSection((Section) GWT.create(NetworkSection.class));
  701. window.addSection((Section) GWT.create(TestBenchSection.class));
  702. if (Profiler.isEnabled()) {
  703. window.addSection((Section) GWT.create(ProfilerSection.class));
  704. }
  705. if (isQuietDebugMode()) {
  706. window.close();
  707. } else {
  708. DebugWindowStyles dws = GWT.create(DebugWindowStyles.class);
  709. dws.css().ensureInjected();
  710. window.init();
  711. }
  712. // Connect to the legacy API
  713. VConsole.setImplementation(window);
  714. Handler errorNotificationHandler = GWT
  715. .create(ErrorNotificationHandler.class);
  716. Logger.getLogger("").addHandler(errorNotificationHandler);
  717. }
  718. /**
  719. * Fix to iOS6 failing when comparing with 0 directly after the kind of
  720. * comparison done by GWT when a double or float is cast to an int. Forcing
  721. * another trivial operation (other than a compare to 0) after the dangerous
  722. * comparison makes the issue go away. See #10460.
  723. */
  724. private static native void enableIOS6castFix()
  725. /*-{
  726. Math.max = function(a,b) {return (a > b === 1 < 2)? a : b}
  727. Math.min = function(a,b) {return (a < b === 1 < 2)? a : b}
  728. }-*/;
  729. /**
  730. * Make Metro versions of IE suggest switching to the desktop when
  731. * window.prompt is called.
  732. */
  733. private static native void enableIEPromptFix()
  734. /*-{
  735. var prompt = $wnd.prompt;
  736. $wnd.prompt = function () {
  737. var result = prompt.apply($wnd, Array.prototype.slice.call(arguments));
  738. if (result === undefined) {
  739. // force the browser to suggest desktop mode
  740. showModalDialog();
  741. return null;
  742. } else {
  743. return result;
  744. }
  745. };
  746. }-*/;
  747. /**
  748. * Registers that callback that the bootstrap javascript uses to start
  749. * applications once the widgetset is loaded and all required information is
  750. * available.
  751. *
  752. * @param widgetsetName
  753. * the name of this widgetset
  754. */
  755. public static native void registerCallback(String widgetsetName)
  756. /*-{
  757. var callbackHandler = $entry(@com.vaadin.client.ApplicationConfiguration::startApplication(Ljava/lang/String;));
  758. $wnd.vaadin.registerWidgetset(widgetsetName, callbackHandler);
  759. }-*/;
  760. /**
  761. * Checks if client side is in debug mode. Practically this is invoked by
  762. * adding ?debug parameter to URI. Please note that debug mode is always
  763. * disabled if production mode is enabled, but disabling production mode
  764. * does not automatically enable debug mode.
  765. *
  766. * @see #isProductionMode()
  767. *
  768. * @return true if client side is currently been debugged
  769. */
  770. public static boolean isDebugMode() {
  771. return isDebugAvailable()
  772. && Window.Location.getParameter("debug") != null;
  773. }
  774. /**
  775. * Checks if production mode is enabled. When production mode is enabled,
  776. * client-side logging is disabled. There may also be other performance
  777. * optimizations.
  778. *
  779. * @since 7.1.2
  780. * @return <code>true</code> if production mode is enabled; otherwise
  781. * <code>false</code>.
  782. */
  783. public static boolean isProductionMode() {
  784. return !isDebugAvailable();
  785. }
  786. private static native boolean isDebugAvailable()
  787. /*-{
  788. return $wnd.vaadin.debug;
  789. }-*/;
  790. /**
  791. * Checks whether debug logging should be quiet.
  792. *
  793. * @return <code>true</code> if debug logging should be quiet
  794. */
  795. public static boolean isQuietDebugMode() {
  796. String debugParameter = Window.Location.getParameter("debug");
  797. return isDebugAvailable() && debugParameter != null
  798. && debugParameter.startsWith("q");
  799. }
  800. /**
  801. * Checks whether the widget set version has been sent to the server. It is
  802. * sent in the first UIDL request.
  803. *
  804. * @return <code>true</code> if browser information has already been sent
  805. */
  806. public boolean isWidgetsetVersionSent() {
  807. return widgetsetVersionSent;
  808. }
  809. /**
  810. * Registers that the widget set version has been sent to the server.
  811. */
  812. public void setWidgetsetVersionSent() {
  813. widgetsetVersionSent = true;
  814. }
  815. private static final Logger getLogger() {
  816. return Logger.getLogger(ApplicationConfiguration.class.getName());
  817. }
  818. /**
  819. * Get the root element instance used for this application.
  820. *
  821. * @return registered root element
  822. * @since 8.4
  823. */
  824. public Element getRootElement() {
  825. return rootElement;
  826. }
  827. }