1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include <fstream>
7 
8 #include "Wt/Utils.h"
9 #include "Wt/WApplication.h"
10 #include "Wt/WCombinedLocalizedStrings.h"
11 #include "Wt/WContainerWidget.h"
12 #include "Wt/WCssTheme.h"
13 #include "Wt/WDate.h"
14 #include "Wt/WDefaultLoadingIndicator.h"
15 #include "Wt/WException.h"
16 #include "Wt/WFileUpload.h"
17 #include "Wt/WLinkedCssStyleSheet.h"
18 #include "Wt/WMemoryResource.h"
19 #include "Wt/WServer.h"
20 #include "Wt/WTimer.h"
21 
22 #include "WebSession.h"
23 #include "DomElement.h"
24 #include "Configuration.h"
25 #include "SoundManager.h"
26 #include "WebController.h"
27 #include "WebUtils.h"
28 
29 #include <boost/algorithm/string/predicate.hpp>
30 #include <boost/pool/pool.hpp>
31 
32 #ifdef min
33 #undef min
34 #endif
35 
36 namespace skeletons {
37   extern const char * Wt_xml1;
38 }
39 
40 namespace Wt {
41 
42 LOGGER("WApplication");
43 
44 #if !(defined(DOXYGEN_ONLY) || defined(WT_TARGET_JAVA))
45 const WtLibVersion WT_INCLUDED_VERSION = WtLibVersion();
46 #endif
47 
48 const char *WApplication::RESOURCES_URL = "resourcesURL";
49 
MetaHeader(MetaHeaderType aType,const std::string & aName,const WString & aContent,const std::string & aLang,const std::string & aUserAgent)50 MetaHeader::MetaHeader(MetaHeaderType aType,
51 		       const std::string& aName,
52 		       const WString& aContent,
53 		       const std::string& aLang,
54 		       const std::string& aUserAgent)
55   : type(aType), name(aName), lang(aLang), userAgent(aUserAgent),
56     content(aContent)
57 { }
58 
ScriptLibrary(const std::string & anUri,const std::string & aSymbol)59 WApplication::ScriptLibrary::ScriptLibrary(const std::string& anUri,
60 					   const std::string& aSymbol)
61   : uri(anUri), symbol(aSymbol)
62 { }
63 
MetaLink(const std::string & aHref,const std::string & aRel,const std::string & aMedia,const std::string & aHreflang,const std::string & aType,const std::string & aSizes,bool aDisabled)64 WApplication::MetaLink::MetaLink(const std::string &aHref,
65 				 const std::string &aRel,
66 				 const std::string &aMedia,
67 				 const std::string &aHreflang,
68 				 const std::string &aType,
69 				 const std::string &aSizes,
70 				 bool aDisabled)
71   : href(aHref), rel(aRel), media(aMedia), hreflang(aHreflang), type(aType),
72     sizes(aSizes), disabled(aDisabled)
73 { }
74 
75 bool WApplication::ScriptLibrary::operator< (const ScriptLibrary& other) const
76 {
77   return uri < other.uri;
78 }
79 
80 bool WApplication::ScriptLibrary::operator== (const ScriptLibrary& other) const
81 {
82   return uri == other.uri;
83 }
84 
WApplication(const WEnvironment & env,WtLibVersion)85 WApplication::WApplication(const WEnvironment& env
86 #if !(defined(DOXYGEN_ONLY) || defined(WT_TARGET_JAVA))
87 			   , WtLibVersion
88 #endif
89 )
90   : session_(env.session_),
91 #ifndef WT_CNOR
92     weakSession_(session_->shared_from_this()),
93 #endif // WT_CNOR
94     titleChanged_(false),
95     closeMessageChanged_(false),
96     localeChanged_(false),
97     widgetRoot_(nullptr),
98     timerRoot_(nullptr),
99     serverPush_(0),
100     serverPushChanged_(true),
101 #ifndef WT_CNOR
102     eventSignalPool_(new boost::pool<>(sizeof(EventSignal<>))),
103 #endif // WT_CNOR
104     javaScriptClass_("Wt"),
105     quitted_(false),
106     internalPathsEnabled_(false),
107     exposedOnly_(nullptr),
108     loadingIndicator_(nullptr),
109     bodyHtmlClassChanged_(true),
110     enableAjax_(false),
111 #ifndef WT_TARGET_JAVA
112     initialized_(false),
113 #endif // WT_TARGET_JAVA
114     selectionStart_(-1),
115     selectionEnd_(-1),
116     layoutDirection_(LayoutDirection::LeftToRight),
117     scriptLibrariesAdded_(0),
118     theme_(nullptr),
119     styleSheetsAdded_(0),
120     exposeSignals_(true),
121     newBeforeLoadJavaScript_(0),
122     autoJavaScriptChanged_(false),
123 #ifndef WT_DEBUG_JS
124     newJavaScriptPreamble_(0),
125 #endif // WT_DEBUG_JS
126     customJQuery_(false),
127     showLoadingIndicator_("showload", this),
128     hideLoadingIndicator_("hideload", this),
129     unloaded_(this, "Wt-unload"),
130     idleTimeout_(this, "Wt-idleTimeout"),
131     soundManager_(nullptr)
132 {
133   session_->setApplication(this);
134   locale_ = environment().locale();
135 
136   renderedInternalPath_ = newInternalPath_ = environment().internalPath();
137   internalPathIsChanged_ = false;
138   internalPathDefaultValid_ = true;
139   internalPathValid_ = true;
140 
141   theme_.reset(new WCssTheme("default"));
142 
143 #ifndef WT_TARGET_JAVA
144   setLocalizedStrings(std::make_shared<WMessageResourceBundle>());
145 #else
146   setLocalizedStrings(std::shared_ptr<WLocalizedStrings>());
147 #endif // !WT_TARGET_JAVA
148 
149   if (!environment().javaScript() && environment().agentIsIE()) {
150     /*
151      * WARNING: Similar code in WebRenderer.C must be kept in sync for
152      *          plain boot.
153      */
154     if (static_cast<unsigned int>(environment().agent()) <
155 	static_cast<unsigned int>(UserAgent::IE9)) {
156       const Configuration& conf = environment().server()->configuration();
157       bool selectIE7 = conf.uaCompatible().find("IE8=IE7")
158 	!= std::string::npos;
159 
160       if (selectIE7)
161 	addMetaHeader(MetaHeaderType::HttpHeader, "X-UA-Compatible", "IE=7");
162     } else if (environment().agent() == UserAgent::IE9) {
163 	addMetaHeader(MetaHeaderType::HttpHeader, "X-UA-Compatible", "IE=9");
164     } else if (environment().agent() == UserAgent::IE10) {
165 	addMetaHeader(MetaHeaderType::HttpHeader, "X-UA-Compatible", "IE=10");
166     } else {
167 	addMetaHeader(MetaHeaderType::HttpHeader, "X-UA-Compatible", "IE=11");
168     }
169   }
170 
171   domRoot_.reset(new WContainerWidget());
172   domRoot_->setGlobalUnfocused(true);
173   domRoot_->setStyleClass("Wt-domRoot");
174   domRoot_->load();
175 
176   if (session_->type() == EntryPointType::Application)
177     domRoot_->resize(WLength::Auto, WLength(100, LengthUnit::Percentage));
178 
179   timerRoot_ = domRoot_->addWidget(std::make_unique<WContainerWidget>());
180   timerRoot_->setId("Wt-timers");
181   timerRoot_->resize(WLength::Auto, 0);
182   timerRoot_->setPositionScheme(PositionScheme::Absolute);
183 
184   if (session_->type() == EntryPointType::Application) {
185     widgetRoot_ = domRoot_->addWidget(std::make_unique<WContainerWidget>());
186     widgetRoot_->resize(WLength::Auto, WLength(100, LengthUnit::Percentage));
187   } else {
188     domRoot2_.reset(new WContainerWidget());
189     domRoot2_->load();
190   }
191 
192   // a define so that it shouts at us !
193   #define RTL ".Wt-rtl "
194 
195   /*
196    * Subset of typical CSS "reset" styles, only those that are needed
197    * for Wt's built-in widgets and are relatively harmless.
198    */
199   styleSheet_.addRule("table", "border-collapse: collapse; border: 0px;"
200 		      "border-spacing: 0px");
201   styleSheet_.addRule("div, td, img",
202 		      "margin: 0px; padding: 0px; border: 0px");
203   styleSheet_.addRule("td", "vertical-align: top;");
204   styleSheet_.addRule("td", "text-align: left;");
205   styleSheet_.addRule(RTL "td", "text-align: right;");
206   styleSheet_.addRule("button", "white-space: nowrap;");
207   styleSheet_.addRule("video", "display: block");
208 
209   if (environment().agentIsGecko())
210     styleSheet_.addRule("html", "overflow: auto;");
211 
212   /*
213    * Standard Wt CSS styles: resources, button wrap and form validation
214    */
215   styleSheet_.addRule("iframe.Wt-resource",
216 		      "width: 0px; height: 0px; border: 0px;");
217   if (environment().agentIsIElt(9))
218     styleSheet_.addRule("iframe.Wt-shim",
219 			"position: absolute; top: -1px; left: -1px; "
220 			"z-index: -1;"
221 			"opacity: 0; filter: alpha(opacity=0);"
222 			"border: none; margin: 0; padding: 0;");
223   styleSheet_.addRule(".Wt-wrap",
224 		      "border: 0px;"
225 		      "margin: 0px;"
226 		      "padding: 0px;"
227 		      "font: inherit; "
228 		      "cursor: pointer; cursor: hand;"
229 		      "background: transparent;"
230 		      "text-decoration: none;"
231 		      "color: inherit;");
232 
233   styleSheet_.addRule(".Wt-wrap", "text-align: left;");
234   styleSheet_.addRule(RTL ".Wt-wrap", "text-align: right;");
235   styleSheet_.addRule("div.Wt-chwrap", "width: 100%; height: 100%");
236 
237   if (environment().agentIsIE())
238     styleSheet_.addRule(".Wt-wrap",
239 			"margin: -1px 0px -3px;");
240   //styleSheet_.addRule("a.Wt-wrap", "text-decoration: none;");
241   styleSheet_.addRule(".unselectable",
242 		      "-moz-user-select:-moz-none;"
243 		      "-khtml-user-select: none;"
244 		      "-webkit-user-select: none;"
245 		      "user-select: none;");
246   styleSheet_.addRule(".selectable",
247 		      "-moz-user-select: text;"
248 		      "-khtml-user-select: normal;"
249 		      "-webkit-user-select: text;"
250 		      "user-select: text;");
251   styleSheet_.addRule(".Wt-domRoot", "position: relative;");
252   styleSheet_.addRule("body.Wt-layout", std::string() +
253 		      "height: 100%; width: 100%;"
254 		      "margin: 0px; padding: 0px; border: none;"
255 		      + (environment().javaScript() ? "overflow:hidden" : ""));
256   styleSheet_.addRule("html.Wt-layout", std::string() +
257 		      "height: 100%; width: 100%;"
258 		      "margin: 0px; padding: 0px; border: none;"
259 		      + (environment().javaScript() ? "overflow:hidden" : ""));
260 
261   if (environment().agentIsOpera())
262     if (environment().userAgent().find("Mac OS X") != std::string::npos)
263       styleSheet_.addRule("img.Wt-indeterminate", "margin: 4px 1px -3px 2px;");
264     else
265       styleSheet_.addRule("img.Wt-indeterminate", "margin: 4px 2px -3px 0px;");
266   else
267     if (environment().userAgent().find("Mac OS X") != std::string::npos)
268       styleSheet_.addRule("img.Wt-indeterminate", "margin: 4px 3px 0px 4px;");
269     else
270       styleSheet_.addRule("img.Wt-indeterminate", "margin: 3px 3px 0px 4px;");
271 
272   if (environment().supportsCss3Animations()) {
273     std::string prefix = "";
274     if (environment().agentIsWebKit())
275       prefix = "webkit-";
276     else if (environment().agentIsGecko())
277       prefix = "moz-";
278 
279     useStyleSheet(WApplication::relativeResourcesUrl()
280 		  + prefix + "transitions.css");
281   }
282 
283   setLoadingIndicator
284     (std::unique_ptr<WLoadingIndicator>(new WDefaultLoadingIndicator()));
285 
286   unloaded_.connect(this, &WApplication::doUnload);
287   idleTimeout_.connect(this, &WApplication::doIdleTimeout);
288 }
289 
setJavaScriptClass(const std::string & javaScriptClass)290 void WApplication::setJavaScriptClass(const std::string& javaScriptClass)
291 {
292   if (session_->type() != EntryPointType::Application)
293     javaScriptClass_ = javaScriptClass;
294 }
295 
296 void WApplication
setLoadingIndicator(std::unique_ptr<WLoadingIndicator> indicator)297 ::setLoadingIndicator(std::unique_ptr<WLoadingIndicator> indicator)
298 {
299 #ifdef WT_TARGET_JAVA
300   if (!loadingIndicator_) {
301     showLoadingIndicator_.connect(showLoadJS);
302     hideLoadingIndicator_.connect(hideLoadJS);
303   }
304 #endif
305 
306   if (loadingIndicator_)
307     loadingIndicator_->removeFromParent();
308 
309   loadingIndicator_ = indicator.get();
310 
311   if (loadingIndicator_) {
312     domRoot_->addWidget(std::move(indicator));
313 
314 #ifndef WT_TARGET_JAVA
315     showLoadingIndicator_.connect(loadingIndicator_, &WWidget::show);
316     hideLoadingIndicator_.connect(loadingIndicator_, &WWidget::hide);
317 #else
318     // stateless learning does not work in Java
319     showLoadJS.setJavaScript
320       ("function(o,e) {"
321        "" WT_CLASS ".inline('" + loadingIndicator_->id() + "');"
322        "}");
323 
324     hideLoadJS.setJavaScript
325       ("function(o,e) {"
326        "" WT_CLASS ".hide('" + loadingIndicator_->id() + "');"
327        "}");
328 #endif
329 
330     loadingIndicator_->hide();
331   }
332 }
333 
334 #ifndef WT_TARGET_JAVA
335 
initialize()336 void WApplication::initialize()
337 { }
338 
finalize()339 void WApplication::finalize()
340 { }
341 
342 #else
343 
destroy()344 void WApplication::destroy()
345 { }
346 
347 #endif // !WT_TARGET_JAVA
348 
349 #ifndef WT_TARGET_JAVA
messageResourceBundle()350 WMessageResourceBundle& WApplication::messageResourceBundle()
351 {
352   auto result = dynamic_cast<WMessageResourceBundle*>(localizedStrings().get());
353   if (result)
354     return *result;
355   else
356     throw WException("messageResourceBundle(): failed to cast localizedStrings() to WMessageResourceBundle*!");
357 }
358 #endif // !WT_TARGET_JAVA
359 
onePixelGifUrl()360 std::string WApplication::onePixelGifUrl()
361 {
362   if (environment().agentIsIElt(7)) {
363     if (!onePixelGifR_) {
364       std::unique_ptr<WMemoryResource> w(new WMemoryResource("image/gif"));
365 
366       static const unsigned char gifData[]
367 	= { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00,
368             0x80, 0x00, 0x00, 0xdb, 0xdf, 0xef, 0x00, 0x00, 0x00, 0x21,
369             0xf9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
370             0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44,
371             0x01, 0x00, 0x3b };
372 
373       w->setData(gifData, 43);
374       onePixelGifR_ = std::move(w);
375     }
376 
377     return onePixelGifR_->url();
378   } else
379     return "data:image/gif;base64,"
380       "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
381 }
382 
~WApplication()383 WApplication::~WApplication()
384 {
385 #ifndef WT_TARGET_JAVA
386   // Fix issue #5331: if WTimer is a child of WApplication,
387   // it will outlive timerRoot_. Delete it now already.
388   for (std::size_t i = 0; i < children_.size(); ++i) {
389     WTimer *timer = dynamic_cast<WTimer*>(children_[i].get());
390     if (timer) {
391       removeChild(timer);
392     }
393   }
394   timerRoot_ = nullptr;
395 
396   // First remove all children owned by this WApplication (issue #6282)
397   if (domRoot_) {
398     auto domRoot = domRoot_.get();
399     for (WWidget *child : domRoot->children())
400       removeChild(child);
401   }
402   if (domRoot2_) {
403     auto domRoot = domRoot2_.get();
404     for (WWidget *child : domRoot->children())
405       removeChild(child);
406   }
407 #endif // WT_TARGET_JAVA
408 
409   /* Clear domRoot */
410   domRoot_.reset();
411 
412   /* Widgetset bound widgets */
413   domRoot2_.reset();
414 
415   session_->setApplication(nullptr);
416 
417 #ifndef WT_TARGET_JAVA
418   delete eventSignalPool_;
419 #endif
420 }
421 
domRoot()422 WWebWidget *WApplication::domRoot() const
423 {
424   return domRoot_.get();
425 }
426 
attachThread(bool attach)427 void WApplication::attachThread(bool attach)
428 {
429 #ifndef WT_CNOR
430   if (attach) {
431     std::shared_ptr<WebSession> session = weakSession_.lock();
432     if (session)
433       WebSession::Handler::attachThreadToSession(session);
434     else
435       session_->attachThreadToLockedHandler();
436   } else
437     WebSession::Handler::attachThreadToSession(std::shared_ptr<WebSession>());
438 #else
439   if (attach)
440     WebSession::Handler::attachThreadToSession(session_);
441   else
442     WebSession::Handler::attachThreadToSession(std::shared_ptr<WebSession>());
443 #endif
444 }
445 
relativeResourcesUrl()446 std::string WApplication::relativeResourcesUrl()
447 {
448 #ifndef WT_TARGET_JAVA
449   std::string result = "resources/";
450   readConfigurationProperty(WApplication::RESOURCES_URL, result);
451 
452   if (!result.empty() && result[result.length()-1] != '/')
453     result += '/';
454 
455   return result;
456 #else
457   WApplication *app = WApplication::instance();
458   const Configuration& conf = app->environment().server()->configuration();
459   const std::string* path = conf.property(WApplication::RESOURCES_URL);
460 
461   int version;
462   try {
463     version = app->environment().server()->servletMajorVersion();
464   } catch (std::exception& e) {
465     return "";
466   }
467   if (version < 3) {
468     /*
469      * Arghll... we should in fact know when we need the absolute URL: only
470      * when we are having a request.pathInfo().
471      */
472     if (path == "/wt-resources/") {
473       std::string result = app->environment().deploymentPath();
474       if (!result.empty() && result[result.length() - 1] == '/')
475 	return result + path->substr(1);
476       else
477 	return result + *path;
478     } else
479       return *path;
480   } else { // from v3.0, resources can be deployed in META-INF of a jar-file
481     std::string contextPath = app->environment().server()->getContextPath();
482     if (!contextPath.empty() && contextPath[contextPath.length() - 1] != '/')
483       contextPath = contextPath + "/";
484     if (path == "/wt-resources/")
485       return  contextPath + path->substr(1);
486     else
487       return contextPath + *path;
488   }
489 #endif // WT_TARGET_JAVA
490 }
491 
resourcesUrl()492 std::string WApplication::resourcesUrl()
493 {
494   return WApplication::instance()->resolveRelativeUrl
495     (WApplication::relativeResourcesUrl());
496 }
497 
498 #ifndef WT_TARGET_JAVA
appRoot()499 std::string WApplication::appRoot()
500 {
501   return WServer::instance()->appRoot();
502 }
503 
docRoot()504 std::string WApplication::docRoot() const
505 {
506   return environment().getCgiValue("DOCUMENT_ROOT");
507 }
508 
setConnectionMonitor(const std::string & jsFunction)509 void WApplication::setConnectionMonitor(const std::string& jsFunction) {
510   doJavaScript(javaScriptClass_
511 	       + "._p_.setConnectionMonitor("+ jsFunction + ")");
512 
513 }
514 
515 #endif // WT_TARGET_JAVA
516 
bindWidget(std::unique_ptr<WWidget> widget,const std::string & domId)517 void WApplication::bindWidget(std::unique_ptr<WWidget> widget,
518 			      const std::string& domId)
519 {
520   if (session_->type() != EntryPointType::WidgetSet)
521     throw WException("WApplication::bindWidget() can be used only "
522 		     "in WidgetSet mode.");
523 
524   widget->setId(domId);
525   widget->setJavaScriptMember("wtReparentBarrier", "true");
526   domRoot2_->addWidget(std::move(widget));
527 }
528 
pushExposedConstraint(WWidget * w)529 void WApplication::pushExposedConstraint(WWidget *w)
530 {
531   exposedOnly_ = w;
532 }
533 
popExposedConstraint(WWidget * w)534 void WApplication::popExposedConstraint(WWidget *w)
535 {
536   assert (exposedOnly_ == w);
537   exposedOnly_ = nullptr;
538 }
539 
addGlobalWidget(WWidget * w)540 void WApplication::addGlobalWidget(WWidget *w)
541 {
542   domRoot_->addWidget(std::unique_ptr<WWidget>(w)); // take ownership
543 #ifndef WT_TARGET_JAVA
544   domRoot_->removeChild(w).release();               // return ownership
545 #endif // WT_TARGET_JAVA
546   w->setGlobalWidget(true);
547 }
548 
removeGlobalWidget(WWidget * w)549 void WApplication::removeGlobalWidget(WWidget *w)
550 {
551   // In the destructor domRoot_->reset() can cause domRoot_
552   // to be null. In that case, we don't need to remove this
553   // widget from the domRoot.
554   if (domRoot_) {
555     w->setGlobalWidget(false);
556     auto removed = domRoot_->removeWidget(w);
557     // domRoot_ should never own the global widget
558 #ifndef WT_TARGET_JAVA
559     assert(!removed);
560 #endif // WT_TARGET_JAVA
561   }
562 }
563 
isExposed(WWidget * w)564 bool WApplication::isExposed(WWidget *w) const
565 {
566   // File uploads may be hidden when emitting a signal.
567   // Other hidden widgets should not emit signals.
568   // FIXME: fix all of the regressions caused by this check
569   //        before reenabling it
570 #if 0
571   if (!w->isVisible() && !dynamic_cast<WFileUpload*>(w))
572     return false;
573 #endif
574 
575   if (!w->isEnabled())
576     return false;
577 
578   if (w == domRoot_.get())
579     return true;
580 
581   if (w->parent() == timerRoot_)
582     return true;
583 
584   if (exposedOnly_)
585     return exposedOnly_->isExposed(w);
586   else {
587     WWidget *p = w->adam();
588     return (p == domRoot_.get() || p == domRoot2_.get());
589   }
590 }
591 
sessionId()592 std::string WApplication::sessionId() const
593 {
594   return session_->sessionId();
595 }
596 
597 #ifndef WT_TARGET_JAVA
changeSessionId()598 void WApplication::changeSessionId()
599 {
600   session_->generateNewSessionId();
601 }
602 #endif // WT_TARGET_JAVA
603 
setCssTheme(const std::string & theme)604 void WApplication::setCssTheme(const std::string& theme)
605 {
606   setTheme(std::shared_ptr<WTheme>(new WCssTheme(theme)));
607 }
608 
setTheme(const std::shared_ptr<WTheme> & theme)609 void WApplication::setTheme(const std::shared_ptr<WTheme>& theme)
610 {
611   theme_ = theme;
612   theme_->init(this);
613 }
614 
useStyleSheet(const WLink & link,const std::string & media)615 void WApplication::useStyleSheet(const WLink& link, const std::string& media)
616 {
617   useStyleSheet(WLinkedCssStyleSheet(link, media));
618 }
619 
useStyleSheet(const WLink & link,const std::string & condition,const std::string & media)620 void WApplication::useStyleSheet(const WLink& link,
621 				 const std::string& condition,
622 				 const std::string& media)
623 {
624   useStyleSheet(WLinkedCssStyleSheet(link, media), condition);
625 }
626 
useStyleSheet(const WLinkedCssStyleSheet & styleSheet,const std::string & condition)627 void WApplication::useStyleSheet(const WLinkedCssStyleSheet& styleSheet,
628 				 const std::string& condition)
629 {
630   bool display = true;
631 
632   if (!condition.empty()) {
633     display = false;
634     if (environment().agentIsIE()) {
635       int thisVersion = 4;
636 
637       switch (environment().agent()) {
638       case UserAgent::IEMobile:
639 	thisVersion = 5; break;
640       case UserAgent::IE6:
641 	thisVersion = 6; break;
642       case UserAgent::IE7:
643 	thisVersion = 7; break;
644       case UserAgent::IE8:
645 	thisVersion = 8; break;
646       case UserAgent::IE9:
647 	thisVersion = 9; break;
648       case UserAgent::IE10:
649 	thisVersion = 10; break;
650       default:
651 	thisVersion = 11; break;
652       }
653 
654       enum { lte, lt, eq, gt, gte } cond = eq;
655 
656       bool invert = false;
657       std::string r = condition;
658 
659       while (!r.empty()) {
660 	if (r.length() >= 3 && r.substr(0, 3) == "IE ") {
661 	  r = r.substr(3);
662 	} else if (r[0] == '!') {
663 	  r = r.substr(1);
664 	  invert = !invert;
665 	} else if (r.length() >= 4 && r.substr(0, 4) == "lte ") {
666 	  r = r.substr(4);
667 	  cond = lte;
668 	} else if (r.length() >= 3 && r.substr(0, 3) == "lt ") {
669 	  r = r.substr(3);
670 	  cond = lt;
671 	} else if (r.length() >= 3 && r.substr(0, 3) == "gt ") {
672 	  r = r.substr(3);
673 	  cond = gt;
674 	} else if (r.length() >= 4 && r.substr(0, 4) == "gte ") {
675 	  r = r.substr(4);
676 	  cond = gte;
677 	} else {
678 	  try {
679 	    int version = Utils::stoi(r);
680 	    switch (cond) {
681 	    case eq:  display = thisVersion == version; break;
682 	    case lte: display = thisVersion <= version; break;
683 	    case lt:  display = thisVersion <  version; break;
684 	    case gte: display = thisVersion >= version; break;
685 	    case gt:  display = thisVersion >  version; break;
686 	    }
687 	    if (invert)
688 	      display = !display;
689 	  } catch (std::exception& e) {
690 	    LOG_ERROR("Could not parse condition: '" << condition << "'");
691 	  }
692 	  r.clear();
693 	}
694       }
695     }
696   }
697 
698   if (display) {
699     for (unsigned i = 0; i < styleSheets_.size(); ++i) {
700       if (styleSheets_[i].link() == styleSheet.link()
701 	  && styleSheets_[i].media() == styleSheet.media()) {
702 	return;
703       }
704     }
705 
706     styleSheets_.push_back(styleSheet);
707     ++styleSheetsAdded_;
708   }
709 }
710 
removeStyleSheet(const WLink & link)711 void WApplication::removeStyleSheet(const WLink& link)
712 {
713   for (int i = (int)styleSheets_.size() - 1; i > -1; --i) {
714     if (styleSheets_[i].link() == link) {
715       WLinkedCssStyleSheet &sheet = styleSheets_[i];
716       styleSheetsToRemove_.push_back(sheet);
717       if (i > (int)styleSheets_.size() + styleSheetsAdded_ - 1)
718         styleSheetsAdded_--;
719       styleSheets_.erase(styleSheets_.begin() + i);
720       break;
721     }
722   }
723 }
724 
environment()725 const WEnvironment& WApplication::environment() const
726 {
727   return session_->env();
728 }
729 
env()730 WEnvironment& WApplication::env()
731 {
732   return session_->env();
733 }
734 
setTitle(const WString & title)735 void WApplication::setTitle(const WString& title)
736 {
737   if (session_->renderer().preLearning() || title_ != title) {
738     title_ = title;
739     titleChanged_ = true;
740   }
741 }
742 
setConfirmCloseMessage(const WString & message)743 void WApplication::setConfirmCloseMessage(const WString& message)
744 {
745   if (message != closeMessage_) {
746     closeMessage_ = message;
747     closeMessageChanged_ = true;
748   }
749 }
750 
url(const std::string & internalPath)751 std::string WApplication::url(const std::string& internalPath) const
752 {
753   return resolveRelativeUrl(session_->mostRelativeUrl(internalPath));
754 }
755 
makeAbsoluteUrl(const std::string & url)756 std::string WApplication::makeAbsoluteUrl(const std::string& url) const
757 {
758   return session_->makeAbsoluteUrl(url);
759 }
760 
resolveRelativeUrl(const std::string & url)761 std::string WApplication::resolveRelativeUrl(const std::string& url) const
762 {
763   return session_->fixRelativeUrl(url);
764 }
765 
quit()766 void WApplication::quit()
767 {
768   quit(WString::tr("Wt.QuittedMessage"));
769 }
770 
quit(const WString & restartMessage)771 void WApplication::quit(const WString& restartMessage)
772 {
773   quitted_ = true;
774   quittedMessage_ = restartMessage;
775 }
776 
findWidget(const std::string & name)777 WWidget *WApplication::findWidget(const std::string& name)
778 {
779   WWidget *result = domRoot_->find(name);
780   if (!result && domRoot2_)
781     result = domRoot2_->find(name);
782 
783   return result;
784 }
785 
doUnload()786 void WApplication::doUnload()
787 {
788   if (session_->suspended())
789     return;
790 
791   const Configuration& conf = environment().server()->configuration();
792 
793   if (conf.reloadIsNewSession())
794     unload();
795   else
796     session_->setState(WebSession::State::Loaded, 5);
797 }
798 
unload()799 void WApplication::unload()
800 {
801   quit();
802 }
803 
804 
suspend(std::chrono::seconds duration)805 void WApplication::suspend(std::chrono::seconds duration) {
806   session_->setState(WebSession::State::Suspended, static_cast<int>(duration.count()));
807 }
808 
doIdleTimeout()809 void WApplication::doIdleTimeout()
810 {
811   const Configuration& conf = environment().server()->configuration();
812 
813   if (conf.idleTimeout() != -1)
814     idleTimeout();
815 }
816 
idleTimeout()817 void WApplication::idleTimeout()
818 {
819   const Configuration& conf = environment().server()->configuration();
820   LOG_INFO("User idle for " << conf.idleTimeout() << " seconds, quitting due to idle timeout");
821   quit();
822 }
823 
handleJavaScriptError(const std::string & errorText)824 void WApplication::handleJavaScriptError(const std::string& errorText)
825 {
826   LOG_ERROR("JavaScript error: " << errorText);
827   quit();
828 }
829 
addExposedSignal(Wt::EventSignalBase * signal)830 void WApplication::addExposedSignal(Wt::EventSignalBase *signal)
831 {
832   std::string s = signal->encodeCmd();
833   Utils::insert(exposedSignals_, s, signal);
834 
835   LOG_DEBUG("addExposedSignal: " << s);
836 }
837 
removeExposedSignal(Wt::EventSignalBase * signal)838 void WApplication::removeExposedSignal(Wt::EventSignalBase *signal)
839 {
840   std::string s = signal->encodeCmd();
841 
842   if (exposedSignals_.erase(s)) {
843     justRemovedSignals_.insert(s);
844     LOG_DEBUG("removeExposedSignal: " << s);
845   } else {
846     LOG_DEBUG("removeExposedSignal of non-exposed " << s << "??");
847   }
848 }
849 
850 EventSignalBase *
decodeExposedSignal(const std::string & signalName)851 WApplication::decodeExposedSignal(const std::string& signalName) const
852 {
853   SignalMap::const_iterator i = exposedSignals_.find(signalName);
854 
855   if (i != exposedSignals_.end()) {
856     return i->second;
857   } else
858     return nullptr;
859 }
860 
encodeSignal(const std::string & objectId,const std::string & name)861 std::string WApplication::encodeSignal(const std::string& objectId,
862 				       const std::string& name) const
863 {
864   return objectId + '.' + name;
865 }
866 
resourceMapKey(WResource * resource)867 std::string WApplication::resourceMapKey(WResource *resource)
868 {
869   return resource->internalPath().empty()
870     ? resource->id() : "/path/" + resource->internalPath();
871 }
872 
addExposedResource(WResource * resource)873 std::string WApplication::addExposedResource(WResource *resource)
874 {
875   exposedResources_[resourceMapKey(resource)] = resource;
876   resource->incrementVersion();
877 
878   std::string fn = resource->suggestedFileName().toUTF8();
879   if (!fn.empty() && fn[0] != '/')
880     fn = '/' + fn;
881 
882   if (resource->internalPath().empty())
883     return session_->mostRelativeUrl(fn)
884       + "&request=resource&resource=" + Utils::urlEncode(resource->id())
885       + "&ver=" + std::to_string(resource->version());
886   else {
887     fn = resource->internalPath() + fn;
888     if (!session_->applicationName().empty() && fn[0] != '/')
889       fn = '/' + fn;
890     return session_->mostRelativeUrl(fn);
891   }
892 }
893 
removeExposedResource(WResource * resource)894 bool WApplication::removeExposedResource(WResource *resource)
895 {
896   std::string key = resourceMapKey(resource);
897   ResourceMap::iterator i = exposedResources_.find(key);
898 
899   if (i != exposedResources_.end() && i->second == resource) {
900 #ifndef WT_TARGET_JAVA
901     exposedResources_.erase(i);
902 #else
903     exposedResources_.erase(key);
904 #endif
905     return true;
906   } else
907     return false;
908 }
909 
decodeExposedResource(const std::string & resourceKey)910 WResource *WApplication::decodeExposedResource(const std::string& resourceKey)
911   const
912 {
913   ResourceMap::const_iterator i = exposedResources_.find(resourceKey);
914 
915   if (i != exposedResources_.end())
916     return i->second;
917   else {
918     std::size_t j = resourceKey.rfind('/');
919     if (j != std::string::npos && j > 1)
920       return decodeExposedResource(resourceKey.substr(0, j));
921     else
922       return nullptr;
923   }
924 }
925 
decodeExposedResource(const std::string & resourceKey,unsigned long ver)926 WResource *WApplication::decodeExposedResource(const std::string& resourceKey,
927                                                unsigned long ver) const
928 {
929   ResourceMap::const_iterator i = exposedResources_.find(resourceKey);
930 
931   WResource *resource = nullptr;
932   if (i != exposedResources_.end())
933     resource = i->second;
934 
935   if (resource
936       && resource->invalidAfterChanged()
937       && (resource->version() != ver))
938     resource = nullptr;
939 
940   return resource;
941 }
942 
encodeObject(WObject * object)943 std::string WApplication::encodeObject(WObject *object)
944 {
945   std::string result = "w" + object->uniqueId();
946 
947   encodedObjects_[result] = object;
948 
949   return result;
950 }
951 
decodeObject(const std::string & objectId)952 WObject *WApplication::decodeObject(const std::string& objectId) const
953 {
954   ObjectMap::const_iterator i = encodedObjects_.find(objectId);
955 
956   if (i != encodedObjects_.end()) {
957     return i->second;
958   } else
959     return nullptr;
960 }
961 
setLocale(const WLocale & locale)962 void WApplication::setLocale(const WLocale& locale)
963 {
964   locale_ = locale;
965   localeChanged_ = true;
966   refresh();
967 }
968 
setBodyClass(const std::string & styleClass)969 void WApplication::setBodyClass(const std::string& styleClass)
970 {
971   bodyClass_ = styleClass;
972   bodyHtmlClassChanged_ = true;
973 }
974 
setLayoutDirection(LayoutDirection direction)975 void WApplication::setLayoutDirection(LayoutDirection direction)
976 {
977   if (direction != layoutDirection_) {
978     layoutDirection_ = direction;
979     bodyHtmlClassChanged_ = true;
980   }
981 }
982 
setHtmlClass(const std::string & styleClass)983 void WApplication::setHtmlClass(const std::string& styleClass)
984 {
985   htmlClass_ = styleClass;
986   bodyHtmlClassChanged_ = true;
987 }
988 
globalKeyWentDown()989 EventSignal<WKeyEvent>& WApplication::globalKeyWentDown()
990 {
991   return domRoot_->keyWentDown();
992 }
993 
globalKeyWentUp()994 EventSignal<WKeyEvent>& WApplication::globalKeyWentUp()
995 {
996   return domRoot_->keyWentUp();
997 }
998 
globalKeyPressed()999 EventSignal<WKeyEvent>& WApplication::globalKeyPressed()
1000 {
1001   return domRoot_->keyPressed();
1002 }
1003 
globalEnterPressed()1004 EventSignal<>& WApplication::globalEnterPressed()
1005 {
1006   return domRoot_->enterPressed();
1007 }
1008 
globalEscapePressed()1009 EventSignal<>& WApplication::globalEscapePressed()
1010 {
1011   return domRoot_->escapePressed();
1012 }
1013 
localizedStrings()1014 std::shared_ptr<WLocalizedStrings> WApplication::localizedStrings()
1015 {
1016   if (localizedStrings_->items().size() > 1)
1017     return localizedStrings_->items()[0];
1018   else
1019     return std::shared_ptr<WLocalizedStrings>();
1020 }
1021 
localizedStringsPack()1022 WLocalizedStrings *WApplication::localizedStringsPack()
1023 {
1024   return localizedStrings_.get();
1025 }
1026 
builtinLocalizedStrings()1027 WMessageResourceBundle& WApplication::builtinLocalizedStrings()
1028 {
1029   return *(dynamic_cast<WMessageResourceBundle *>
1030 	   (localizedStrings_->items().back().get()));
1031 }
1032 
1033 void WApplication
setLocalizedStrings(const std::shared_ptr<WLocalizedStrings> & translator)1034 ::setLocalizedStrings(const std::shared_ptr<WLocalizedStrings>& translator)
1035 {
1036   if (!localizedStrings_) {
1037     localizedStrings_.reset(new WCombinedLocalizedStrings());
1038 
1039     auto defaultMessages = std::shared_ptr<WMessageResourceBundle>(new WMessageResourceBundle());
1040     defaultMessages->useBuiltin(skeletons::Wt_xml1);
1041     localizedStrings_->add(defaultMessages);
1042   }
1043 
1044   if (localizedStrings_->items().size() > 1)
1045     localizedStrings_->remove(localizedStrings_->items()[0]);
1046 
1047   if (translator)
1048     localizedStrings_->insert(0, translator);
1049 }
1050 
refresh()1051 void WApplication::refresh()
1052 {
1053   if (domRoot2_)
1054     domRoot2_->refresh();
1055   else
1056     domRoot_->refresh();
1057 
1058   if (title_.refresh())
1059     titleChanged_ = true;
1060 
1061   if (closeMessage_.refresh())
1062     closeMessageChanged_ = true;
1063 }
1064 
enableAjax()1065 void WApplication::enableAjax()
1066 {
1067   enableAjax_ = true;
1068 
1069   streamBeforeLoadJavaScript(session_->renderer().beforeLoadJS_, false);
1070   streamAfterLoadJavaScript(session_->renderer().beforeLoadJS_);
1071 
1072   domRoot_->enableAjax();
1073 
1074   if (domRoot2_)
1075     domRoot2_->enableAjax();
1076 
1077   doJavaScript
1078     (WT_CLASS ".ajaxInternalPaths(" +
1079      WWebWidget::jsStringLiteral(resolveRelativeUrl(bookmarkUrl("/"))) + ");");
1080 }
1081 
redirect(const std::string & url)1082 void WApplication::redirect(const std::string& url)
1083 {
1084   session_->redirect(url);
1085 }
1086 
encodeUntrustedUrl(const std::string & url)1087 std::string WApplication::encodeUntrustedUrl(const std::string& url) const
1088 {
1089   /*
1090    * If url is an absolute URL, then we jump through a redirect
1091    * page, to strip the session ID from the referer URL, in case the
1092    * current page has the session ID in the URL.
1093    */
1094 
1095   bool needRedirect = (url.find("://") != std::string::npos
1096 		       || boost::starts_with(url, "//"))
1097     && session_->hasSessionIdInUrl();
1098 
1099   if (needRedirect) {
1100     WebController *c = session_->controller();
1101     return "?request=redirect&url=" + Utils::urlEncode(url)
1102       + "&hash="
1103       + Utils::urlEncode(c->computeRedirectHash(url));
1104   } else
1105     return url;
1106 }
1107 
setTwoPhaseRenderingThreshold(int bytes)1108 void WApplication::setTwoPhaseRenderingThreshold(int bytes)
1109 {
1110   session_->renderer().setTwoPhaseThreshold(bytes);
1111 }
1112 
setCookie(const std::string & name,const std::string & value,int maxAge,const std::string & domain,const std::string & path,bool secure)1113 void WApplication::setCookie(const std::string& name,
1114 			     const std::string& value, int maxAge,
1115 			     const std::string& domain,
1116 			     const std::string& path,
1117 			     bool secure)
1118 {
1119   WDateTime expires = WDateTime::currentDateTime();
1120   expires = expires.addSecs(maxAge);
1121   session_->renderer().setCookie(name, value, expires, domain, path, secure);
1122 }
1123 
1124 #ifndef WT_TARGET_JAVA
setCookie(const std::string & name,const std::string & value,const WDateTime & expires,const std::string & domain,const std::string & path,bool secure)1125 void WApplication::setCookie(const std::string& name,
1126 			     const std::string& value,
1127 			     const WDateTime& expires,
1128 			     const std::string& domain,
1129 			     const std::string& path,
1130 			     bool secure)
1131 {
1132   session_->renderer().setCookie(name, value, expires, domain, path, secure);
1133 }
1134 #endif // WT_TARGET_JAVA
1135 
removeCookie(const std::string & name,const std::string & domain,const std::string & path)1136 void WApplication::removeCookie(const std::string& name,
1137 				const std::string& domain,
1138 				const std::string& path)
1139 {
1140   session_->renderer().setCookie(name, std::string(),
1141 				 WDateTime(WDate(1970,1,1)),
1142 				 domain, path, false);
1143 }
1144 
addMetaLink(const std::string & href,const std::string & rel,const std::string & media,const std::string & hreflang,const std::string & type,const std::string & sizes,bool disabled)1145 void WApplication::addMetaLink(const std::string &href,
1146 			       const std::string &rel,
1147 			       const std::string &media,
1148 			       const std::string &hreflang,
1149 			       const std::string &type,
1150 			       const std::string &sizes,
1151 			       bool disabled)
1152 {
1153   if (environment().javaScript())
1154     LOG_WARN("WApplication::addMetaLink() with no effect");
1155 
1156   if (href.empty())
1157     throw WException("WApplication::addMetaLink() href cannot be empty!");
1158   if (rel.empty())
1159     throw WException("WApplication::addMetaLink() rel cannot be empty!");
1160 
1161   for (unsigned i = 0; i < metaLinks_.size(); ++i) {
1162     MetaLink& ml = metaLinks_[i];
1163     if (ml.href == href) {
1164       ml.rel = rel;
1165       ml.media = media;
1166       ml.hreflang = hreflang;
1167       ml.type = type;
1168       ml.sizes = sizes;
1169       ml.disabled = disabled;
1170       return;
1171     }
1172   }
1173 
1174   MetaLink ml(href, rel, media, hreflang, type, sizes, disabled);
1175   metaLinks_.push_back(ml);
1176 }
1177 
removeMetaLink(const std::string & href)1178 void WApplication::removeMetaLink(const std::string &href)
1179 {
1180   for (unsigned i = 0; i < metaLinks_.size(); ++i) {
1181     MetaLink& ml = metaLinks_[i];
1182     if (ml.href == href) {
1183       metaLinks_.erase(metaLinks_.begin() + i);
1184       return;
1185     }
1186   }
1187 }
1188 
addMetaHeader(const std::string & name,const WString & content,const std::string & lang)1189 void WApplication::addMetaHeader(const std::string& name,
1190 				 const WString& content,
1191 				 const std::string& lang)
1192 {
1193   addMetaHeader(MetaHeaderType::Meta, name, content, lang);
1194 }
1195 
metaHeader(MetaHeaderType type,const std::string & name)1196 WString WApplication::metaHeader(MetaHeaderType type, const std::string& name) const
1197 {
1198   for (unsigned i = 0; i < metaHeaders_.size(); ++i) {
1199     const MetaHeader& m = metaHeaders_[i];
1200 
1201     if (m.type == type && m.name == name)
1202       return m.content;
1203   }
1204 
1205   return WString::Empty;
1206 }
1207 
addMetaHeader(MetaHeaderType type,const std::string & name,const WString & content,const std::string & lang)1208 void WApplication::addMetaHeader(MetaHeaderType type,
1209 				 const std::string& name,
1210 				 const WString& content,
1211 				 const std::string& lang)
1212 {
1213   if (environment().javaScript())
1214     LOG_WARN("WApplication::addMetaHeader() with no effect");
1215 
1216   /*
1217    * Replace or remove existing value
1218    */
1219   for (unsigned i = 0; i < metaHeaders_.size(); ++i) {
1220     MetaHeader& m = metaHeaders_[i];
1221 
1222     if (m.type == type && m.name == name) {
1223       if (content.empty())
1224 	metaHeaders_.erase(metaHeaders_.begin() + i);
1225       else
1226 	m.content = content;
1227       return;
1228     }
1229   }
1230 
1231   if (!content.empty())
1232     metaHeaders_.push_back(MetaHeader(type, name, content, lang,
1233 				      std::string()));
1234 }
1235 
removeMetaHeader(MetaHeaderType type,const std::string & name)1236 void WApplication::removeMetaHeader(MetaHeaderType type,
1237 				    const std::string& name)
1238 {
1239   if (environment().javaScript())
1240     LOG_WARN("removeMetaHeader() with no effect");
1241 
1242   for (unsigned i = 0; i < metaHeaders_.size(); ++i) {
1243     MetaHeader& m = metaHeaders_[i];
1244 
1245     if (m.type == type && (name.empty() || m.name == name)) {
1246       metaHeaders_.erase(metaHeaders_.begin() + i);
1247 
1248       if (name.empty())
1249 	--i;
1250       else
1251 	break;
1252     }
1253   }
1254 }
1255 
instance()1256 WApplication *WApplication::instance()
1257 {
1258   WebSession *session = WebSession::instance();
1259 
1260   return session ? session->app() : nullptr;
1261 }
1262 
maximumRequestSize()1263 ::int64_t WApplication::maximumRequestSize() const
1264 {
1265   return environment().server()->configuration().maxRequestSize();
1266 }
1267 
docType()1268 std::string WApplication::docType() const
1269 {
1270   return session_->docType();
1271 }
1272 
enableInternalPaths()1273 void WApplication::enableInternalPaths()
1274 {
1275   if (!internalPathsEnabled_) {
1276     internalPathsEnabled_ = true;
1277 
1278     doJavaScript
1279       (javaScriptClass() + "._p_.enableInternalPaths("
1280        + WWebWidget::jsStringLiteral(renderedInternalPath_)
1281        + ");");
1282 
1283     if (session_->useUglyInternalPaths())
1284       LOG_WARN("Deploy-path ends with '/', using /?_= for internal paths");
1285   }
1286 }
1287 
internalPathChanged()1288 Signal<std::string>& WApplication::internalPathChanged()
1289 {
1290   enableInternalPaths();
1291 
1292   return internalPathChanged_;
1293 }
1294 
internalPathMatches(const std::string & path)1295 bool WApplication::internalPathMatches(const std::string& path) const
1296 {
1297   if (session_->renderer().preLearning())
1298     return false;
1299   else
1300     return pathMatches(Utils::append(newInternalPath_, '/'), path);
1301 }
1302 
pathMatches(const std::string & path,const std::string & query)1303 bool WApplication::pathMatches(const std::string& path,
1304 			       const std::string& query)
1305 {
1306   /* Returns whether the current path start with the query */
1307   if (query == path
1308       || (path.length() > query.length()
1309 	  && path.substr(0, query.length()) == query
1310 	  && (query[query.length() - 1] == '/' || path[query.length()] == '/')))
1311     return true;
1312   else
1313     return false;
1314 }
1315 
internalPathNextPart(const std::string & path)1316 std::string WApplication::internalPathNextPart(const std::string& path) const
1317 {
1318   std::string subPath = internalSubPath(path);
1319 
1320   std::size_t t = subPath.find('/');
1321 
1322   if (t == std::string::npos)
1323     return subPath;
1324   else
1325     return subPath.substr(0, t);
1326 }
1327 
internalSubPath(const std::string & path)1328 std::string WApplication::internalSubPath(const std::string& path) const
1329 {
1330   std::string current = Utils::append(newInternalPath_, '/');
1331 
1332   if (!pathMatches(current, path)) {
1333     LOG_WARN("internalPath(): path '"
1334 	     << path << "' not within current path '" << internalPath()
1335 	     << "'");
1336     return std::string();
1337   }
1338 
1339   return current.substr(path.length());
1340 }
1341 
internalPath()1342 std::string WApplication::internalPath() const
1343 {
1344   return Utils::prepend(newInternalPath_, '/');
1345 }
1346 
setInternalPath(const std::string & path,bool emitChange)1347 void WApplication::setInternalPath(const std::string& path, bool emitChange)
1348 {
1349   enableInternalPaths();
1350 
1351   if (!session_->renderer().preLearning() && emitChange)
1352     changeInternalPath(path);
1353   else
1354     newInternalPath_ = path;
1355 
1356   internalPathValid_ = true;
1357   internalPathIsChanged_ = true;
1358 }
1359 
setInternalPathValid(bool valid)1360 void WApplication::setInternalPathValid(bool valid)
1361 {
1362   internalPathValid_ = valid;
1363 }
1364 
setInternalPathDefaultValid(bool valid)1365 void WApplication::setInternalPathDefaultValid(bool valid)
1366 {
1367   internalPathDefaultValid_ = valid;
1368 }
1369 
changeInternalPath(const std::string & aPath)1370 bool WApplication::changeInternalPath(const std::string& aPath)
1371 {
1372   std::string path = Utils::prepend(aPath, '/');
1373 
1374   if (path != internalPath()) {
1375     renderedInternalPath_ = newInternalPath_ = path;
1376     internalPathValid_ = internalPathDefaultValid_;
1377     internalPathChanged_.emit(newInternalPath_);
1378 
1379     if (!internalPathValid_)
1380       internalPathInvalid_.emit(newInternalPath_);
1381   }
1382 
1383   return internalPathValid_;
1384 }
1385 
changedInternalPath(const std::string & path)1386 bool WApplication::changedInternalPath(const std::string& path)
1387 {
1388   if (!environment().internalPathUsingFragments())
1389     session_->setPagePathInfo(path);
1390 
1391   return changeInternalPath(path);
1392 }
1393 
bookmarkUrl()1394 std::string WApplication::bookmarkUrl() const
1395 {
1396   return bookmarkUrl(newInternalPath_);
1397 }
1398 
bookmarkUrl(const std::string & internalPath)1399 std::string WApplication::bookmarkUrl(const std::string& internalPath) const
1400 {
1401   // ? return session_->bookmarkUrl("") + '#' + internalPath;
1402   // to avoid an extra round trip
1403   return session_->bookmarkUrl(internalPath);
1404 }
1405 
1406 #ifndef WT_TARGET_JAVA
log(const std::string & type)1407 WLogEntry WApplication::log(const std::string& type) const
1408 {
1409   return session_->log(type);
1410 }
1411 #endif // WT_TARGET_JAVA
1412 
enableUpdates(bool enabled)1413 void WApplication::enableUpdates(bool enabled)
1414 {
1415   if (enabled) {
1416     if (serverPush_ == 0 && !WebSession::Handler::instance()->request())
1417       LOG_WARN("WApplication::enableUpdates(true): "
1418 	       "should be called from within event loop");
1419     ++serverPush_;
1420   } else
1421     --serverPush_;
1422 
1423   if ((enabled && serverPush_ == 1) || (!enabled && serverPush_ == 0))
1424     serverPushChanged_ = true;
1425 }
1426 
triggerUpdate()1427 void WApplication::triggerUpdate()
1428 {
1429   if (!serverPush_)
1430     LOG_WARN("WApplication::triggerUpdate(): updates not enabled?");
1431 
1432   session_->setTriggerUpdate(true);
1433 }
1434 
1435 #ifdef WT_TARGET_JAVA
getUpdateLock()1436 WApplication::UpdateLock WApplication::getUpdateLock()
1437 {
1438   return UpdateLock(this);
1439 }
1440 #endif
1441 
1442 #ifndef WT_TARGET_JAVA
1443 
1444 class UpdateLockImpl
1445 {
1446 public:
UpdateLockImpl(WApplication * app)1447   UpdateLockImpl(WApplication *app)
1448     : handler_(nullptr)
1449   {
1450 #ifdef WT_THREADED
1451     handler_ = new WebSession::Handler(app->weakSession_.lock(),
1452 				       WebSession::Handler::LockOption::TakeLock);
1453 #endif // WT_THREADED
1454   }
1455 
1456 #ifdef WT_THREADED
~UpdateLockImpl()1457   ~UpdateLockImpl() {
1458     delete handler_;
1459   }
1460 #endif // WT_THREADED
1461 
1462 private:
1463   // Handler which we created for actual lock
1464   WebSession::Handler *handler_;
1465 };
1466 
UpdateLock(WApplication * app)1467 WApplication::UpdateLock::UpdateLock(WApplication *app)
1468   : ok_(true)
1469 {
1470 #ifndef WT_THREADED
1471   return;
1472 #else
1473   /*
1474    * If we are already handling this application, then we already have
1475    * exclusive access, unless we are not having the lock (e.g. from a
1476    * WResource::handleRequest()).
1477    */
1478   WebSession::Handler *handler = WebSession::Handler::instance();
1479 
1480   std::shared_ptr<WebSession> appSession = app->weakSession_.lock();
1481   if (handler && handler->haveLock() && handler->session() == appSession.get())
1482     return;
1483 
1484   if (appSession.get() && !appSession->dead())
1485     impl_.reset(new UpdateLockImpl(app));
1486   else
1487     ok_ = false;
1488 #endif // WT_THREADED
1489 }
1490 
~UpdateLock()1491 WApplication::UpdateLock::~UpdateLock()
1492 { }
1493 
1494 #else
1495 
UpdateLock(WApplication * app)1496 WApplication::UpdateLock::UpdateLock(WApplication *app)
1497 {
1498   WebSession::Handler *handler = WebSession::Handler::instance();
1499 
1500   createdHandler_ = false;
1501   if (handler && handler->haveLock() && handler->session() == app->session_)
1502     return;
1503 
1504   new WebSession::Handler(app->session_, WebSession::Handler::LockOption::TakeLock);
1505 
1506   createdHandler_ = true;
1507 }
1508 
release()1509 void WApplication::UpdateLock::release()
1510 {
1511   if (createdHandler_) {
1512     WebSession::Handler::instance()->release();
1513   }
1514 }
1515 
close()1516 void WApplication::UpdateLock::close()
1517 {
1518   release();
1519 }
1520 
1521 #endif // WT_TARGET_JAVA
1522 
doJavaScript(const std::string & javascript,bool afterLoaded)1523 void WApplication::doJavaScript(const std::string& javascript,
1524 				bool afterLoaded)
1525 {
1526   if (afterLoaded) {
1527     afterLoadJavaScript_ += javascript;
1528     afterLoadJavaScript_ += '\n';
1529   } else {
1530     beforeLoadJavaScript_ += javascript;
1531     beforeLoadJavaScript_ += '\n';
1532     newBeforeLoadJavaScript_ += javascript.length() + 1;
1533   }
1534 }
1535 
addAutoJavaScript(const std::string & javascript)1536 void WApplication::addAutoJavaScript(const std::string& javascript)
1537 {
1538   autoJavaScript_ += javascript;
1539   autoJavaScriptChanged_ = true;
1540 }
1541 
declareJavaScriptFunction(const std::string & name,const std::string & function)1542 void WApplication::declareJavaScriptFunction(const std::string& name,
1543 					     const std::string& function)
1544 {
1545   doJavaScript(javaScriptClass_ + '.' + name + '=' + function + ';', false);
1546 }
1547 
streamAfterLoadJavaScript(WStringStream & out)1548 void WApplication::streamAfterLoadJavaScript(WStringStream& out)
1549 {
1550   out << afterLoadJavaScript_;
1551   afterLoadJavaScript_.clear();
1552 }
1553 
streamBeforeLoadJavaScript(WStringStream & out,bool all)1554 void WApplication::streamBeforeLoadJavaScript(WStringStream& out, bool all)
1555 {
1556   streamJavaScriptPreamble(out, all);
1557 
1558   if (!all) {
1559     if (newBeforeLoadJavaScript_)
1560       out << beforeLoadJavaScript_.substr(beforeLoadJavaScript_.length()
1561 					  - newBeforeLoadJavaScript_);
1562   } else {
1563     out << beforeLoadJavaScript_;
1564   }
1565   newBeforeLoadJavaScript_ = 0;
1566 }
1567 
notify(const WEvent & e)1568 void WApplication::notify(const WEvent& e)
1569 {
1570   session_->notify(e);
1571 }
1572 
processEvents()1573 void WApplication::processEvents()
1574 {
1575   /* set timeout to allow other events to be interleaved */
1576   doJavaScript("setTimeout(\"" + javaScriptClass_
1577 	       + "._p_.update(null,'none',null,true);\",0);");
1578 
1579   waitForEvent();
1580 }
1581 
waitForEvent()1582 void WApplication::waitForEvent()
1583 {
1584   if (!environment().isTest())
1585     session_->doRecursiveEventLoop();
1586 }
1587 
require(const std::string & uri,const std::string & symbol)1588 bool WApplication::require(const std::string& uri, const std::string& symbol)
1589 {
1590   ScriptLibrary sl(uri, symbol);
1591 
1592   if (Utils::indexOf(scriptLibraries_, sl) == -1) {
1593     WStringStream ss;
1594     streamBeforeLoadJavaScript(ss, false);
1595     sl.beforeLoadJS = ss.str();
1596 
1597     scriptLibraries_.push_back(sl);
1598     ++scriptLibrariesAdded_;
1599 
1600     return true;
1601   } else
1602     return false;
1603 }
1604 
requireJQuery(const std::string & uri)1605 bool WApplication::requireJQuery(const std::string& uri)
1606 {
1607   customJQuery_ = true;
1608   return require(uri);
1609 }
1610 
1611 #ifndef WT_TARGET_JAVA
readConfigurationProperty(const std::string & name,std::string & value)1612 bool WApplication::readConfigurationProperty(const std::string& name,
1613 					     std::string& value)
1614 {
1615   WebSession *session = WebSession::instance();
1616   if (session)
1617     return session->env().server()->readConfigurationProperty(name, value);
1618   else
1619     return false;
1620 }
1621 #else
readConfigurationProperty(const std::string & name,const std::string & value)1622 std::string *WApplication::readConfigurationProperty(const std::string& name,
1623 						     const std::string& value)
1624 {
1625   WebSession *session = WebSession::instance();
1626   if (session)
1627     return session->env().server()->readConfigurationProperty(name, value);
1628   else
1629     return &value;
1630 }
1631 #endif // WT_TARGET_JAVA
1632 
debug()1633 bool WApplication::debug() const
1634 {
1635   return session_->debug();
1636 }
1637 
getSoundManager()1638 SoundManager *WApplication::getSoundManager()
1639 {
1640   if (!soundManager_)
1641     soundManager_ = domRoot_->addWidget(std::make_unique<SoundManager>());
1642 
1643   return soundManager_;
1644 }
1645 
1646 #ifdef WT_DEBUG_JS
1647 
loadJavaScript(const char * jsFile)1648 void WApplication::loadJavaScript(const char *jsFile)
1649 {
1650   if (!javaScriptLoaded(jsFile)) {
1651     javaScriptLoaded_.insert(jsFile);
1652     newJavaScriptToLoad_.push_back(jsFile);
1653   }
1654 }
1655 
streamJavaScriptPreamble(WStringStream & out,bool all)1656 void WApplication::streamJavaScriptPreamble(WStringStream& out, bool all)
1657 {
1658   if (all) {
1659     out << "window.currentApp = " + javaScriptClass_ + ";";
1660     for (std::set<const char *>::const_iterator i = javaScriptLoaded_.begin();
1661 	 i != javaScriptLoaded_.end(); ++i)
1662       loadJavaScriptFile(out, *i);
1663   } else {
1664     if (!newJavaScriptToLoad_.empty()) {
1665       out << "window.currentApp = " + javaScriptClass_ + ";";
1666       for (unsigned i = 0; i < newJavaScriptToLoad_.size(); ++i)
1667 	loadJavaScriptFile(out, newJavaScriptToLoad_[i]);
1668     }
1669   }
1670 
1671   newJavaScriptToLoad_.clear();
1672 }
1673 
loadJavaScriptFile(WStringStream & out,const char * jsFile)1674 void WApplication::loadJavaScriptFile(WStringStream& out, const char *jsFile)
1675 {
1676 #define xstr(s) str(s)
1677 #define str(s) #s
1678   std::string fname = std::string( xstr(WT_DEBUG_JS) "/") + jsFile;
1679   out << Utils::readFile(fname);
1680 }
1681 
1682 #else
1683 
loadJavaScript(const char * jsFile,const WJavaScriptPreamble & preamble)1684 void WApplication::loadJavaScript(const char *jsFile,
1685 				  const WJavaScriptPreamble& preamble)
1686 {
1687   if (!javaScriptLoaded(preamble.name)) {
1688     javaScriptLoaded_.insert(jsFile);
1689     javaScriptLoaded_.insert(preamble.name);
1690 
1691     javaScriptPreamble_.push_back(preamble);
1692     ++newJavaScriptPreamble_;
1693   }
1694 }
1695 
streamJavaScriptPreamble(WStringStream & out,bool all)1696 void WApplication::streamJavaScriptPreamble(WStringStream& out, bool all)
1697 {
1698   if (all)
1699     newJavaScriptPreamble_ = javaScriptPreamble_.size();
1700 
1701   for (unsigned i = javaScriptPreamble_.size() - newJavaScriptPreamble_;
1702        i < javaScriptPreamble_.size(); ++i) {
1703     const WJavaScriptPreamble& preamble = javaScriptPreamble_[i];
1704     std::string scope
1705       = preamble.scope == ApplicationScope ? javaScriptClass() : WT_CLASS;
1706 
1707     if (preamble.type == JavaScriptFunction) {
1708       out << scope << '.' << (char *)preamble.name
1709 	  << " = function() { return ("
1710 	  << (char *)preamble.src << ").apply(" << scope << ", arguments) };\n";
1711     } else {
1712       out << scope << '.' << (char *)preamble.name
1713 	  << " = " << (char *)preamble.src << ";\n";
1714     }
1715   }
1716 
1717   newJavaScriptPreamble_ = 0;
1718 }
1719 
1720 #endif
1721 
javaScriptLoaded(const char * jsFile)1722 bool WApplication::javaScriptLoaded(const char *jsFile) const
1723 {
1724   return javaScriptLoaded_.find(jsFile) != javaScriptLoaded_.end();
1725 }
1726 
setFocus(const std::string & id,int selectionStart,int selectionEnd)1727 void WApplication::setFocus(const std::string& id,
1728 			    int selectionStart, int selectionEnd)
1729 {
1730   focusId_ = id;
1731   selectionStart_ = selectionStart;
1732   selectionEnd_ = selectionEnd;
1733 }
1734 
1735 #ifndef WT_TARGET_JAVA
deferRendering()1736 void WApplication::deferRendering()
1737 {
1738   session_->deferRendering();
1739 }
1740 
resumeRendering()1741 void WApplication::resumeRendering()
1742 {
1743   session_->resumeRendering();
1744 }
1745 #endif // WT_TARGET_JAVA
1746 
1747 }
1748