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