1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include <algorithm>
7 #include <cmath>
8 #include <cstdlib>
9 #include <boost/algorithm/string.hpp>
10 
11 #include "Wt/WApplication.h"
12 #include "Wt/WCompositeWidget.h"
13 #include "Wt/WContainerWidget.h"
14 #include "Wt/WLogger.h"
15 #include "Wt/WJavaScript.h"
16 #include "Wt/WStringStream.h"
17 #include "Wt/WTheme.h"
18 #include "Wt/WWebWidget.h"
19 
20 #include "DomElement.h"
21 #include "EscapeOStream.h"
22 #include "WebRenderer.h"
23 #include "WebSession.h"
24 #include "WebUtils.h"
25 #include "StringUtils.h"
26 #include "XSSFilter.h"
27 
28 #include "3rdparty/rapidxml/rapidxml.hpp"
29 #include "3rdparty/rapidxml/rapidxml_xhtml.hpp"
30 
31 #ifndef WT_DEBUG_JS
32 #include "js/WWebWidget.min.js"
33 #include "js/ToolTip.min.js"
34 #include "js/ScrollVisibility.min.js"
35 #endif
36 
37 #ifdef max
38 #undef max
39 #endif
40 
41 namespace Wt {
42 
43 LOGGER("WWebWidget");
44 
45 namespace {
46 
nonNegative(const WLength & w)47   WLength nonNegative(const WLength& w) {
48     if (w.isAuto())
49       return w;
50     else
51       return WLength(std::fabs(w.value()), w.unit());
52   }
53 
54 }
55 
56 const char *WWebWidget::FOCUS_SIGNAL = "focus";
57 const char *WWebWidget::BLUR_SIGNAL = "blur";
58 const int WWebWidget::DEFAULT_BASE_Z_INDEX = 1100;
59 const int WWebWidget::Z_INDEX_INCREMENT = 1100;
60 
61 #ifndef WT_TARGET_JAVA
62 const std::bitset<38> WWebWidget::AllChangeFlags = std::bitset<38>()
63   .set(BIT_FLEX_BOX_CHANGED)
64   .set(BIT_HIDDEN_CHANGED)
65   .set(BIT_GEOMETRY_CHANGED)
66   .set(BIT_FLOAT_SIDE_CHANGED)
67   .set(BIT_TOOLTIP_CHANGED)
68   .set(BIT_MARGINS_CHANGED)
69   .set(BIT_STYLECLASS_CHANGED)
70   .set(BIT_SELECTABLE_CHANGED)
71   .set(BIT_WIDTH_CHANGED)
72   .set(BIT_HEIGHT_CHANGED)
73   .set(BIT_DISABLED_CHANGED)
74   .set(BIT_ZINDEX_CHANGED)
75   .set(BIT_TABINDEX_CHANGED)
76   .set(BIT_SCROLL_VISIBILITY_CHANGED)
77   .set(BIT_OBJECT_NAME_CHANGED);
78 #endif // WT_TARGET_JAVA
79 
TransientImpl()80 WWebWidget::TransientImpl::TransientImpl()
81   : addedChildren_(0),
82     specialChildRemove_(false)
83 { }
84 
~TransientImpl()85 WWebWidget::TransientImpl::~TransientImpl()
86 { }
87 
LayoutImpl()88 WWebWidget::LayoutImpl::LayoutImpl()
89   : positionScheme_(PositionScheme::Static),
90     floatSide_(static_cast<Side>(0)),
91     clearSides_(None),
92     minimumWidth_(0),
93     minimumHeight_(0),
94     baseZIndex_(DEFAULT_BASE_Z_INDEX),
95     zIndex_(0),
96     verticalAlignment_(AlignmentFlag::Baseline)
97 {
98   for (unsigned i = 0; i < 4; ++i) {
99 #ifdef WT_TARGET_JAVA
100     offsets_[i] = WLength::Auto;
101 #endif // WT_TARGET_JAVA
102     margin_[i] = WLength(0);
103   }
104 }
105 
LookImpl(WWebWidget * w)106 WWebWidget::LookImpl::LookImpl(WWebWidget *w)
107   : toolTipTextFormat_(TextFormat::Plain),
108     loadToolTip_(w, "Wt-loadToolTip")
109 { }
110 
~LookImpl()111 WWebWidget::LookImpl::~LookImpl()
112 { }
113 
JavaScriptStatement(JavaScriptStatementType aType,const std::string & aData)114 WWebWidget::OtherImpl::JavaScriptStatement::JavaScriptStatement
115 (JavaScriptStatementType aType, const std::string& aData)
116   : type(aType),
117     data(aData)
118 { }
119 
OtherImpl(WWebWidget * const self)120 WWebWidget::OtherImpl::OtherImpl(WWebWidget *const self)
121   : elementTagName_(nullptr),
122     tabIndex_(std::numeric_limits<int>::min()),
123     scrollVisibilityMargin_(0)
124 { }
125 
~OtherImpl()126 WWebWidget::OtherImpl::~OtherImpl()
127 { }
128 
WWebWidget()129 WWebWidget::WWebWidget()
130   : id_(nullptr) // CNOR needs an extra hint so it sets id_ to null instead of empty string
131 {
132   flags_.set(BIT_INLINE);
133   flags_.set(BIT_ENABLED);
134 }
135 
136 
137 #ifndef WT_TARGET_JAVA
getStateless(Method method)138 WStatelessSlot *WWebWidget::getStateless(Method method)
139 {
140   typedef void (WWebWidget::*Type)();
141 
142   Type focusMethod = &WWebWidget::setFocus;
143 
144   if (method == static_cast<WObject::Method>(focusMethod))
145     return implementStateless(focusMethod,
146 			      &WWebWidget::undoSetFocus);
147   else
148     return WWidget::getStateless(method);
149 }
150 #endif
151 
setFormObject(bool how)152 void WWebWidget::setFormObject(bool how)
153 {
154   flags_.set(BIT_FORM_OBJECT, how);
155 
156   WApplication::instance()
157     ->session()->renderer().updateFormObjects(this, false);
158 }
159 
setId(const std::string & id)160 void WWebWidget::setId(const std::string& id)
161 {
162   if (!otherImpl_)
163     otherImpl_.reset(new OtherImpl(this));
164 
165   WApplication* app = WApplication::instance();
166   for (std::size_t i = 0; i < jsignals_.size(); ++i) {
167     EventSignalBase* signal = jsignals_[i];
168     if (signal->isExposedSignal())
169       app->removeExposedSignal(signal);
170   }
171 
172   if (!id_)
173     id_.reset(new std::string());
174 
175   *id_ = id;
176 
177   for (std::size_t i = 0; i < jsignals_.size(); ++i) {
178     EventSignalBase* signal = jsignals_[i];
179     if (signal->isExposedSignal())
180       app->addExposedSignal(signal);
181   }
182 }
183 
setSelectable(bool selectable)184 void WWebWidget::setSelectable(bool selectable)
185 {
186   flags_.set(BIT_SET_SELECTABLE, selectable);
187   flags_.set(BIT_SET_UNSELECTABLE, !selectable);
188   flags_.set(BIT_SELECTABLE_CHANGED);
189 
190   repaint();
191 }
192 
id()193 const std::string WWebWidget::id() const
194 {
195   if (id_)
196     return *id_;
197   else
198     return WWidget::id();
199 }
200 
repaint(WFlags<RepaintFlag> flags)201 void WWebWidget::repaint(WFlags<RepaintFlag> flags)
202 {
203   /*
204    * If the widget is currently stubbed, we need to redo the slot
205    * learning while unstubbing.
206    */
207   if (isStubbed()) {
208     WebRenderer& renderer = WApplication::instance()->session()->renderer();
209     if (renderer.preLearning())
210       renderer.learningIncomplete();
211   }
212 
213   /*
214    * We ignore repaints to an unrendered widget.
215    */
216   if (!flags_.test(BIT_RENDERED))
217     return;
218 
219   WWidget::scheduleRerender(false, flags);
220 
221   if (flags.test(RepaintFlag::ToAjax))
222     flags_.set(BIT_REPAINT_TO_AJAX);
223 }
224 
renderOk()225 void WWebWidget::renderOk()
226 {
227   WWidget::renderOk();
228 
229   flags_.reset(BIT_REPAINT_TO_AJAX);
230 }
231 
signalConnectionsChanged()232 void WWebWidget::signalConnectionsChanged()
233 {
234   repaint();
235 }
236 
beingDeleted()237 void WWebWidget::beingDeleted()
238 {
239   // flag that we are being deleted, this allows some optimalizations
240   flags_.set(BIT_BEING_DELETED);
241 }
242 
~WWebWidget()243 WWebWidget::~WWebWidget()
244 {
245   beingDeleted();
246   std::unique_ptr<WWidget> unique_this = removeFromParent();
247 #ifndef WT_TARGET_JAVA
248   // removeFromParent should always return a nullptr if it is called in the destructor!
249   assert(unique_this == nullptr);
250 #endif // WT_TARGET_JAVA
251 
252   // Run destructors in this order (same as Wt 3), some destructors
253   // rely on otherImpl_ (loadToolTip_ JSignal destructor unexposes signal,
254   // which calls id(), which looks at otherImpl_)
255   transientImpl_.reset();
256   layoutImpl_.reset();
257   lookImpl_.reset();
258   otherImpl_.reset();
259 }
260 
decorationStyle()261 WCssDecorationStyle& WWebWidget::decorationStyle()
262 {
263   if (!lookImpl_)
264     lookImpl_.reset(new LookImpl(this));
265 
266   if (!lookImpl_->decorationStyle_) {
267     lookImpl_->decorationStyle_.reset(new WCssDecorationStyle());
268     lookImpl_->decorationStyle_->setWebWidget(this);
269   }
270 
271   return *lookImpl_->decorationStyle_;
272 }
273 
decorationStyle()274 const WCssDecorationStyle& WWebWidget::decorationStyle() const
275 {
276   return const_cast<WWebWidget *>(this)->decorationStyle();
277 }
278 
setDecorationStyle(const WCssDecorationStyle & style)279 void WWebWidget::setDecorationStyle(const WCssDecorationStyle& style)
280 {
281 #ifndef WT_TARGET_JAVA
282   decorationStyle() = style;
283 #else
284   if (!lookImpl_)
285     lookImpl_.reset(new LookImpl(this));
286 
287   lookImpl_->decorationStyle_.reset(&style);
288 #endif // WT_TARGET_JAVA
289 }
290 
iterateChildren(const HandleWidgetMethod & method)291 void WWebWidget::iterateChildren(const HandleWidgetMethod& method) const
292 { }
293 
renderRemoveJs(bool recursive)294 std::string WWebWidget::renderRemoveJs(bool recursive)
295 {
296   Wt::WStringStream result;
297 
298   if (isRendered() && scrollVisibilityEnabled()) {
299     result << WT_CLASS ".scrollVisibility.remove("
300         << jsStringLiteral(id()) << ");";
301     flags_.set(BIT_SCROLL_VISIBILITY_CHANGED);
302     flags_.reset(BIT_SCROLL_VISIBILITY_LOADED);
303   }
304 
305   iterateChildren
306     ([&](WWidget *c) {
307       result << c->renderRemoveJs(true);
308     });
309 
310   if (!recursive) {
311     if (result.empty())
312       result << "_" << id();
313     else
314       result << WT_CLASS ".remove('" << id() << "');";
315   }
316 
317   return result.str();
318 }
319 
widgetRemoved(WWidget * child,bool renderRemove)320 void WWebWidget::widgetRemoved(WWidget *child, bool renderRemove)
321 {
322   if (!flags_.test(BIT_BEING_DELETED) && renderRemove) {
323     std::string js = child->renderRemoveJs(false);
324 
325     if (!transientImpl_)
326       transientImpl_.reset(new TransientImpl());
327 
328     transientImpl_->childRemoveChanges_.push_back(js);
329     if (js[0] != '_')
330       transientImpl_->specialChildRemove_ = true;
331 
332     repaint(RepaintFlag::SizeAffected);
333   }
334 
335   child->setParentWidget(nullptr);
336 
337   if (transientImpl_ &&
338       !child->webWidget()->isRendered() &&
339       !child->webWidget()->isStubbed())
340     --transientImpl_->addedChildren_;
341 
342   /*
343    * When the child is about to be deleted, all of its descendants
344    * properly removes itself from the renderer "dirty" list. If not,
345    * we here force this propagation.
346    */
347   if (!child->webWidget()->flags_.test(BIT_BEING_DELETED))
348     child->webWidget()->setRendered(false);
349 
350   WApplication::instance()
351     ->session()->renderer().updateFormObjects(child->webWidget(), true);
352 
353   emitChildrenChanged();
354 }
355 
childrenChanged()356 Signal<>& WWebWidget::childrenChanged()
357 {
358   if (!otherImpl_)
359     otherImpl_.reset(new OtherImpl(this));
360 
361   return otherImpl_->childrenChanged_;
362 }
363 
setPositionScheme(PositionScheme scheme)364 void WWebWidget::setPositionScheme(PositionScheme scheme)
365 {
366   if (!layoutImpl_)
367     layoutImpl_.reset(new LayoutImpl());
368 
369   layoutImpl_->positionScheme_ = scheme;
370 
371   if ((scheme == PositionScheme::Absolute) || (scheme == PositionScheme::Fixed))
372     flags_.reset(BIT_INLINE);
373 
374   flags_.set(BIT_GEOMETRY_CHANGED);
375   repaint(RepaintFlag::SizeAffected);
376 }
377 
positionScheme()378 PositionScheme WWebWidget::positionScheme() const
379 {
380   return layoutImpl_ ? layoutImpl_->positionScheme_ : PositionScheme::Static;
381 }
382 
resize(const WLength & width,const WLength & height)383 void WWebWidget::resize(const WLength& width, const WLength& height)
384 {
385   bool changed = false;
386 
387   if (!width_ && !width.isAuto())
388     width_.reset(new WLength());
389 
390   if (width_ && *width_ != width) {
391     changed = true;
392     *width_ = nonNegative(width);
393     flags_.set(BIT_WIDTH_CHANGED);
394   }
395 
396   if (!height_ && !height.isAuto())
397     height_.reset(new WLength());
398 
399   if (height_ && *height_ != height) {
400     changed = true;
401     *height_ = nonNegative(height);
402     flags_.set(BIT_HEIGHT_CHANGED);
403   }
404 
405   if (changed) {
406     repaint(RepaintFlag::SizeAffected);
407     WWidget::resize(width, height);
408   }
409 }
410 
width()411 WLength WWebWidget::width() const
412 {
413   return width_ ? *width_ : WLength::Auto;
414 }
415 
height()416 WLength WWebWidget::height() const
417 {
418   return height_ ? *height_ : WLength::Auto;
419 }
420 
setMinimumSize(const WLength & width,const WLength & height)421 void WWebWidget::setMinimumSize(const WLength& width, const WLength& height)
422 {
423   if (!layoutImpl_)
424     layoutImpl_.reset(new LayoutImpl());
425 
426   layoutImpl_->minimumWidth_ = nonNegative(width);
427   layoutImpl_->minimumHeight_ = nonNegative(height);
428 
429   flags_.set(BIT_GEOMETRY_CHANGED);
430 
431   repaint(RepaintFlag::SizeAffected);
432 }
433 
minimumWidth()434 WLength WWebWidget::minimumWidth() const
435 {
436   return layoutImpl_ ? layoutImpl_->minimumWidth_ : WLength(0);
437 }
438 
minimumHeight()439 WLength WWebWidget::minimumHeight() const
440 {
441   return layoutImpl_ ? layoutImpl_->minimumHeight_ : WLength(0);
442 }
443 
setMaximumSize(const WLength & width,const WLength & height)444 void WWebWidget::setMaximumSize(const WLength& width, const WLength& height)
445 {
446   if (!layoutImpl_)
447     layoutImpl_.reset(new LayoutImpl());
448 
449   layoutImpl_->maximumWidth_ = nonNegative(width);
450   layoutImpl_->maximumHeight_ = nonNegative(height);
451 
452   flags_.set(BIT_GEOMETRY_CHANGED);
453 
454   repaint(RepaintFlag::SizeAffected);
455 }
456 
maximumWidth()457 WLength WWebWidget::maximumWidth() const
458 {
459   return layoutImpl_ ? layoutImpl_->maximumWidth_ : WLength::Auto;
460 }
461 
maximumHeight()462 WLength WWebWidget::maximumHeight() const
463 {
464   return layoutImpl_ ? layoutImpl_->maximumHeight_ : WLength::Auto;
465 }
466 
setLineHeight(const WLength & height)467 void WWebWidget::setLineHeight(const WLength& height)
468 {
469   if (!layoutImpl_)
470     layoutImpl_.reset(new LayoutImpl());
471 
472   layoutImpl_->lineHeight_ = height;
473 
474   flags_.set(BIT_GEOMETRY_CHANGED);
475 
476   repaint(RepaintFlag::SizeAffected);
477 }
478 
lineHeight()479 WLength WWebWidget::lineHeight() const
480 {
481   return layoutImpl_ ? layoutImpl_->lineHeight_ : WLength::Auto;
482 }
483 
setFloatSide(Side s)484 void WWebWidget::setFloatSide(Side s)
485 {
486   if (!layoutImpl_)
487     layoutImpl_.reset(new LayoutImpl());
488 
489   layoutImpl_->floatSide_ = s;
490 
491   flags_.set(BIT_FLOAT_SIDE_CHANGED);
492 
493   repaint();
494 }
495 
floatSide()496 Side WWebWidget::floatSide() const
497 {
498   if (layoutImpl_)
499     return layoutImpl_->floatSide_;
500   else
501     return static_cast<Side>(0);
502 }
503 
setClearSides(WFlags<Side> sides)504 void WWebWidget::setClearSides(WFlags<Side> sides)
505 {
506   if (!layoutImpl_)
507     layoutImpl_.reset(new LayoutImpl());
508 
509   layoutImpl_->clearSides_ = sides;
510 
511   flags_.set(BIT_GEOMETRY_CHANGED);
512 
513   repaint();
514 }
515 
clearSides()516 WFlags<Side> WWebWidget::clearSides() const
517 {
518   if (layoutImpl_)
519     return layoutImpl_->clearSides_;
520   else
521     return None;
522 }
523 
setVerticalAlignment(AlignmentFlag alignment,const WLength & length)524 void WWebWidget::setVerticalAlignment(AlignmentFlag alignment,
525 				      const WLength& length)
526 {
527   if (AlignHorizontalMask.test(alignment))
528     LOG_ERROR("setVerticalAlignment(): alignment "
529 	      << static_cast<int>(alignment)
530 	      << " is not vertical");
531 
532   if (!layoutImpl_)
533     layoutImpl_.reset(new LayoutImpl());
534 
535   layoutImpl_->verticalAlignment_ = alignment;
536   layoutImpl_->verticalAlignmentLength_ = length;
537 
538   flags_.set(BIT_GEOMETRY_CHANGED);
539 
540   repaint();
541 }
542 
verticalAlignment()543 AlignmentFlag WWebWidget::verticalAlignment() const
544 {
545   return layoutImpl_
546     ? layoutImpl_->verticalAlignment_ : AlignmentFlag::Baseline;
547 }
548 
verticalAlignmentLength()549 WLength WWebWidget::verticalAlignmentLength() const
550 {
551   return layoutImpl_ ? layoutImpl_->verticalAlignmentLength_ : WLength::Auto;
552 }
553 
setOffsets(const WLength & offset,WFlags<Side> sides)554 void WWebWidget::setOffsets(const WLength& offset, WFlags<Side> sides)
555 {
556   if (!layoutImpl_)
557     layoutImpl_.reset(new LayoutImpl());
558 
559   if (sides.test(Side::Top))
560     layoutImpl_->offsets_[0] = offset;
561   if (sides.test(Side::Right))
562     layoutImpl_->offsets_[1] = offset;
563   if (sides.test(Side::Bottom))
564     layoutImpl_->offsets_[2] = offset;
565   if (sides.test(Side::Left))
566     layoutImpl_->offsets_[3] = offset;
567 
568   flags_.set(BIT_GEOMETRY_CHANGED);
569 
570   repaint();
571 }
572 
offset(Side s)573 WLength WWebWidget::offset(Side s) const
574 {
575   if (!layoutImpl_)
576     return WLength::Auto;
577 
578   switch (s) {
579   case Side::Top:
580     return layoutImpl_->offsets_[0];
581   case Side::Right:
582     return layoutImpl_->offsets_[1];
583   case Side::Bottom:
584     return layoutImpl_->offsets_[2];
585   case Side::Left:
586     return layoutImpl_->offsets_[3];
587   default:
588     LOG_ERROR("offset(Side) with invalid side: " << (int)s);
589     return WLength();
590   }
591 }
592 
zIndex()593 int WWebWidget::zIndex() const
594 {
595   if (layoutImpl_)
596     return layoutImpl_->zIndex_;
597   else
598     return 0;
599 }
600 
setInline(bool inl)601 void WWebWidget::setInline(bool inl)
602 {
603   flags_.set(BIT_INLINE, inl);
604 
605   resetLearnedSlot(&WWidget::show);
606 
607   flags_.set(BIT_GEOMETRY_CHANGED);
608 
609   repaint();
610 }
611 
isInline()612 bool WWebWidget::isInline() const
613 {
614   return flags_.test(BIT_INLINE);
615 }
616 
setFlexBox(bool enabled)617 void WWebWidget::setFlexBox(bool enabled)
618 {
619   flags_.set(BIT_FLEX_BOX, enabled);
620   flags_.set(BIT_FLEX_BOX_CHANGED);
621 }
622 
setPopup(bool popup)623 void WWebWidget::setPopup(bool popup)
624 {
625   if (!layoutImpl_)
626     layoutImpl_.reset(new LayoutImpl());
627 
628   layoutImpl_->zIndex_ = popup ? -1 : 0;
629 
630   if (popup && parent())
631     calcZIndex();
632 
633   flags_.set(BIT_ZINDEX_CHANGED);
634 
635   repaint();
636 }
637 
setZIndex(int zIndex)638 void WWebWidget::setZIndex(int zIndex)
639 {
640   if (!layoutImpl_)
641     layoutImpl_.reset(new LayoutImpl());
642 
643   layoutImpl_->zIndex_ = zIndex;
644 
645   flags_.set(BIT_ZINDEX_CHANGED);
646 
647   repaint();
648 }
649 
setParentWidget(WWidget * parent)650 void WWebWidget::setParentWidget(WWidget *parent)
651 {
652   WWidget::setParentWidget(parent);
653 
654   if (parent)
655     if (isPopup())
656       calcZIndex();
657 }
658 
parentWebWidget()659 WWebWidget *WWebWidget::parentWebWidget() const
660 {
661   /*
662    * Returns the parent webwidget, i.e. skipping composite widgets
663    */
664   WWidget *p = this->parent();
665   while (p != nullptr && dynamic_cast<WCompositeWidget *>(p) != nullptr)
666     p = p->parent();
667 
668   return p ? p->webWidget() : nullptr;
669 }
670 
selfWidget()671 WWidget *WWebWidget::selfWidget()
672 {
673   /*
674    * Returns the composite widget implemented by this web widget
675    */
676   WWidget *p = nullptr, *p_parent = this;
677   do {
678     p = p_parent;
679     p_parent = p->parent();
680   } while (p_parent != nullptr &&
681 	   dynamic_cast<WCompositeWidget *>(p_parent) != nullptr);
682 
683   return p;
684 }
685 
calcZIndex()686 void WWebWidget::calcZIndex()
687 {
688   layoutImpl_->zIndex_ = -1;
689 
690   WWebWidget *ww = parentWebWidget();
691   if (ww) {
692     const std::vector<WWidget *>& children = ww->children();
693 
694     int maxZ = 0;
695     for (unsigned i = 0; i < children.size(); ++i) {
696       WWebWidget *wi = children[i]->webWidget();
697       if (wi->baseZIndex() <= baseZIndex())
698         maxZ = std::max(maxZ, wi->zIndex());
699     }
700 
701     layoutImpl_->zIndex_ = std::max(baseZIndex(), maxZ + Z_INDEX_INCREMENT);
702   }
703 }
704 
isPopup()705 bool WWebWidget::isPopup() const
706 {
707   return layoutImpl_ ? layoutImpl_->zIndex_ != 0 : false;
708 }
709 
setMargin(const WLength & margin,WFlags<Side> sides)710 void WWebWidget::setMargin(const WLength& margin, WFlags<Side> sides)
711 {
712   if (!layoutImpl_)
713     layoutImpl_.reset(new LayoutImpl());
714 
715   if (sides.test(Side::Top))
716     layoutImpl_->margin_[0] = margin;
717   if (sides.test(Side::Right))
718     layoutImpl_->margin_[1] = margin;
719   if (sides.test(Side::Bottom))
720     layoutImpl_->margin_[2] = margin;
721   if (sides.test(Side::Left))
722     layoutImpl_->margin_[3] = margin;
723 
724   flags_.set(BIT_MARGINS_CHANGED);
725 
726   repaint(RepaintFlag::SizeAffected);
727 }
728 
margin(Side side)729 WLength WWebWidget::margin(Side side) const
730 {
731   if (!layoutImpl_)
732     return WLength(0);
733 
734   switch (side) {
735   case Side::Top:
736     return layoutImpl_->margin_[0];
737   case Side::Right:
738     return layoutImpl_->margin_[1];
739   case Side::Bottom:
740     return layoutImpl_->margin_[2];
741   case Side::Left:
742     return layoutImpl_->margin_[3];
743   default:
744     LOG_ERROR("margin(Side) with invalid side: " << (int)side);
745     return WLength();
746   }
747 }
748 
addStyleClass(const WT_USTRING & styleClass,bool force)749 void WWebWidget::addStyleClass(const WT_USTRING& styleClass, bool force)
750 {
751   if (!lookImpl_)
752     lookImpl_.reset(new LookImpl(this));
753 
754   std::string currentClass = lookImpl_->styleClass_.toUTF8();
755   Utils::SplitSet classes;
756   Utils::split(classes, currentClass, " ", true);
757 
758   if (classes.find(styleClass.toUTF8()) == classes.end()) {
759     lookImpl_->styleClass_
760       = WT_USTRING::fromUTF8(Utils::addWord(lookImpl_->styleClass_.toUTF8(),
761 					    styleClass.toUTF8()));
762 
763     if (!force) {
764       flags_.set(BIT_STYLECLASS_CHANGED);
765       repaint(RepaintFlag::SizeAffected);
766     }
767   }
768 
769   if (force && isRendered()) {
770     if (!transientImpl_)
771       transientImpl_.reset(new TransientImpl());
772 
773     Utils::add(transientImpl_->addedStyleClasses_, styleClass);
774     Utils::erase(transientImpl_->removedStyleClasses_, styleClass);
775 
776     repaint(RepaintFlag::SizeAffected);
777   }
778 }
779 
removeStyleClass(const WT_USTRING & styleClass,bool force)780 void WWebWidget::removeStyleClass(const WT_USTRING& styleClass, bool force)
781 {
782   if (!lookImpl_)
783     lookImpl_.reset(new LookImpl(this));
784 
785   if (hasStyleClass(styleClass)) {
786     // perhaps it is quicker to join the classes back, but then we need to
787     // make sure we keep the original order ?
788     lookImpl_->styleClass_
789       = WT_USTRING::fromUTF8(Utils::eraseWord(lookImpl_->styleClass_.toUTF8(),
790 					      styleClass.toUTF8()));
791     if (!force) {
792       flags_.set(BIT_STYLECLASS_CHANGED);
793       repaint(RepaintFlag::SizeAffected);
794     }
795   }
796 
797   if (force && isRendered()) {
798     if (!transientImpl_)
799       transientImpl_.reset(new TransientImpl());
800 
801     Utils::add(transientImpl_->removedStyleClasses_, styleClass);
802     Utils::erase(transientImpl_->addedStyleClasses_, styleClass);
803 
804     repaint(RepaintFlag::SizeAffected);
805   }
806 }
807 
addStyleClass(const char * styleClass,bool force)808 void WWebWidget::addStyleClass(const char *styleClass, bool force)
809 {
810   addStyleClass(WString::fromUTF8(styleClass), force);
811 }
812 
removeStyleClass(const char * styleClass,bool force)813 void WWebWidget::removeStyleClass(const char *styleClass, bool force)
814 {
815   removeStyleClass(WString::fromUTF8(styleClass), force);
816 }
817 
hasStyleClass(const WT_USTRING & styleClass)818 bool WWebWidget::hasStyleClass(const WT_USTRING& styleClass) const
819 {
820   if (!lookImpl_)
821     return false;
822 
823   std::string currentClass = lookImpl_->styleClass_.toUTF8();
824   Utils::SplitSet classes;
825   Utils::split(classes, currentClass, " ", true);
826 
827   return classes.find(styleClass.toUTF8()) != classes.end();
828 }
829 
setStyleClass(const WT_USTRING & styleClass)830 void WWebWidget::setStyleClass(const WT_USTRING& styleClass)
831 {
832   if (canOptimizeUpdates() && (styleClass == this->styleClass()))
833     return;
834 
835   if (!lookImpl_)
836     lookImpl_.reset(new LookImpl(this));
837 
838   lookImpl_->styleClass_ = styleClass;
839 
840   flags_.set(BIT_STYLECLASS_CHANGED);
841 
842   repaint(RepaintFlag::SizeAffected);
843 }
844 
setStyleClass(const char * styleClass)845 void WWebWidget::setStyleClass(const char *styleClass)
846 {
847   setStyleClass(WString::fromUTF8(styleClass));
848 }
849 
styleClass()850 WT_USTRING WWebWidget::styleClass() const
851 {
852   return lookImpl_ ? lookImpl_->styleClass_ : WT_USTRING();
853 }
854 
setAttributeValue(const std::string & name,const WT_USTRING & value)855 void WWebWidget::setAttributeValue(const std::string& name,
856 				   const WT_USTRING& value)
857 {
858   if (!otherImpl_)
859     otherImpl_.reset(new OtherImpl(this));
860 
861   if (!otherImpl_->attributes_)
862     otherImpl_->attributes_.reset(new std::map<std::string, WT_USTRING>);
863 
864   std::map<std::string, WT_USTRING>::const_iterator i
865     = otherImpl_->attributes_->find(name);
866 
867   if (i != otherImpl_->attributes_->end() && i->second == value)
868     return;
869 
870   (*otherImpl_->attributes_)[name] = value;
871 
872   if (!transientImpl_)
873     transientImpl_.reset(new TransientImpl());
874 
875   transientImpl_->attributesSet_.push_back(name);
876 
877   repaint();
878 }
879 
attributeValue(const std::string & name)880 WT_USTRING WWebWidget::attributeValue(const std::string& name) const
881 {
882   if (otherImpl_ && otherImpl_->attributes_) {
883     std::map<std::string, WT_USTRING>::const_iterator i
884       = otherImpl_->attributes_->find(name);
885 
886     if (i != otherImpl_->attributes_->end())
887       return i->second;
888   }
889 
890   return WT_USTRING();
891 }
892 
setJavaScriptMember(const std::string & name,const std::string & value)893 void WWebWidget::setJavaScriptMember(const std::string& name,
894 				     const std::string& value)
895 {
896   if (!otherImpl_)
897     otherImpl_.reset(new OtherImpl(this));
898 
899   if (!otherImpl_->jsMembers_)
900     otherImpl_->jsMembers_.reset(new std::vector<OtherImpl::Member>);
901 
902   std::vector<OtherImpl::Member>& members = *otherImpl_->jsMembers_;
903   int index = indexOfJavaScriptMember(name);
904 
905   if (index != -1 && (members[index].value == value))
906     return;
907 
908   if (value.empty()) {
909     if (index != -1)
910       members.erase(members.begin() + index);
911     else
912       return;
913   } else {
914     if (index == -1) {
915       OtherImpl::Member m;
916       m.name = name;
917       m.value = value;
918       members.push_back(m);
919     } else {
920       members[index].value = value;
921     }
922   }
923 
924   addJavaScriptStatement(JavaScriptStatementType::SetMember, name);
925 
926   repaint();
927 }
928 
javaScriptMember(const std::string & name)929 std::string WWebWidget::javaScriptMember(const std::string& name) const
930 {
931   int index = indexOfJavaScriptMember(name);
932   if (index != -1)
933     return (*otherImpl_->jsMembers_)[index].value;
934   else
935     return std::string();
936 }
937 
indexOfJavaScriptMember(const std::string & name)938 int WWebWidget::indexOfJavaScriptMember(const std::string& name) const
939 {
940   if (otherImpl_ && otherImpl_->jsMembers_)
941   for (unsigned i = 0; i < otherImpl_->jsMembers_->size(); i++)
942     if ((*otherImpl_->jsMembers_)[i].name == name)
943       return i;
944 
945   return -1;
946 }
947 
callJavaScriptMember(const std::string & name,const std::string & args)948 void WWebWidget::callJavaScriptMember(const std::string& name,
949 				      const std::string& args)
950 {
951   addJavaScriptStatement(JavaScriptStatementType::CallMethod,
952 			 name + "(" + args + ");");
953 
954   repaint();
955 }
956 
addJavaScriptStatement(JavaScriptStatementType type,const std::string & data)957 void WWebWidget::addJavaScriptStatement(JavaScriptStatementType type,
958 					const std::string& data)
959 {
960   if (!otherImpl_)
961     otherImpl_.reset(new OtherImpl(this));
962 
963   if (!otherImpl_->jsStatements_)
964     otherImpl_->jsStatements_.reset
965       (new std::vector<OtherImpl::JavaScriptStatement>());
966 
967   std::vector<OtherImpl::JavaScriptStatement>& v = *otherImpl_->jsStatements_;
968 
969   /*
970    * a SetMember is idempotent, if one is already scheduled we do not need
971    * to add another statement.
972    */
973   if (type == JavaScriptStatementType::SetMember) {
974     for (unsigned i = 0; i < v.size(); ++i) {
975       if (v[i].type == JavaScriptStatementType::SetMember && v[i].data == data)
976 	return;
977     }
978   }
979 
980   /*
981    * If the last statement is exactly the same, then it's a dupe, discard it
982    * too.
983    */
984   if (v.empty() ||
985       v.back().type != type ||
986       v.back().data != data)
987     v.push_back(OtherImpl::JavaScriptStatement(type, data));
988 }
989 
990 
loadToolTip()991 void WWebWidget::loadToolTip()
992 {
993   if (!lookImpl_->toolTip_)
994     lookImpl_->toolTip_.reset(new WString());
995   *lookImpl_->toolTip_ = toolTip();
996 
997   flags_.set(BIT_TOOLTIP_CHANGED);
998   repaint();
999 }
1000 
setToolTip(const WString & text,TextFormat textFormat)1001 void WWebWidget::setToolTip(const WString& text, TextFormat textFormat)
1002 {
1003   flags_.reset(BIT_TOOLTIP_DEFERRED);
1004 
1005   if (canOptimizeUpdates() && text == storedToolTip())
1006     return;
1007 
1008   if (!lookImpl_)
1009     lookImpl_.reset(new LookImpl(this));
1010 
1011   if (!lookImpl_->toolTip_)
1012     lookImpl_->toolTip_.reset(new WString());
1013 
1014   *lookImpl_->toolTip_ = text;
1015   lookImpl_->toolTipTextFormat_ = textFormat;
1016 
1017   flags_.set(BIT_TOOLTIP_CHANGED);
1018 
1019   repaint();
1020 }
1021 
toolTip()1022 WString WWebWidget::toolTip() const
1023 {
1024   return storedToolTip();
1025 }
1026 
storedToolTip()1027 WString WWebWidget::storedToolTip() const
1028 {
1029   return lookImpl_ && lookImpl_->toolTip_
1030     ? *lookImpl_->toolTip_
1031     : WString::Empty;
1032 }
1033 
setDeferredToolTip(bool enable,TextFormat textFormat)1034 void WWebWidget::setDeferredToolTip(bool enable, TextFormat textFormat)
1035 {
1036   flags_.set(BIT_TOOLTIP_DEFERRED, enable);
1037 
1038   if (!enable)
1039     setToolTip("", textFormat);
1040   else{
1041     if (!lookImpl_)
1042       lookImpl_.reset(new LookImpl(this));
1043 
1044     if (!lookImpl_->toolTip_)
1045       lookImpl_->toolTip_.reset(new WString());
1046     else
1047       *lookImpl_->toolTip_ = WString();
1048 
1049     lookImpl_->toolTipTextFormat_ = textFormat;
1050 
1051     flags_.set(BIT_TOOLTIP_CHANGED);
1052 
1053     repaint();
1054   }
1055 }
1056 
setHiddenKeepsGeometry(bool enabled)1057 void WWebWidget::setHiddenKeepsGeometry(bool enabled)
1058 {
1059   flags_.set(BIT_DONOT_STUB);
1060   flags_.set(BIT_HIDE_WITH_VISIBILITY, enabled);
1061   flags_.set(BIT_HIDDEN_CHANGED);
1062 }
1063 
hiddenKeepsGeometry()1064 bool WWebWidget::hiddenKeepsGeometry() const
1065 {
1066   return flags_.test(BIT_HIDE_WITH_VISIBILITY)
1067     && !flags_.test(BIT_HIDE_WITH_OFFSETS);
1068 }
1069 
setHidden(bool hidden,const WAnimation & animation)1070 void WWebWidget::setHidden(bool hidden, const WAnimation& animation)
1071 {
1072   if (canOptimizeUpdates() && (animation.empty() && hidden == isHidden()))
1073     return;
1074 
1075   bool wasVisible = isVisible();
1076 
1077   flags_.set(BIT_HIDDEN, hidden);
1078   flags_.set(BIT_HIDDEN_CHANGED);
1079 
1080   if (!animation.empty()
1081       && WApplication::instance()->environment().supportsCss3Animations()
1082       && WApplication::instance()->environment().ajax()) {
1083     if (!transientImpl_)
1084       transientImpl_.reset(new TransientImpl());
1085     transientImpl_->animation_ = animation;
1086   }
1087 
1088   bool shouldBeVisible = !hidden;
1089   if (shouldBeVisible && parent())
1090     shouldBeVisible = parent()->isVisible();
1091 
1092   if (!canOptimizeUpdates() || shouldBeVisible != wasVisible)
1093     propagateSetVisible(shouldBeVisible);
1094 
1095   WApplication::instance()
1096     ->session()->renderer().updateFormObjects(this, true);
1097 
1098   repaint(RepaintFlag::SizeAffected);
1099 }
1100 
parentResized(WWidget * parent,WFlags<Orientation> directions)1101 void WWebWidget::parentResized(WWidget *parent, WFlags<Orientation> directions)
1102 {
1103   if (flags_.test(BIT_CONTAINS_LAYOUT)) {
1104     iterateChildren
1105       ([=](WWidget *c) {
1106 	if (!c->isHidden())
1107 	  c->webWidget()->parentResized(parent, directions);
1108       });
1109   }
1110 }
1111 
containsLayout()1112 void WWebWidget::containsLayout()
1113 {
1114   if (!flags_.test(BIT_CONTAINS_LAYOUT)) {
1115     flags_.set(BIT_CONTAINS_LAYOUT);
1116     WWebWidget *p = parentWebWidget();
1117     if (p)
1118       p->containsLayout();
1119   }
1120 }
1121 
isHidden()1122 bool WWebWidget::isHidden() const
1123 {
1124   return flags_.test(BIT_HIDDEN);
1125 }
1126 
isVisible()1127 bool WWebWidget::isVisible() const
1128 {
1129   if (flags_.test(BIT_STUBBED) || flags_.test(BIT_HIDDEN))
1130     return false;
1131   else
1132     if (parent())
1133       return parent()->isVisible();
1134     else
1135       return this == WApplication::instance()->domRoot() ||
1136 	     this == WApplication::instance()->domRoot2();
1137 }
1138 
setDisabled(bool disabled)1139 void WWebWidget::setDisabled(bool disabled)
1140 {
1141   if (canOptimizeUpdates() && (disabled == flags_.test(BIT_DISABLED)))
1142     return;
1143 
1144   bool wasEnabled = isEnabled();
1145 
1146   flags_.set(BIT_DISABLED, disabled);
1147   flags_.set(BIT_DISABLED_CHANGED);
1148 
1149   bool shouldBeEnabled = !disabled;
1150   if (shouldBeEnabled && parent())
1151     shouldBeEnabled = parent()->isEnabled();
1152 
1153   if (shouldBeEnabled != wasEnabled)
1154     propagateSetEnabled(shouldBeEnabled);
1155 
1156   WApplication::instance()
1157     ->session()->renderer().updateFormObjects(this, true);
1158 
1159   repaint();
1160 }
1161 
propagateSetEnabled(bool enabled)1162 void WWebWidget::propagateSetEnabled(bool enabled)
1163 {
1164   iterateChildren
1165     ([=](WWidget *c) {
1166       if (!c->isDisabled())
1167 	c->webWidget()->propagateSetEnabled(enabled);
1168       });
1169 }
1170 
propagateSetVisible(bool visible)1171 void WWebWidget::propagateSetVisible(bool visible)
1172 {
1173   iterateChildren
1174     ([=](WWidget *c) {
1175       if (!c->isHidden())
1176         c->webWidget()->propagateSetVisible(visible);
1177       });
1178 }
1179 
isDisabled()1180 bool WWebWidget::isDisabled() const
1181 {
1182   return flags_.test(BIT_DISABLED);
1183 }
1184 
isEnabled()1185 bool WWebWidget::isEnabled() const
1186 {
1187   if (isDisabled())
1188     return false;
1189   else
1190     if (parent())
1191       return parent()->isEnabled();
1192     else
1193       return true;
1194 }
1195 
widgetAdded(WWidget * child)1196 void WWebWidget::widgetAdded(WWidget *child)
1197 {
1198   child->setParentWidget(this);
1199 
1200   if (flags_.test(BIT_LOADED)) {
1201     doLoad(child);
1202   }
1203 
1204   WApplication::instance()
1205     ->session()->renderer().updateFormObjects(this, false);
1206 
1207   if (!transientImpl_)
1208     transientImpl_.reset(new TransientImpl());
1209   ++transientImpl_->addedChildren_;
1210 
1211   emitChildrenChanged();
1212 }
1213 
children()1214 std::vector<WWidget *> WWebWidget::children() const
1215 {
1216   std::vector<WWidget *> result;
1217 
1218   iterateChildren
1219     ([&](WWidget *c) {
1220       result.push_back(c);
1221     });
1222 
1223   return result;
1224 }
1225 
setHideWithOffsets(bool how)1226 void WWebWidget::setHideWithOffsets(bool how)
1227 {
1228   if (how) {
1229     if (!flags_.test(BIT_HIDE_WITH_OFFSETS)) {
1230       flags_.set(BIT_HIDE_WITH_VISIBILITY);
1231       flags_.set(BIT_HIDE_WITH_OFFSETS);
1232 
1233       resetLearnedSlot(&WWidget::show);
1234       resetLearnedSlot(&WWidget::hide);
1235 
1236       if (parent())
1237 	parent()->setHideWithOffsets(true);
1238     }
1239   }
1240 }
1241 
setImplementLayoutSizeAware(bool aware)1242 void WWebWidget::setImplementLayoutSizeAware(bool aware)
1243 {
1244   if (!aware) {
1245     if (otherImpl_) {
1246       if (otherImpl_->resized_) {
1247 	otherImpl_->resized_.reset();
1248 
1249 	std::string v = javaScriptMember(WT_RESIZE_JS);
1250 	if (v.length() == 1)
1251 	  setJavaScriptMember(WT_RESIZE_JS, std::string());
1252 	else
1253 	  addJavaScriptStatement(JavaScriptStatementType::SetMember, WT_RESIZE_JS);
1254       }
1255     }
1256   }
1257 }
1258 
resized()1259 JSignal<int, int>& WWebWidget::resized()
1260 {
1261   if (!otherImpl_)
1262     otherImpl_.reset(new OtherImpl(this));
1263 
1264   if (!otherImpl_->resized_) {
1265     otherImpl_->resized_.reset(new JSignal<int, int>(this, "resized"));
1266     otherImpl_->resized_->connect(this, &WWidget::layoutSizeChanged);
1267 
1268     std::string v = javaScriptMember(WT_RESIZE_JS);
1269     if (v.empty())
1270       setJavaScriptMember(WT_RESIZE_JS, "0");
1271     else
1272       addJavaScriptStatement(JavaScriptStatementType::SetMember, WT_RESIZE_JS);
1273   }
1274 
1275   return *otherImpl_->resized_;
1276 }
1277 
updateDom(DomElement & element,bool all)1278 void WWebWidget::updateDom(DomElement& element, bool all)
1279 {
1280   WApplication *app = nullptr;
1281 
1282   /*
1283    * determine display
1284    */
1285 
1286   if (flags_.test(BIT_GEOMETRY_CHANGED) ||
1287       flags_.test(BIT_FLEX_BOX_CHANGED) ||
1288       (!flags_.test(BIT_HIDE_WITH_VISIBILITY) &&
1289         flags_.test(BIT_HIDDEN_CHANGED)) ||
1290       all) {
1291     if (flags_.test(BIT_HIDE_WITH_VISIBILITY) || !flags_.test(BIT_HIDDEN)) {
1292       const char *Inline = "inline";
1293       const char *InlineTable = "inline-table";
1294       const char *InlineBlock = "inline-block";
1295       const char *Block = "block";
1296       const char *Flex = "flex";
1297       const char *InlineFlex = "inline-flex";
1298       const char *Empty = "";
1299       const char *display = nullptr;
1300 
1301       const bool defaultInline = element.type() == DomElementType::OTHER ? DomElement::isDefaultInline(domElementType()) : element.isDefaultInline();
1302       if (defaultInline != flags_.test(BIT_INLINE)) {
1303 	if (flags_.test(BIT_INLINE)) {
1304 	  if (element.type() == DomElementType::TABLE)
1305 	    display = InlineTable;
1306 	  if (element.type() == DomElementType::LI)
1307 	    display = Inline;
1308 	  else if (element.type() != DomElementType::TD) {
1309 	    if (!app) app = WApplication::instance();
1310 	    if (app->environment().agentIsIElt(9)) {
1311 	      display = Inline;
1312 	      element.setProperty(Property::StyleZoom, "1");
1313 	    } else
1314 	      display = InlineBlock;
1315 	  }
1316 	} else {
1317 	  display = Block;
1318 	}
1319       } else if (!all && flags_.test(BIT_HIDDEN_CHANGED)) {
1320 	if (defaultInline == flags_.test(BIT_INLINE))
1321 	  display = Empty;
1322 	else
1323 	  display = flags_.test(BIT_INLINE) ? Inline : Block;
1324       }
1325 
1326       if (flags_.test(BIT_FLEX_BOX))
1327 	display = flags_.test(BIT_INLINE) ? InlineFlex : Flex;
1328       else if (flags_.test(BIT_FLEX_BOX_CHANGED) && !display)
1329         display = Empty;
1330 
1331       if (display)
1332 	element.setProperty(Property::StyleDisplay, display);
1333     } else
1334       element.setProperty(Property::StyleDisplay, "none");
1335   }
1336 
1337   if (flags_.test(BIT_ZINDEX_CHANGED) || all) {
1338     if (layoutImpl_) {
1339       /*
1340        * set z-index
1341        */
1342       if (layoutImpl_->zIndex_ > 0) {
1343 	element.setProperty(Property::StyleZIndex,
1344 			    std::to_string(layoutImpl_->zIndex_));
1345 	element.addPropertyWord(Property::Class, "Wt-popup");
1346 	if (!all &&
1347 	    !flags_.test(BIT_STYLECLASS_CHANGED) &&
1348 	    lookImpl_ && !lookImpl_->styleClass_.empty())
1349 	  element.addPropertyWord(Property::Class,
1350 				  lookImpl_->styleClass_.toUTF8());
1351 
1352 	if (!app) app = WApplication::instance();
1353 	if (all && app->environment().agent() == UserAgent::IE6
1354 	    && element.type() == DomElementType::DIV) {
1355 	  DomElement *i = DomElement::createNew(DomElementType::IFRAME);
1356 	  i->setId("sh" + id());
1357 	  i->setProperty(Property::Class, "Wt-shim");
1358 	  i->setProperty(Property::Src, "javascript:false;");
1359 	  i->setAttribute("title", "Popup Shim");
1360 	  i->setAttribute("tabindex", "-1");
1361 	  i->setAttribute("frameborder", "0");
1362 
1363 	  app->addAutoJavaScript
1364 	    ("{var w = " + jsRef() + ";"
1365 	     "if (w && !" WT_CLASS ".isHidden(w)) {"
1366 	     "var i = " WT_CLASS ".getElement('" + i->id() + "');"
1367 	     "i.style.width=w.clientWidth + 'px';"
1368 	     "i.style.height=w.clientHeight + 'px';"
1369 	     "}}");
1370 
1371 	  element.addChild(i);
1372 	}
1373       }
1374     }
1375 
1376     flags_.reset(BIT_ZINDEX_CHANGED);
1377   }
1378 
1379   if (flags_.test(BIT_GEOMETRY_CHANGED) || all) {
1380     if (layoutImpl_) {
1381       /*
1382        * set position
1383        */
1384       if (!(flags_.test(BIT_HIDE_WITH_VISIBILITY) && flags_.test(BIT_HIDDEN)))
1385 	switch (layoutImpl_->positionScheme_) {
1386 	case PositionScheme::Static:
1387 	  break;
1388 	case PositionScheme::Relative:
1389 	  element.setProperty(Property::StylePosition, "relative"); break;
1390 	case PositionScheme::Absolute:
1391 	  element.setProperty(Property::StylePosition, "absolute"); break;
1392 	case PositionScheme::Fixed:
1393 	  element.setProperty(Property::StylePosition, "fixed"); break;
1394 	}
1395 
1396       /*
1397        * set clear: FIXME: multiple values
1398        */
1399       if (layoutImpl_->clearSides_ == Side::Left) {
1400 	element.setProperty(Property::StyleClear, "left");
1401       } else if (layoutImpl_->clearSides_ == Side::Right) {
1402 	element.setProperty(Property::StyleClear, "right");
1403       } else if (layoutImpl_->clearSides_ == (Side::Left | Side::Right)) {
1404 	element.setProperty(Property::StyleClear, "both");
1405       }
1406 
1407       if (layoutImpl_->minimumWidth_.value() != 0) {
1408 	std::string text
1409 	  = layoutImpl_->minimumWidth_.isAuto() ? "0px"
1410 	  : layoutImpl_->minimumWidth_.cssText();
1411 	element.setProperty(Property::StyleMinWidth, text);
1412       }
1413       if (layoutImpl_->minimumHeight_.value() != 0) {
1414 	std::string text
1415 	  = layoutImpl_->minimumHeight_.isAuto() ? "0px"
1416 	  : layoutImpl_->minimumHeight_.cssText();
1417 	element.setProperty(Property::StyleMinHeight, text);
1418       }
1419       if (!layoutImpl_->maximumWidth_.isAuto())
1420 	element.setProperty(Property::StyleMaxWidth,
1421 			    layoutImpl_->maximumWidth_.cssText());
1422       if (!layoutImpl_->maximumHeight_.isAuto())
1423 	element.setProperty(Property::StyleMaxHeight,
1424 			    layoutImpl_->maximumHeight_.cssText());
1425 
1426       /*
1427        * set offsets
1428        */
1429       if (layoutImpl_->positionScheme_ != PositionScheme::Static) {
1430 	static const Property properties[] = { Property::StyleTop,
1431 					       Property::StyleRight,
1432 					       Property::StyleBottom,
1433 					       Property::StyleLeft };
1434 
1435 	if (!layoutImpl_->offsets_[0].isAuto()
1436 	    || !layoutImpl_->offsets_[1].isAuto()
1437 	    || !layoutImpl_->offsets_[2].isAuto()
1438 	    || !layoutImpl_->offsets_[3].isAuto()) {
1439 	  for (unsigned i = 0; i < 4; ++i) {
1440 	    Property property = properties[i];
1441 
1442 	    if (!app) app = WApplication::instance();
1443 
1444 	    if (app->layoutDirection() == LayoutDirection::RightToLeft) {
1445 	      if (i == 1) property = properties[3];
1446 	      else if (i == 3) property = properties[1];
1447 	    }
1448 
1449 	    if ((app->environment().ajax()
1450 		 && !app->environment().agentIsIElt(9))
1451 		|| !layoutImpl_->offsets_[i].isAuto())
1452 	      element.setProperty(property, layoutImpl_->offsets_[i].cssText());
1453 	  }
1454 	}
1455       }
1456 
1457       /*
1458        * set vertical alignment
1459        */
1460       switch (layoutImpl_->verticalAlignment_) {
1461       case AlignmentFlag::Baseline:
1462 	break;
1463       case AlignmentFlag::Sub:
1464 	element.setProperty(Property::StyleVerticalAlign, "sub"); break;
1465       case AlignmentFlag::Super:
1466 	element.setProperty(Property::StyleVerticalAlign, "super"); break;
1467       case AlignmentFlag::Top:
1468 	element.setProperty(Property::StyleVerticalAlign, "top"); break;
1469       case AlignmentFlag::TextTop:
1470 	element.setProperty(Property::StyleVerticalAlign, "text-top"); break;
1471       case AlignmentFlag::Middle:
1472 	element.setProperty(Property::StyleVerticalAlign, "middle"); break;
1473       case AlignmentFlag::Bottom:
1474 	element.setProperty(Property::StyleVerticalAlign, "bottom"); break;
1475       case AlignmentFlag::TextBottom:
1476 	element.setProperty(Property::StyleVerticalAlign, "text-bottom"); break;
1477       default:
1478 	break;
1479       }
1480 
1481       if (!layoutImpl_->lineHeight_.isAuto()) // == none
1482 	element.setProperty(Property::StyleLineHeight,
1483 			    layoutImpl_->lineHeight_.cssText());
1484 
1485     }
1486 
1487     flags_.reset(BIT_GEOMETRY_CHANGED);
1488   }
1489 
1490 
1491   /*
1492    * set width & height
1493    */
1494   if (width_ && (flags_.test(BIT_WIDTH_CHANGED) || all)) {
1495     if (!all || !width_->isAuto())
1496       element.setProperty(Property::StyleWidth, width_->cssText());
1497     flags_.reset(BIT_WIDTH_CHANGED);
1498   }
1499 
1500   if (height_ && (flags_.test(BIT_HEIGHT_CHANGED) || all)) {
1501     if (!all || !height_->isAuto())
1502       element.setProperty(Property::StyleHeight, height_->cssText());
1503     flags_.reset(BIT_HEIGHT_CHANGED);
1504   }
1505 
1506   if (flags_.test(BIT_FLOAT_SIDE_CHANGED) || all) {
1507     if (layoutImpl_) {
1508       if (layoutImpl_->floatSide_ == static_cast<Side>(0)) {
1509 	if (flags_.test(BIT_FLOAT_SIDE_CHANGED))
1510 	  element.setProperty(Property::StyleFloat, "none");
1511       }
1512       else {
1513         /*
1514         * set float
1515         */
1516 	if (!app) app = WApplication::instance();
1517 
1518 	bool ltr = app->layoutDirection() == LayoutDirection::LeftToRight;
1519         switch (layoutImpl_->floatSide_) {
1520         case Side::Left:
1521 	  element.setProperty(Property::StyleFloat, ltr ? "left" : "right");
1522 	  break;
1523         case Side::Right:
1524 	  element.setProperty(Property::StyleFloat, ltr ? "right" : "left");
1525 	  break;
1526         default:
1527 	  /* illegal values */
1528 	  ;
1529         }
1530       }
1531     }
1532 
1533     flags_.reset(BIT_FLOAT_SIDE_CHANGED);
1534   }
1535 
1536   if (layoutImpl_) {
1537     bool changed = flags_.test(BIT_MARGINS_CHANGED);
1538     if (changed || all) {
1539       if (changed || (layoutImpl_->margin_[0].value() != 0))
1540 	element.setProperty(Property::StyleMarginTop,
1541 			    layoutImpl_->margin_[0].cssText());
1542       if (changed || (layoutImpl_->margin_[1].value() != 0))
1543 	element.setProperty(Property::StyleMarginRight,
1544 			    layoutImpl_->margin_[1].cssText());
1545       if (changed || (layoutImpl_->margin_[2].value() != 0))
1546 	element.setProperty(Property::StyleMarginBottom,
1547 			    layoutImpl_->margin_[2].cssText());
1548       if (changed || (layoutImpl_->margin_[3].value() != 0))
1549 	element.setProperty(Property::StyleMarginLeft,
1550 			    layoutImpl_->margin_[3].cssText());
1551 
1552       flags_.reset(BIT_MARGINS_CHANGED);
1553     }
1554   }
1555 
1556   if (lookImpl_) {
1557     if ((lookImpl_->toolTip_ || flags_.test(BIT_TOOLTIP_DEFERRED))
1558         && (flags_.test(BIT_TOOLTIP_CHANGED) || all)) {
1559       if (!all || (!lookImpl_->toolTip_->empty() ||
1560                    flags_.test(BIT_TOOLTIP_DEFERRED))) {
1561         if (!app) app = WApplication::instance();
1562         if ( (lookImpl_->toolTipTextFormat_ != TextFormat::Plain
1563               || flags_.test(BIT_TOOLTIP_DEFERRED))
1564             && app->environment().ajax()) {
1565           LOAD_JAVASCRIPT(app, "js/ToolTip.js", "toolTip", wtjs10);
1566 
1567           WString tooltipText(lookImpl_->toolTip_->toUTF8()); // UTF8 Guarantees copy for JWt
1568           if (lookImpl_->toolTipTextFormat_ == TextFormat::Plain) {
1569             tooltipText = escapeText(*lookImpl_->toolTip_);
1570           } else if (lookImpl_->toolTipTextFormat_ == TextFormat::XHTML) {
1571 	    bool res = removeScript(tooltipText);
1572 	    if (!res) {
1573 	      tooltipText = escapeText(*lookImpl_->toolTip_);
1574 	    }
1575 	  }
1576 
1577           std::string deferred = flags_.test(BIT_TOOLTIP_DEFERRED) ?
1578                 "true" : "false";
1579           element.callJavaScript(WT_CLASS ".toolTip(" +
1580                                  app->javaScriptClass() + ","
1581                                  + jsStringLiteral(id()) + ","
1582                                  + tooltipText.jsStringLiteral()
1583                                  + ", " + deferred
1584                                  + ", " +
1585                                  jsStringLiteral(app->theme()->
1586                                                  utilityCssClass(ToolTipInner))
1587                                  + ", " +
1588                                  jsStringLiteral(app->theme()->
1589                                                  utilityCssClass(ToolTipOuter))
1590                                  + ");");
1591 
1592           if (flags_.test(BIT_TOOLTIP_DEFERRED) &&
1593               !lookImpl_->loadToolTip_.isConnected())
1594             lookImpl_->loadToolTip_.connect(this, &WWebWidget::loadToolTip);
1595 
1596           element.removeAttribute("title");
1597         } else
1598           element.setAttribute("title", lookImpl_->toolTip_->toUTF8());
1599       }
1600 
1601       flags_.reset(BIT_TOOLTIP_CHANGED);
1602     }
1603 
1604     if (lookImpl_->decorationStyle_)
1605       lookImpl_->decorationStyle_->updateDomElement(element, all);
1606 
1607     if (all || flags_.test(BIT_STYLECLASS_CHANGED))
1608       if (!all || !lookImpl_->styleClass_.empty())
1609 	element.addPropertyWord(Property::Class, lookImpl_->styleClass_.toUTF8());
1610 
1611     flags_.reset(BIT_STYLECLASS_CHANGED);
1612   }
1613 
1614   if (!all && transientImpl_) {
1615     for (unsigned i = 0; i < transientImpl_->addedStyleClasses_.size(); ++i)
1616       element.callJavaScript("$('#" + id() + "').addClass('"
1617 			     + transientImpl_->addedStyleClasses_[i].toUTF8()
1618 			     +"');");
1619 
1620     for (unsigned i = 0; i < transientImpl_->removedStyleClasses_.size(); ++i)
1621       element.callJavaScript("$('#" + id() + "').removeClass('"
1622 			     + transientImpl_->removedStyleClasses_[i].toUTF8()
1623 			     +"');");
1624 
1625     if (!transientImpl_->childRemoveChanges_.empty()) {
1626       if ((int)children().size() != transientImpl_->addedChildren_
1627 	  || transientImpl_->specialChildRemove_) {
1628 	for (unsigned i = 0; i < transientImpl_->childRemoveChanges_.size();
1629 	     ++i) {
1630 	  const std::string& js = transientImpl_->childRemoveChanges_[i];
1631 	  if (js[0] == '_')
1632 	    element.callJavaScript(WT_CLASS ".remove('" + js.substr(1) + "');",
1633 				   true);
1634 	  else
1635 	    element.callJavaScript(js, true);
1636 	}
1637       } else
1638 	element.removeAllChildren();
1639 
1640       transientImpl_->addedChildren_ = 0;
1641       transientImpl_->childRemoveChanges_.clear();
1642       transientImpl_->specialChildRemove_ = false;
1643     }
1644   }
1645 
1646   if (all || flags_.test(BIT_SELECTABLE_CHANGED)) {
1647     if (flags_.test(BIT_SET_UNSELECTABLE)) {
1648       element.addPropertyWord(Property::Class, "unselectable");
1649       element.setAttribute("unselectable", "on");
1650       element.setAttribute("onselectstart", "return false;");
1651     } else if (flags_.test(BIT_SET_SELECTABLE)) {
1652       element.addPropertyWord(Property::Class, "selectable");
1653       element.setAttribute("unselectable", "off");
1654       element.setAttribute("onselectstart",
1655 			   "event.cancelBubble=true; return true;");
1656     }
1657 
1658     flags_.reset(BIT_SELECTABLE_CHANGED);
1659   }
1660 
1661   if (otherImpl_) {
1662     if (otherImpl_->attributes_) {
1663       if (all) {
1664 	for (std::map<std::string, WT_USTRING>::const_iterator i
1665 	       = otherImpl_->attributes_->begin();
1666 	     i != otherImpl_->attributes_->end(); ++i)
1667 	  if (i->first == "style")
1668 	    element.setProperty(Property::Style, i->second.toUTF8());
1669 	  else
1670 	    element.setAttribute(i->first, i->second.toUTF8());
1671       } else if (transientImpl_) {
1672 	for (unsigned i = 0; i < transientImpl_->attributesSet_.size(); ++i) {
1673 	  std::string attr = transientImpl_->attributesSet_[i];
1674 	  if (attr == "style")
1675 	    element.setProperty(Property::Style,
1676 				(*otherImpl_->attributes_)[attr].toUTF8());
1677 	  else
1678 	    element.setAttribute(attr,
1679 				 (*otherImpl_->attributes_)[attr].toUTF8());
1680 	}
1681       }
1682     }
1683 
1684     if (all && otherImpl_->jsMembers_) {
1685       for (unsigned i = 0; i < otherImpl_->jsMembers_->size(); i++) {
1686 	OtherImpl::Member member = (*otherImpl_->jsMembers_)[i];
1687 
1688 	bool notHere = false;
1689 	if (otherImpl_->jsStatements_) {
1690 	  for (unsigned j = 0; j < otherImpl_->jsStatements_->size(); ++j) {
1691 	    const OtherImpl::JavaScriptStatement& jss
1692 	      = (*otherImpl_->jsStatements_)[j];
1693 
1694 	    if (jss.type == JavaScriptStatementType::SetMember &&
1695 		jss.data == member.name) {
1696 	      notHere = true;
1697 	      break;
1698 	    }
1699 	  }
1700 	}
1701 
1702 	if (notHere)
1703 	  continue;
1704 
1705 	declareJavaScriptMember(element, member.name, member.value);
1706       }
1707     }
1708 
1709     if (otherImpl_->jsStatements_) {
1710       for (unsigned i = 0; i < otherImpl_->jsStatements_->size(); ++i) {
1711 	const OtherImpl::JavaScriptStatement& jss
1712 	  = (*otherImpl_->jsStatements_)[i];
1713 
1714 	switch (jss.type) {
1715 	case JavaScriptStatementType::SetMember:
1716 	  declareJavaScriptMember(element, jss.data,
1717 				  javaScriptMember(jss.data));
1718 	  break;
1719 	case JavaScriptStatementType::CallMethod:
1720 	  element.callMethod(jss.data);
1721 	  break;
1722 	case JavaScriptStatementType::Statement:
1723 	  element.callJavaScript(jss.data);
1724 	  break;
1725 	}
1726       }
1727 
1728       otherImpl_->jsStatements_.reset();
1729     }
1730   }
1731 
1732   if (flags_.test(BIT_HIDE_WITH_VISIBILITY)) {
1733     if (flags_.test(BIT_HIDDEN_CHANGED)
1734 	|| (all && flags_.test(BIT_HIDDEN))) {
1735       if (flags_.test(BIT_HIDDEN)) {
1736 	element.callJavaScript("$('#" + id() + "').addClass('Wt-hidden');");
1737 	element.setProperty(Property::StyleVisibility, "hidden");
1738 	if (flags_.test(BIT_HIDE_WITH_OFFSETS)) {
1739 	  element.setProperty(Property::StylePosition, "absolute");
1740 	  element.setProperty(Property::StyleTop, "-10000px");
1741 	  element.setProperty(Property::StyleLeft, "-10000px");
1742 	}
1743       } else {
1744 	if (flags_.test(BIT_HIDE_WITH_OFFSETS)) {
1745 	  if (layoutImpl_) {
1746 	    switch (layoutImpl_->positionScheme_) {
1747 	    case PositionScheme::Static:
1748 	      element.setProperty(Property::StylePosition, "static"); break;
1749 	    case PositionScheme::Relative:
1750 	      element.setProperty(Property::StylePosition, "relative"); break;
1751 	    case PositionScheme::Absolute:
1752 	      element.setProperty(Property::StylePosition, "absolute"); break;
1753 	    case PositionScheme::Fixed:
1754 	      element.setProperty(Property::StylePosition, "fixed"); break;
1755 	    }
1756 
1757 	    if (!layoutImpl_->offsets_[0].isAuto())
1758 	      element.setProperty(Property::StyleTop,
1759 				  layoutImpl_->offsets_[0].cssText());
1760 	    else
1761 	      element.setProperty(Property::StyleTop, "");
1762 
1763 	    if (!layoutImpl_->offsets_[3].isAuto())
1764 	      element.setProperty(Property::StyleLeft,
1765 				  layoutImpl_->offsets_[3].cssText());
1766 	    else
1767 	      element.setProperty(Property::StyleTop, "");
1768 	  } else {
1769 	    element.setProperty(Property::StylePosition, "static");
1770 	    element.setProperty(Property::StyleTop, "");
1771 	    element.setProperty(Property::StyleLeft, "");
1772 	  }
1773 	}
1774 	element.callJavaScript("$('#" + id() + "').removeClass('Wt-hidden');");
1775 	element.setProperty(Property::StyleVisibility, "visible");
1776       }
1777     }
1778   }
1779 
1780   if ((!all && flags_.test(BIT_HIDDEN_CHANGED))
1781       || (all && !flags_.test(BIT_HIDDEN))) {
1782     if (transientImpl_ && !transientImpl_->animation_.empty()) {
1783       const char *THIS_JS = "js/WWebWidget.js";
1784 
1785       if (!app) app = WApplication::instance();
1786 
1787       LOAD_JAVASCRIPT(app, THIS_JS, "animateDisplay", wtjs1);
1788       LOAD_JAVASCRIPT(app, THIS_JS, "animateVisible", wtjs2);
1789 
1790       if (!flags_.test(BIT_HIDE_WITH_VISIBILITY)) {
1791 	/*
1792 	 * Not using visibility: delay changing the display property
1793 	 */
1794 	WStringStream ss;
1795 	ss << WT_CLASS << ".animateDisplay("
1796 	   << app->javaScriptClass()
1797 	   << ",'" << id()
1798 	   << "'," << transientImpl_->animation_.effects().value()
1799 	   << "," << (int)transientImpl_->animation_.timingFunction()
1800 	   << "," << transientImpl_->animation_.duration()
1801 	   << ",'" << element.getProperty(Property::StyleDisplay)
1802 	   << "');";
1803 	element.callJavaScript(ss.str());
1804 
1805 	if (all)
1806 	  element.setProperty(Property::StyleDisplay, "none");
1807 	else
1808 	  element.removeProperty(Property::StyleDisplay);
1809       } else {
1810 	/*
1811 	 * Using visibility: remember how to show/hide the widget
1812 	 */
1813 	WStringStream ss;
1814 	ss << WT_CLASS << ".animateVisible('" << id()
1815 	   << "'," << transientImpl_->animation_.effects().value()
1816 	   << "," << (int)transientImpl_->animation_.timingFunction()
1817 	   << "," << transientImpl_->animation_.duration()
1818 	   << ",'" << element.getProperty(Property::StyleVisibility)
1819 	   << "','" << element.getProperty(Property::StylePosition)
1820 	   << "','" << element.getProperty(Property::StyleTop)
1821 	   << "','" << element.getProperty(Property::StyleLeft)
1822 	   << "');";
1823 	element.callJavaScript(ss.str());
1824 
1825 	if (all) {
1826 	  element.setProperty(Property::StyleVisibility, "hidden");
1827 	  element.setProperty(Property::StylePosition, "absolute");
1828 	  element.setProperty(Property::StyleTop, "-10000px");
1829 	  element.setProperty(Property::StyleLeft, "-10000px");
1830 	} else {
1831 	  element.removeProperty(Property::StyleVisibility);
1832 	  element.removeProperty(Property::StylePosition);
1833 	  element.removeProperty(Property::StyleTop);
1834 	  element.removeProperty(Property::StyleLeft);
1835 	}
1836       }
1837     }
1838   }
1839 
1840   flags_.reset(BIT_HIDDEN_CHANGED);
1841 
1842   if (flags_.test(BIT_GOT_FOCUS)) {
1843     if (!app) app = WApplication::instance();
1844     const WEnvironment& env = app->environment();
1845 
1846     element.callJavaScript("setTimeout(function() {"
1847 			   """var o = " + jsRef() + ";"
1848 			   """if (o) {"
1849 			   ""   "if (!$(o).hasClass('" +
1850 			         app->theme()->disabledClass() + "')) {"
1851 			   ""      "try { "
1852 			   ""          "o.focus();"
1853 			   ""      "} catch (e) {}"
1854 			   ""   "}"
1855 			   """}"
1856 			   "}, " + (env.agentIsIElt(9) ? "500" : "10") + ");");
1857 
1858     flags_.reset(BIT_GOT_FOCUS);
1859   }
1860 
1861   if (flags_.test(BIT_TABINDEX_CHANGED) || all) {
1862     if (otherImpl_ && otherImpl_->tabIndex_ != std::numeric_limits<int>::min())
1863       element.setProperty(Property::TabIndex,
1864 			  std::to_string(otherImpl_->tabIndex_));
1865     else if (!all)
1866       element.removeAttribute("tabindex");
1867 
1868     flags_.reset(BIT_TABINDEX_CHANGED);
1869   }
1870 
1871   if (all || flags_.test(BIT_SCROLL_VISIBILITY_CHANGED)) {
1872     const char *SCROLL_JS = "js/ScrollVisibility.js";
1873 
1874     if (!app) app = WApplication::instance();
1875 
1876     if (!app->javaScriptLoaded(SCROLL_JS) && scrollVisibilityEnabled()) {
1877       LOAD_JAVASCRIPT(app, SCROLL_JS, "ScrollVisibility", wtjs3);
1878       WStringStream ss;
1879       ss << "if (!" WT_CLASS ".scrollVisibility) {"
1880 	    WT_CLASS ".scrollVisibility = new ";
1881       ss << WT_CLASS ".ScrollVisibility(" << app->javaScriptClass() + "); }";
1882       element.callJavaScript(ss.str());
1883     }
1884 
1885     if (scrollVisibilityEnabled()) {
1886       WStringStream ss;
1887       ss << WT_CLASS ".scrollVisibility.add({";
1888       ss << "el:" << jsRef() << ',';
1889       ss << "margin:" << scrollVisibilityMargin() << ',';
1890       ss << "visible:" << isScrollVisible();
1891       ss << "});";
1892       element.callJavaScript(ss.str());
1893       flags_.set(BIT_SCROLL_VISIBILITY_LOADED);
1894     } else if (flags_.test(BIT_SCROLL_VISIBILITY_LOADED)) {
1895       element.callJavaScript(WT_CLASS ".scrollVisibility.remove("
1896 			     + jsStringLiteral(id()) + ");");
1897       flags_.reset(BIT_SCROLL_VISIBILITY_LOADED);
1898     }
1899     flags_.reset(BIT_SCROLL_VISIBILITY_CHANGED);
1900   }
1901 
1902   if (all || flags_.test(BIT_OBJECT_NAME_CHANGED)) {
1903     if (!objectName().empty()) {
1904       element.setAttribute("data-object-name", objectName());
1905     } else if (!all) {
1906       element.removeAttribute("data-object-name");
1907     }
1908     flags_.reset(BIT_OBJECT_NAME_CHANGED);
1909   }
1910 
1911   renderOk();
1912 
1913   transientImpl_.reset();
1914 }
1915 
declareJavaScriptMember(DomElement & element,const std::string & name,const std::string & value)1916 void WWebWidget::declareJavaScriptMember(DomElement& element,
1917 					 const std::string& name,
1918 					 const std::string& value)
1919 {
1920   if (name[0] != ' ') {
1921     if (name == WT_RESIZE_JS && otherImpl_->resized_) {
1922       WStringStream combined;
1923       if (value.length() > 1) {
1924 	combined << name << "=function(s,w,h) {"
1925 		 << WApplication::instance()->javaScriptClass()
1926 		 << "._p_.propagateSize(s,w,h);"
1927 		 << "(" << value << ")(s,w,h);"
1928 		 << "}";
1929       } else
1930 	combined << name << "="
1931 		 << WApplication::instance()->javaScriptClass()
1932 		 << "._p_.propagateSize";
1933 
1934       element.callMethod(combined.str());
1935     } else {
1936       if (value.length() > 0)
1937 	element.callMethod(name + "=" + value);
1938       else
1939 	element.callMethod(name + "=null");
1940     }
1941   } else
1942     element.callJavaScript(value);
1943 }
1944 
isStubbed()1945 bool WWebWidget::isStubbed() const
1946 {
1947   if (flags_.test(BIT_STUBBED))
1948     return true;
1949   else {
1950     WWidget *p = parent();
1951     return p ? p->isStubbed() : false;
1952   }
1953 }
1954 
needsToBeRendered()1955 bool WWebWidget::needsToBeRendered() const
1956 {
1957   /*
1958    * Returns whether this widget should be rendered. The only alternative
1959    * is to be stubbed as an invisible widget.
1960    */
1961   return flags_.test(BIT_DONOT_STUB)
1962     || !flags_.test(BIT_HIDDEN)
1963     || !WApplication::instance()->session()->renderer().visibleOnly();
1964 }
1965 
setCanReceiveFocus(bool enabled)1966 void WWebWidget::setCanReceiveFocus(bool enabled)
1967 {
1968   setTabIndex(enabled ? 0 : std::numeric_limits<int>::min());
1969 }
1970 
canReceiveFocus()1971 bool WWebWidget::canReceiveFocus() const
1972 {
1973   if (otherImpl_)
1974     return otherImpl_->tabIndex_ != std::numeric_limits<int>::min();
1975   else
1976     return false;
1977 }
1978 
setTabIndex(int index)1979 void WWebWidget::setTabIndex(int index)
1980 {
1981   if (!otherImpl_)
1982     otherImpl_.reset(new OtherImpl(this));
1983 
1984   otherImpl_->tabIndex_ = index;
1985 
1986   flags_.set(BIT_TABINDEX_CHANGED);
1987   repaint();
1988 }
1989 
tabIndex()1990 int WWebWidget::tabIndex() const
1991 {
1992   if (!otherImpl_)
1993     return canReceiveFocus() ? 0 : std::numeric_limits<int>::min();
1994   else
1995     return otherImpl_->tabIndex_;
1996 }
1997 
setFirstFocus()1998 bool WWebWidget::setFirstFocus()
1999 {
2000   if (isVisible() && isEnabled()) {
2001     if (canReceiveFocus()) {
2002       setFocus(true);
2003       return true;
2004     }
2005 
2006     bool result[1];
2007     result[0] = false;
2008     iterateChildren([&](Wt::WWidget *w){
2009       if (!result[0])
2010         result[0] = w->setFirstFocus();
2011     });
2012     if (result[0])
2013       return true;
2014 
2015     return false;
2016   } else
2017     return false;
2018 }
2019 
blurred()2020 EventSignal<>& WWebWidget::blurred()
2021 {
2022   return *voidEventSignal(BLUR_SIGNAL, true);
2023 }
2024 
focussed()2025 EventSignal<>& WWebWidget::focussed()
2026 {
2027   return *voidEventSignal(FOCUS_SIGNAL, true);
2028 }
2029 
setFocus(bool focus)2030 void WWebWidget::setFocus(bool focus)
2031 {
2032   flags_.set(BIT_GOT_FOCUS, focus);
2033   repaint();
2034 
2035   WApplication *app = WApplication::instance();
2036   if (focus)
2037     app->setFocus(id(), -1, -1);
2038   else if (app->focus() == id())
2039     app->setFocus(std::string(), -1, -1);
2040 }
2041 
undoSetFocus()2042 void WWebWidget::undoSetFocus()
2043 { }
2044 
hasFocus()2045 bool WWebWidget::hasFocus() const
2046 {
2047   return WApplication::instance()->focus() == id();
2048 }
2049 
getSFormObjects(FormObjectsMap & result)2050 void WWebWidget::getSFormObjects(FormObjectsMap& result)
2051 {
2052   if (!flags_.test(BIT_STUBBED) && !flags_.test(BIT_HIDDEN)
2053       && flags_.test(BIT_RENDERED))
2054     getFormObjects(result);
2055 }
2056 
getFormObjects(FormObjectsMap & formObjects)2057 void WWebWidget::getFormObjects(FormObjectsMap& formObjects)
2058 {
2059   if (flags_.test(BIT_FORM_OBJECT))
2060     formObjects[id()] = this;
2061 
2062   iterateChildren
2063     ([&](WWidget *c) {
2064       c->webWidget()->getSFormObjects(formObjects);
2065     });
2066 }
2067 
getDomChanges(std::vector<DomElement * > & result,WApplication * app)2068 void WWebWidget::getDomChanges(std::vector<DomElement *>& result,
2069 			       WApplication *app)
2070 {
2071   DomElement *e = DomElement::getForUpdate(this, domElementType());
2072   updateDom(*e, false);
2073   result.push_back(e);
2074 }
2075 
getSDomChanges(std::vector<DomElement * > & result,WApplication * app)2076 void WWebWidget::getSDomChanges(std::vector<DomElement *>& result,
2077 				WApplication *app)
2078 {
2079   if (flags_.test(BIT_STUBBED)) {
2080     /*
2081      * If we are prelearning, we still want to catch changes to *this*
2082      * widget, since it could be related to the prelearning. Note that
2083      * this assumes that getDomChanges() does not attempt to do things
2084      * like recreating the widget...
2085      *
2086      * ... which is what happens in WContainerWidget, we make the exception
2087      * there...
2088      */
2089     if (app->session()->renderer().preLearning()) {
2090       getDomChanges(result, app);
2091       scheduleRerender(true);
2092     } else {
2093       if (!app->session()->renderer().visibleOnly()) {
2094 	flags_.reset(BIT_STUBBED);
2095 
2096 	DomElement *stub = DomElement::getForUpdate(this, DomElementType::SPAN);
2097 	WWidget *self = selfWidget();
2098 	setRendered(true);
2099 	self->render(RenderFlag::Full);
2100 	DomElement *realElement = createDomElement(app);
2101 	app->theme()->apply(self, *realElement, MainElement);
2102 	stub->unstubWith(realElement, !flags_.test(BIT_HIDE_WITH_OFFSETS));
2103 	result.push_back(stub);
2104       }
2105     }
2106   } else {
2107     render(RenderFlag::Update);
2108 
2109     getDomChanges(result, app);
2110   }
2111 }
2112 
doneRerender()2113 void WWebWidget::doneRerender()
2114 {
2115   iterateChildren
2116     ([](WWidget *c) {
2117       c->webWidget()->doneRerender();
2118     });
2119 }
2120 
propagateRenderOk(bool deep)2121 void WWebWidget::propagateRenderOk(bool deep)
2122 {
2123 #ifndef WT_TARGET_JAVA
2124   flags_ &= ~AllChangeFlags;
2125 #else
2126   flags_.reset(BIT_FLEX_BOX_CHANGED);
2127   flags_.reset(BIT_HIDDEN_CHANGED);
2128   flags_.reset(BIT_GEOMETRY_CHANGED);
2129   flags_.reset(BIT_FLOAT_SIDE_CHANGED);
2130   flags_.reset(BIT_TOOLTIP_CHANGED);
2131   flags_.reset(BIT_MARGINS_CHANGED);
2132   flags_.reset(BIT_STYLECLASS_CHANGED);
2133   flags_.reset(BIT_SELECTABLE_CHANGED);
2134   flags_.reset(BIT_WIDTH_CHANGED);
2135   flags_.reset(BIT_HEIGHT_CHANGED);
2136   flags_.reset(BIT_DISABLED_CHANGED);
2137   flags_.reset(BIT_ZINDEX_CHANGED);
2138   flags_.reset(BIT_TABINDEX_CHANGED);
2139   flags_.reset(BIT_SCROLL_VISIBILITY_CHANGED);
2140   flags_.reset(BIT_OBJECT_NAME_CHANGED);
2141 #endif
2142 
2143   renderOk();
2144 
2145   if (deep)
2146     iterateChildren
2147       ([](WWidget *c) {
2148 	c->webWidget()->propagateRenderOk();
2149       });
2150 
2151   transientImpl_.reset();
2152 }
2153 
setRendered(bool rendered)2154 void WWebWidget::setRendered(bool rendered)
2155 {
2156   if (rendered)
2157     flags_.set(BIT_RENDERED);
2158   else {
2159     flags_.reset(BIT_RENDERED);
2160 
2161     renderOk();
2162 
2163     iterateChildren
2164       ([](WWidget *c) {
2165 	c->webWidget()->setRendered(false);
2166       });
2167   }
2168 }
2169 
setLoadLaterWhenInvisible(bool how)2170 void WWebWidget::setLoadLaterWhenInvisible(bool how)
2171 {
2172   flags_.set(BIT_DONOT_STUB, !how);
2173 }
2174 
setHtmlTagName(const std::string & tag)2175 void WWebWidget::setHtmlTagName(const std::string& tag)
2176 {
2177   if (!otherImpl_)
2178     otherImpl_.reset(new OtherImpl(this));
2179 
2180   if (!otherImpl_->elementTagName_)
2181     otherImpl_->elementTagName_.reset(new std::string());
2182 
2183   *otherImpl_->elementTagName_ = tag;
2184 }
2185 
htmlTagName()2186 std::string WWebWidget::htmlTagName() const
2187 {
2188   if (otherImpl_ && otherImpl_->elementTagName_)
2189     return *otherImpl_->elementTagName_;
2190   else {
2191     DomElementType type = domElementType();
2192     return DomElement::tagName(type);
2193   }
2194 }
2195 
setId(DomElement * element,WApplication * app)2196 void WWebWidget::setId(DomElement *element, WApplication *app)
2197 {
2198   if (!app->environment().agentIsSpiderBot()
2199       || id_) {
2200     if (!flags_.test(BIT_FORM_OBJECT))
2201       element->setId(id());
2202     else
2203       element->setName(id());
2204   }
2205 }
2206 
find(const std::string & name)2207 WWidget *WWebWidget::find(const std::string& name)
2208 {
2209   if (objectName() == name)
2210     return this;
2211   else {
2212     WWidget *result[1];
2213     result[0] = nullptr;
2214     iterateChildren
2215       ([&](WWidget *c) {
2216 	if (!result[0])
2217 	  result[0] = c->find(name);
2218       });
2219 
2220     return result[0];
2221   }
2222 }
2223 
findById(const std::string & id)2224 WWidget *WWebWidget::findById(const std::string& id)
2225 {
2226   if (this->id() == id)
2227     return this;
2228   else {
2229     WWidget *result[1];
2230     result[0] = nullptr;
2231     iterateChildren
2232       ([&](WWidget *c) {
2233 	if (!result[0])
2234 	  result[0] = c->findById(id);
2235       });
2236     if (result[0])
2237       return result[0];
2238   }
2239 
2240   return nullptr;
2241 }
2242 
createDomElement(WApplication * app)2243 DomElement *WWebWidget::createDomElement(WApplication *app)
2244 {
2245   setRendered(true);
2246   DomElement *result;
2247 
2248   if (otherImpl_ && otherImpl_->elementTagName_) {
2249     result = DomElement::createNew(DomElementType::OTHER);
2250     result->setDomElementTagName(*otherImpl_->elementTagName_);
2251   } else
2252     result = DomElement::createNew(domElementType());
2253 
2254   setId(result, app);
2255   updateDom(*result, true);
2256 
2257   return result;
2258 }
2259 
domCanBeSaved()2260 bool WWebWidget::domCanBeSaved() const
2261 {
2262   bool canBeSaved[1];
2263   canBeSaved[0] = true;
2264   iterateChildren([&](WWidget *child){
2265     canBeSaved[0] = canBeSaved[0] && child->webWidget()->domCanBeSaved();
2266   });
2267   return canBeSaved[0];
2268 }
2269 
isRendered()2270 bool WWebWidget::isRendered() const
2271 {
2272   return flags_.test(WWebWidget::BIT_RENDERED);
2273 }
2274 
createStubElement(WApplication * app)2275 DomElement *WWebWidget::createStubElement(WApplication *app)
2276 {
2277   /*
2278    * Make sure the object itself is clean, so that stateless slot
2279    * learning is not confused.
2280    */
2281   propagateRenderOk();
2282 
2283   flags_.set(BIT_STUBBED);
2284 
2285   DomElement *stub = DomElement::createNew(DomElementType::SPAN);
2286   if (!flags_.test(BIT_HIDE_WITH_OFFSETS)) {
2287     stub->setProperty(Property::StyleDisplay, "none");
2288   } else {
2289     stub->setProperty(Property::StylePosition, "absolute");
2290     stub->setProperty(Property::StyleLeft, "-10000px");
2291     stub->setProperty(Property::StyleTop, "-10000px");
2292     stub->setProperty(Property::StyleVisibility, "hidden");
2293   }
2294   if (app->environment().javaScript())
2295     stub->setProperty(Property::InnerHTML, "...");
2296 
2297   if (!app->environment().agentIsSpiderBot()
2298       || id_)
2299     stub->setId(id());
2300 
2301   return stub;
2302 }
2303 
createActualElement(WWidget * self,WApplication * app)2304 DomElement *WWebWidget::createActualElement(WWidget *self, WApplication *app)
2305 {
2306   flags_.reset(BIT_STUBBED);
2307 
2308   DomElement *result = createDomElement(app);
2309 
2310   app->theme()->apply(self, *result, MainElement);
2311 
2312   /* Make sure addStyleClass() does not mess up later */
2313   std::string styleClass = result->getProperty(Property::Class);
2314   if (!styleClass.empty()) {
2315     if (!lookImpl_)
2316       lookImpl_.reset(new LookImpl(this));
2317 
2318     lookImpl_->styleClass_ = styleClass;
2319   }
2320 
2321   return result;
2322 }
2323 
refresh()2324 void WWebWidget::refresh()
2325 {
2326   if (lookImpl_ && lookImpl_->toolTip_)
2327     if (lookImpl_->toolTip_->refresh()) {
2328       flags_.set(BIT_TOOLTIP_CHANGED);
2329       repaint();
2330     }
2331 
2332   iterateChildren
2333     ([](WWidget *c) {
2334       c->refresh();
2335     });
2336 
2337   WWidget::refresh();
2338 }
2339 
enableAjax()2340 void WWebWidget::enableAjax()
2341 {
2342   /*
2343    * What needs to be done ? We want to get to the same state as the normal
2344    * AJAX bootstrap: thus still leaving stubs as is.
2345    */
2346   if (!isStubbed()) {
2347     for (EventSignalList::iterator i = eventSignals().begin();
2348 	 i != eventSignals().end(); ++i) {
2349       EventSignalBase& s = **i;
2350       if (s.name() == WInteractWidget::M_CLICK_SIGNAL)
2351 	repaint(RepaintFlag::ToAjax);
2352 
2353       s.ownerRepaint();
2354     }
2355   }
2356 
2357   if (flags_.test(BIT_TOOLTIP_DEFERRED) ||
2358       (lookImpl_ && lookImpl_->toolTipTextFormat_ != TextFormat::Plain)) {
2359     flags_.set(BIT_TOOLTIP_CHANGED);
2360     repaint();
2361   }
2362 
2363   iterateChildren
2364     ([](WWidget *c) {
2365       c->enableAjax();
2366     });
2367 }
2368 
escapeText(const WString & text,bool newlinestoo)2369 WString WWebWidget::escapeText(const WString& text, bool newlinestoo)
2370 {
2371   std::string result = text.toUTF8();
2372   result = escapeText(result, newlinestoo);
2373   return WString::fromUTF8(result);
2374 }
2375 
escapeText(std::string & text,bool newlinestoo)2376 std::string& WWebWidget::escapeText(std::string& text, bool newlinestoo)
2377 {
2378   EscapeOStream sout;
2379   if (newlinestoo)
2380     sout.pushEscape(EscapeOStream::PlainTextNewLines);
2381   else
2382     sout.pushEscape(EscapeOStream::Plain);
2383 
2384   Wt::Utils::sanitizeUnicode(sout, text);
2385 
2386 #ifndef WT_TARGET_JAVA
2387   text = sout.str();
2388   return text;
2389 #else
2390   return sout.str();
2391 #endif // WT_TARGET_JAVA
2392 }
2393 
unescapeText(std::string & text)2394 std::string& WWebWidget::unescapeText(std::string &text)
2395 {
2396 #ifndef WT_TARGET_JAVA
2397   char *inP = &text[0];
2398   char *const inEndP = &text[text.size()];
2399   char *outP = &text[0];
2400   char *ampP = nullptr;
2401   do {
2402     assert(inP >= outP);
2403     ampP = std::find(inP, inEndP, '&');
2404     if (inP == outP) {
2405       inP = ampP;
2406       outP = ampP;
2407     } else {
2408       while (inP != ampP) {
2409         *(outP++) = *(inP++);
2410       }
2411     }
2412     assert(inP >= outP);
2413     if (inP != inEndP) {
2414       char *semiP = std::find(inP, inEndP, ';');
2415       if (semiP == inEndP)
2416         *(outP++) = *(inP++);
2417       else {
2418         if (*(ampP + 1) == '#') {
2419           unsigned long codept = 0;
2420           bool valid = false;
2421           if (*(ampP + 2) == 'x') {
2422             char *end = nullptr;
2423             codept = std::strtoul(ampP + 3, &end, 16);
2424             valid = end == semiP;
2425           } else {
2426             char *end = nullptr;
2427             codept = std::strtoul(ampP + 2, &end, 10);
2428             valid = end == semiP;
2429           }
2430           if (valid) {
2431             Wt::rapidxml::xml_document<>::insert_coded_character<0>(outP, codept);
2432             inP = semiP + 1;
2433           } else {
2434             *(outP++) = *(inP++);
2435           }
2436         } else {
2437           if (!rapidxml::translate_xhtml_entity(inP, outP)) {
2438             *(outP++) = *(inP++);
2439           }
2440         }
2441       }
2442     }
2443   } while (inP < inEndP);
2444   assert(inP >= outP);
2445   *outP = '\0';
2446   std::size_t s = (std::size_t)(outP - (&text[0]));
2447   assert(s <= text.size());
2448   text.resize(s);
2449   return text;
2450 #else // WT_TARGET_JAVA
2451   return doUnescapeText(text);
2452 #endif // WT_TARGET_JAVA
2453 }
2454 
jsStringLiteral(const std::string & value,char delimiter)2455 std::string WWebWidget::jsStringLiteral(const std::string& value,
2456 					char delimiter)
2457 {
2458   WStringStream result;
2459   DomElement::jsStringLiteral(result, value, delimiter);
2460   return result.str();
2461 }
2462 
jsStringLiteral(const WString & value,char delimiter)2463 std::string WWebWidget::jsStringLiteral(const WString& value,
2464 					char delimiter)
2465 {
2466   return value.jsStringLiteral(delimiter);
2467 }
2468 
load()2469 void WWebWidget::load()
2470 {
2471   flags_.set(BIT_LOADED);
2472 
2473   iterateChildren
2474     ([&](WWidget *c) {
2475       doLoad(c);
2476     });
2477 
2478   if (flags_.test(BIT_HIDE_WITH_OFFSETS))
2479     parent()->setHideWithOffsets(true);
2480 }
2481 
doLoad(WWidget * w)2482 void WWebWidget::doLoad(WWidget *w)
2483 {
2484   w->load();
2485   if (!w->loaded())
2486     LOG_ERROR("improper load() implementation: base implementation not called");
2487 }
2488 
render(WFlags<RenderFlag> flags)2489 void WWebWidget::render(WFlags<RenderFlag> flags)
2490 {
2491   WWidget::render(flags);
2492 }
2493 
doJavaScript(const std::string & javascript)2494 void WWebWidget::doJavaScript(const std::string& javascript)
2495 {
2496   addJavaScriptStatement(JavaScriptStatementType::Statement, javascript);
2497   repaint();
2498 }
2499 
loaded()2500 bool WWebWidget::loaded() const
2501 {
2502   return flags_.test(BIT_LOADED);
2503 }
2504 
DropMimeType(const WT_USTRING & aHoverStyleClass)2505 WWebWidget::DropMimeType::DropMimeType(const WT_USTRING& aHoverStyleClass)
2506   : hoverStyleClass(aHoverStyleClass)
2507 { }
2508 
DropMimeType()2509 WWebWidget::DropMimeType::DropMimeType()
2510 { }
2511 
setAcceptDropsImpl(const std::string & mimeType,bool accept,const WT_USTRING & hoverStyleClass)2512 bool WWebWidget::setAcceptDropsImpl(const std::string& mimeType, bool accept,
2513 				    const WT_USTRING& hoverStyleClass)
2514 {
2515   bool result  = false; // whether the signal needs to be connected.
2516   bool changed = false;
2517 
2518   if (!otherImpl_)
2519     otherImpl_.reset(new OtherImpl(this));
2520   if (!otherImpl_->acceptedDropMimeTypes_)
2521     otherImpl_->acceptedDropMimeTypes_.reset(new OtherImpl::MimeTypesMap);
2522 
2523   OtherImpl::MimeTypesMap::iterator i
2524     = otherImpl_->acceptedDropMimeTypes_->find(mimeType);
2525 
2526   if (i == otherImpl_->acceptedDropMimeTypes_->end()) {
2527     if (accept) {
2528       result = otherImpl_->acceptedDropMimeTypes_->empty();
2529       (*otherImpl_->acceptedDropMimeTypes_)[mimeType]
2530 	= DropMimeType(hoverStyleClass);
2531       changed = true;
2532     }
2533   } else {
2534     if (!accept) {
2535 #ifndef WT_TARGET_JAVA
2536       otherImpl_->acceptedDropMimeTypes_->erase(i);
2537 #else
2538       otherImpl_->acceptedDropMimeTypes_->erase(mimeType);
2539 #endif // WT_TARGET_JAVA
2540       changed = true;
2541     }
2542   }
2543 
2544   if (changed) {
2545     std::string mimeTypes = "";
2546 
2547     for (OtherImpl::MimeTypesMap::const_iterator j
2548 	   = otherImpl_->acceptedDropMimeTypes_->begin();
2549 	 j != otherImpl_->acceptedDropMimeTypes_->end(); ++j) {
2550       mimeTypes
2551 	+= "{" + j->first + ":" + j->second.hoverStyleClass.toUTF8() + "}";
2552     }
2553     setAttributeValue("amts", mimeTypes);
2554   }
2555 
2556   if (result && !otherImpl_->dropSignal_)
2557     otherImpl_->dropSignal_
2558       .reset(new JSignal<std::string,std::string, WMouseEvent>(this, "_drop"));
2559 
2560   if (result && !otherImpl_->dropSignal2_)
2561     otherImpl_->dropSignal2_
2562       .reset(new JSignal<std::string,std::string, WTouchEvent>(this, "_drop2"));
2563 
2564   return result;
2565 }
2566 
keyEventSignal(const char * name,bool create)2567 EventSignal<WKeyEvent> *WWebWidget::keyEventSignal(const char *name,
2568 						   bool create)
2569 {
2570   EventSignalBase *b = getEventSignal(name);
2571   if (b)
2572     return static_cast<EventSignal<WKeyEvent> *>(b);
2573   else if (!create)
2574     return nullptr;
2575   else {
2576 #ifndef WT_TARGET_JAVA
2577     EventSignal<WKeyEvent> *result = new EventSignal<WKeyEvent>(name, this);
2578 #else
2579     EventSignal<WKeyEvent> *result
2580       = new EventSignal<WKeyEvent>(name, this, WKeyEvent::templateEvent);
2581 #endif // WT_TARGET_JAVA
2582 
2583     addEventSignal(*result);
2584     return result;
2585   }
2586 }
2587 
voidEventSignal(const char * name,bool create)2588 EventSignal<> *WWebWidget::voidEventSignal(const char *name, bool create)
2589 {
2590   EventSignalBase *b = getEventSignal(name);
2591   if (b)
2592     return static_cast<EventSignal<> *>(b);
2593   else if (!create)
2594     return nullptr;
2595   else {
2596     EventSignal<> *result = new EventSignal<>(name, this);
2597     addEventSignal(*result);
2598     return result;
2599   }
2600 }
2601 
mouseEventSignal(const char * name,bool create)2602 EventSignal<WMouseEvent> *WWebWidget::mouseEventSignal(const char *name,
2603 						       bool create)
2604 {
2605   EventSignalBase *b = getEventSignal(name);
2606   if (b)
2607     return static_cast<EventSignal<WMouseEvent> *>(b);
2608   else if (!create)
2609     return nullptr;
2610   else {
2611 #ifndef WT_TARGET_JAVA
2612     EventSignal<WMouseEvent> *result = new EventSignal<WMouseEvent>(name, this);
2613 #else
2614     EventSignal<WMouseEvent> *result
2615       = new EventSignal<WMouseEvent>(name, this, WMouseEvent::templateEvent);
2616 #endif // WT_TARGET_JAVA
2617     addEventSignal(*result);
2618     return result;
2619   }
2620 }
2621 
touchEventSignal(const char * name,bool create)2622 EventSignal<WTouchEvent> *WWebWidget::touchEventSignal(const char *name,
2623 						       bool create)
2624 {
2625   EventSignalBase *b = getEventSignal(name);
2626   if (b)
2627     return static_cast<EventSignal<WTouchEvent> *>(b);
2628   else if (!create)
2629     return nullptr;
2630   else {
2631 #ifndef WT_TARGET_JAVA
2632     EventSignal<WTouchEvent> *result = new EventSignal<WTouchEvent>(name, this);
2633 #else
2634     EventSignal<WTouchEvent> *result
2635       = new EventSignal<WTouchEvent>(name, this, WTouchEvent::templateEvent);
2636 #endif // WT_TARGET_JAVA
2637     addEventSignal(*result);
2638     return result;
2639   }
2640 }
2641 
gestureEventSignal(const char * name,bool create)2642 EventSignal<WGestureEvent> *WWebWidget::gestureEventSignal(const char *name,
2643 							   bool create)
2644 {
2645   EventSignalBase *b = getEventSignal(name);
2646   if (b)
2647     return static_cast<EventSignal<WGestureEvent> *>(b);
2648   else if (!create)
2649     return nullptr;
2650   else {
2651 #ifndef WT_TARGET_JAVA
2652     EventSignal<WGestureEvent> *result
2653       = new EventSignal<WGestureEvent>(name, this);
2654 #else
2655     EventSignal<WGestureEvent> *result
2656       = new EventSignal<WGestureEvent>(name, this,
2657 				       WGestureEvent::templateEvent);
2658 #endif // WT_TARGET_JAVA
2659     addEventSignal(*result);
2660     return result;
2661   }
2662 }
2663 
scrollEventSignal(const char * name,bool create)2664 EventSignal<WScrollEvent> *WWebWidget::scrollEventSignal(const char *name,
2665 							 bool create)
2666 {
2667   EventSignalBase *b = getEventSignal(name);
2668   if (b)
2669     return static_cast<EventSignal<WScrollEvent> *>(b);
2670   else if (!create)
2671     return nullptr;
2672   else {
2673 #ifndef WT_TARGET_JAVA
2674     EventSignal<WScrollEvent> *result
2675       = new EventSignal<WScrollEvent>(name, this);
2676 #else
2677     EventSignal<WScrollEvent> *result
2678       = new EventSignal<WScrollEvent>(name, this, WScrollEvent::templateEvent);
2679 #endif // WT_TARGET_JAVA
2680     addEventSignal(*result);
2681     return result;
2682   }
2683 }
2684 
updateSignalConnection(DomElement & element,EventSignalBase & signal,const char * name,bool all)2685 void WWebWidget::updateSignalConnection(DomElement& element,
2686 					EventSignalBase &signal,
2687 					const char *name,
2688 					bool all)
2689 {
2690   if (name[0] != 'M' && signal.needsUpdate(all)) {
2691     element.setEventSignal(name, signal);
2692     signal.updateOk();
2693   }
2694 }
2695 
canOptimizeUpdates()2696 bool WWebWidget::canOptimizeUpdates()
2697 {
2698   return !WApplication::instance()->session()->renderer().preLearning();
2699 }
2700 
resolveRelativeUrl(const std::string & url)2701 std::string WWebWidget::resolveRelativeUrl(const std::string& url)
2702 {
2703   return WApplication::instance()->resolveRelativeUrl(url);
2704 }
2705 
removeScript(WString & text)2706 bool WWebWidget::removeScript(WString& text)
2707 {
2708 #ifndef WT_NO_XSS_FILTER
2709   return XSSFilterRemoveScript(text);
2710 #else
2711   return true;
2712 #endif
2713 }
2714 
scrollVisibilityEnabled()2715 bool WWebWidget::scrollVisibilityEnabled() const
2716 {
2717   return flags_.test(BIT_SCROLL_VISIBILITY_ENABLED);
2718 }
2719 
setScrollVisibilityEnabled(bool enabled)2720 void WWebWidget::setScrollVisibilityEnabled(bool enabled)
2721 {
2722   if (enabled) {
2723     if (!otherImpl_)
2724       otherImpl_.reset(new OtherImpl(this));
2725     if (!otherImpl_->jsScrollVisibilityChanged_) {
2726       otherImpl_->jsScrollVisibilityChanged_.reset(new JSignal<bool>(this, "scrollVisibilityChanged"));
2727       otherImpl_->jsScrollVisibilityChanged_->connect(this, &WWebWidget::jsScrollVisibilityChanged);
2728     }
2729   }
2730 
2731   if (scrollVisibilityEnabled() != enabled) {
2732     flags_.set(BIT_SCROLL_VISIBILITY_ENABLED, enabled);
2733     flags_.set(BIT_SCROLL_VISIBILITY_CHANGED);
2734     repaint();
2735   }
2736 }
2737 
scrollVisibilityMargin()2738 int WWebWidget::scrollVisibilityMargin() const
2739 {
2740   if (!otherImpl_)
2741     return 0;
2742   else
2743     return otherImpl_->scrollVisibilityMargin_;
2744 }
2745 
setScrollVisibilityMargin(int margin)2746 void WWebWidget::setScrollVisibilityMargin(int margin)
2747 {
2748   if (scrollVisibilityMargin() != margin) {
2749     if (!otherImpl_)
2750       otherImpl_.reset(new OtherImpl(this));
2751     otherImpl_->scrollVisibilityMargin_ = margin;
2752     if (scrollVisibilityEnabled()) {
2753       flags_.set(BIT_SCROLL_VISIBILITY_CHANGED);
2754       repaint();
2755     }
2756   }
2757 }
2758 
scrollVisibilityChanged()2759 Signal<bool> &WWebWidget::scrollVisibilityChanged()
2760 {
2761   if (!otherImpl_)
2762     otherImpl_.reset(new OtherImpl(this));
2763 
2764   return otherImpl_->scrollVisibilityChanged_;
2765 }
2766 
isScrollVisible()2767 bool WWebWidget::isScrollVisible() const
2768 {
2769   return flags_.test(BIT_IS_SCROLL_VISIBLE);
2770 }
2771 
jsScrollVisibilityChanged(bool visible)2772 void WWebWidget::jsScrollVisibilityChanged(bool visible)
2773 {
2774   flags_.set(BIT_IS_SCROLL_VISIBLE, visible);
2775   if (otherImpl_)
2776     otherImpl_->scrollVisibilityChanged_.emit(visible);
2777 }
2778 
setThemeStyleEnabled(bool enabled)2779 void WWebWidget::setThemeStyleEnabled(bool enabled)
2780 {
2781   flags_.set(BIT_THEME_STYLE_DISABLED, !enabled);
2782 }
2783 
isThemeStyleEnabled()2784 bool WWebWidget::isThemeStyleEnabled() const
2785 {
2786   return !flags_.test(BIT_THEME_STYLE_DISABLED);
2787 }
2788 
setObjectName(const std::string & name)2789 void WWebWidget::setObjectName(const std::string& name)
2790 {
2791   if (objectName() != name) {
2792     WWidget::setObjectName(name);
2793     flags_.set(BIT_OBJECT_NAME_CHANGED);
2794     repaint();
2795   }
2796 }
2797 
baseZIndex()2798 int WWebWidget::baseZIndex() const
2799 {
2800   if (!layoutImpl_)
2801     return DEFAULT_BASE_Z_INDEX;
2802   else
2803     return layoutImpl_->baseZIndex_;
2804 }
2805 
setBaseZIndex(int zIndex)2806 void WWebWidget::setBaseZIndex(int zIndex)
2807 {
2808   if (!layoutImpl_)
2809     layoutImpl_.reset(new LayoutImpl());
2810 
2811   layoutImpl_->baseZIndex_ = zIndex;
2812 }
2813 
emitChildrenChanged()2814 void WWebWidget::emitChildrenChanged()
2815 {
2816   if (!flags_.test(BIT_BEING_DELETED) && otherImpl_) {
2817     otherImpl_->childrenChanged_.emit();
2818   }
2819 }
2820 
2821 }
2822