Repository: bokeh/bokeh

Issue:
Incorrect Layout with column of Div's with Image (1.1.0dev3)
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)
python 3.6
bokeh 1.1.0dev3
bokeh css/js 1.1.0dev3
jquery 3.1.0
Google Chrome Version 72.0.3626.96 (Official Build) (64-bit)
FireFox 65.0 (64-bit)
Host OS: Ubuntu 18.04

#### Description of expected behavior and the observed behavior
Layout produces non-overlapping rows in a column.
Layout of Div rows in a column, with an image Div, the next row overwrites the image (row above).

NOTE: Sometimes the layout IS CORRECT.  Or can be seen to be correct.  For example, when I press the "LOGIN" button, (which has no action) and then hit refresh, the layout becomes correct.  If I then go to another URL and then back to the flask URL, the layout goes back to being incorrect.

#### Complete, minimal, self-contained example code that reproduces the issue
```
from flask import Flask, send_from_directory

import bokeh
from bokeh.layouts import layout, row
from bokeh.models.layouts import Spacer
from bokeh.models.widgets import Div
from bokeh.models.widgets.buttons import Button
from bokeh.embed import components

from dominate.tags import *
import dominate
from dominate.util import raw

app = Flask(__name__)
print(bokeh.__version__)  # this prints 1.1.0dev3

@app.route('/')
def index():
    d = dominate.document(title="test")
    with d.head:
        bokeh_version = '1.1.0dev3'
        link(href="https://cdn.pydata.org/bokeh/dev/bokeh-{bokeh_version}.min.css".format(
            bokeh_version=bokeh_version), rel="stylesheet", type="text/css")
        script(src="https://cdn.pydata.org/bokeh/dev/bokeh-{bokeh_version}.min.js".format(bokeh_version=bokeh_version))
        link(href="https://cdn.pydata.org/bokeh/dev/bokeh-widgets-{bokeh_version}.min.css".format(
            bokeh_version=bokeh_version), rel="stylesheet", type="text/css")
        script(src="https://cdn.pydata.org/bokeh/dev/bokeh-widgets-{bokeh_version}.min.js".format(bokeh_version=bokeh_version))
        link(href="https://cdn.pydata.org/bokeh/dev/bokeh-tables-{bokeh_version}.min.css".format(
            bokeh_version=bokeh_version), rel="stylesheet", type="text/css")
        script(src="https://cdn.pydata.org/bokeh/dev/bokeh-tables-{bokeh_version}.min.js".format(bokeh_version=bokeh_version))

        script(src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js")
        meta(charset="UTF-8")

    doc_layout = layout()
    b_login = Button(label="LOGIN", width=50)
    doc_layout.children.append(row(Div(text="""<h1>{}</h1>""".format("title"), width=100), Spacer(width=100), b_login))
    doc_layout.children.append(Div(text="""<img src="/pywrapstatic/index.png">"""))
    doc_layout.children.append(Div(text="""Version: {}""".format("1.0.1"), width=200))
    doc_layout.children.append(Div(text="message", width=600))

    _script, _div = components(doc_layout)
    d.body += raw(_script)
    d.body += raw(_div)
    return "{}".format(d)

@app.route('/pywrapstatic/<path:path>')
def common__serve_static(path):
    return send_from_directory('.', path)

app.run(host="0.0.0.0", port=6800, debug=False)
```

#### Stack traceback and/or browser JavaScript console output
![screenshot from 2019-02-09 15-17-43](https://user-images.githubusercontent.com/40646488/52525854-e53c9e00-2c7d-11e9-8b14-29ece281a09b.png)

#### Screenshots or screencasts of the bug in action
The "version" and "message" text is expected to go below the image.
![screenshot from 2019-02-09 15-07-55](https://user-images.githubusercontent.com/40646488/52525778-9f330a80-2c7c-11e9-8f53-c5d488439a7d.png)



Discussion:
@mgagcode I am not sure what the intent is here, in case you want to have a fixed size for the image, then changing your code to 
```
doc_layout.children.append(
    Div(text="""<img src="/pywrapstatic/index.png" height=200>""", 
        height=200, sizing_mode="fixed")
)
```
seems to always behave well. This should be looked at at some point but that might be a useful workaround for now. 
Confirmed that this workaround works.
The issue is also in 1.1.0dev8.
The problem here is that images are loaded asynchronously, so layout may already be computed when they arrive. Setting height (and width for that matter) either in the HTML or on the layout is the right approach and not a workaround. We could improve the situation by setting up `load` event handlers on all images and recomputing layout when they are fully loaded, but that's a slippery slope because then we would be asked do the same for CSS background images (there's no event for this), etc. Alternatively we could poll for content size changes, but that may induce a significant performance penalty. We already use polling for detecting display changes, but that's very cheap to check. I will primarily consider this as a documentation issue.

Resolving patch:
diff --git a/bokeh/models/widgets/markups.py b/bokeh/models/widgets/markups.py
--- a/bokeh/models/widgets/markups.py
+++ b/bokeh/models/widgets/markups.py
@@ -60,7 +60,16 @@ class Markup(Widget):
     '''
 
     text = String(default="", help="""
-    The contents of the widget.
+    The text or HTML contents of the widget.
+
+    .. note::
+        If the HTML content contains elements which size depends on
+        on external, asynchronously loaded resources, the size of
+        the widget may be computed incorrectly. This is in particular
+        an issue with images (``<img>``). To remedy this problem, one
+        either has to set explicit dimensions using CSS properties,
+        HTML attributes or model's ``width`` and ``height`` properties,
+        or inline images' contents using data URIs.
     """)
 
     style = Dict(String, Any, default={}, help="""


Test patch: