← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/wbr-is-void into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/wbr-is-void into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #971273 in Launchpad itself: "Launchpad is in denial about <wbr>'s void status"
  https://bugs.launchpad.net/launchpad/+bug/971273

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/wbr-is-void/+merge/100357

Launchpad currently spits out <wbr></wbr> sequences instead of the classical <wbr>. Amusingly, while having a separate closing tag makes it well-formed XML, because it's a void element it's neither valid HTML nor XHTML.

We should use <wbr /> instead. HTML5 and XML both like that, as do all the browsers we support.

The only thing I've found that doesn't is BeautifulSoup; it assumes the closing tag was forgotten, so extends the element to the next tag. Even if a browser I haven't tested does the same thing, the tag will just be ignored harmlessly.

This makes a lot more of our pages valid HTML5 while remaining well-formed XML.
-- 
https://code.launchpad.net/~wgrant/launchpad/wbr-is-void/+merge/100357
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/wbr-is-void into lp:launchpad.
=== modified file 'lib/lp/app/browser/stringformatter.py'
--- lib/lp/app/browser/stringformatter.py	2011-12-29 05:29:36 +0000
+++ lib/lp/app/browser/stringformatter.py	2012-04-02 05:52:24 +0000
@@ -153,7 +153,7 @@
     while pos < len(word):
         chunk, pos = next_word_chunk(word, pos, 7, 15)
         broken.append(chunk)
-    return '<wbr></wbr>'.join(broken)
+    return '<wbr />'.join(broken)
 
 
 break_text_pat = re.compile(r'''
@@ -718,7 +718,7 @@
             return line[:-4]
 
         for line in self.text_to_html().split('\n'):
-            if 'Desired=<wbr></wbr>Unknown/' in line and not in_fold:
+            if 'Desired=<wbr />Unknown/' in line and not in_fold:
                 # When we see a evidence of dpkg output, we switch the
                 # quote matching rules. We do not assume lines that start
                 # with a pipe are quoted passages. dpkg output is often

=== modified file 'lib/lp/app/browser/tests/test_stringformatter.py'
--- lib/lp/app/browser/tests/test_stringformatter.py	2012-01-01 02:58:52 +0000
+++ lib/lp/app/browser/tests/test_stringformatter.py	2012-04-02 05:52:24 +0000
@@ -73,18 +73,18 @@
       >>> from lp.app.browser.stringformatter import add_word_breaks
 
       >>> print add_word_breaks('abcdefghijklmnop')
-      abcdefghijklmno<wbr></wbr>p
+      abcdefghijklmno<wbr />p
 
       >>> print add_word_breaks('abcdef/ghijklmnop')
-      abcdef/<wbr></wbr>ghijklmnop
+      abcdef/<wbr />ghijklmnop
 
       >>> print add_word_breaks('ab/cdefghijklmnop')
-      ab/cdefghijklmn<wbr></wbr>op
+      ab/cdefghijklmn<wbr />op
 
     The string can contain HTML entities, which do not get split:
 
       >>> print add_word_breaks('abcdef&anentity;hijklmnop')
-      abcdef&anentity;<wbr></wbr>hijklmnop
+      abcdef&anentity;<wbr />hijklmnop
     """
 
 
@@ -100,13 +100,13 @@
       1234567890123456
 
       >>> print break_long_words('12345678901234567890')
-      123456789012345<wbr></wbr>67890
+      123456789012345<wbr />67890
 
       >>> print break_long_words('<tag a12345678901234567890="foo"></tag>')
       <tag a12345678901234567890="foo"></tag>
 
       >>> print break_long_words('12345678901234567890 1234567890.1234567890')
-      123456789012345<wbr></wbr>67890 1234567890.<wbr></wbr>1234567890
+      123456789012345<wbr />67890 1234567890.<wbr />1234567890
 
       >>> print break_long_words('1234567890&abcdefghi;123')
       1234567890&abcdefghi;123
@@ -172,13 +172,13 @@
 
         expected_strings = [
             ('<p><a rel="nofollow" href="http://example.com";>'
-             'http://<wbr></wbr>example.<wbr></wbr>com</a></p>'),
+             'http://<wbr />example.<wbr />com</a></p>'),
             ('<p><a rel="nofollow" href="http://example.com/";>'
-             'http://<wbr></wbr>example.<wbr></wbr>com/</a></p>'),
+             'http://<wbr />example.<wbr />com/</a></p>'),
             ('<p><a rel="nofollow" href="http://example.com/path";>'
-             'http://<wbr></wbr>example.<wbr></wbr>com/path</a></p>'),
+             'http://<wbr />example.<wbr />com/path</a></p>'),
             ('<p><a rel="nofollow" href="http://example.com/path/";>'
-             'http://<wbr></wbr>example.<wbr></wbr>com/path/</a></p>'),
+             'http://<wbr />example.<wbr />com/path/</a></p>'),
             ]
 
         self.assertEqual(
@@ -196,23 +196,23 @@
 
         expected_html = [
             ('<p>(<a rel="nofollow" href="http://example.com";>'
-             'http://<wbr></wbr>example.<wbr></wbr>com</a>)</p>'),
+             'http://<wbr />example.<wbr />com</a>)</p>'),
             ('<p><a rel="nofollow" '
              'href="http://example.com/path_(with_parens)">'
-             'http://<wbr></wbr>example.<wbr></wbr>com/path_'
-             '<wbr></wbr>(with_parens)</a></p>'),
+             'http://<wbr />example.<wbr />com/path_'
+             '<wbr />(with_parens)</a></p>'),
             ('<p>(<a rel="nofollow" '
              'href="http://example.com/path_(with_parens)">'
-             'http://<wbr></wbr>example.<wbr></wbr>com/path_'
-             '<wbr></wbr>(with_parens)</a>)</p>'),
+             'http://<wbr />example.<wbr />com/path_'
+             '<wbr />(with_parens)</a>)</p>'),
             ('<p>(<a rel="nofollow" '
              'href="http://example.com/path_(with_parens)and_stuff">'
-             'http://<wbr></wbr>example.<wbr></wbr>com'
-             '/path_<wbr></wbr>(with_parens)<wbr></wbr>and_stuff</a>)</p>'),
+             'http://<wbr />example.<wbr />com'
+             '/path_<wbr />(with_parens)<wbr />and_stuff</a>)</p>'),
             ('<p><a rel="nofollow" '
              'href="http://example.com/path_(with_parens">'
-             'http://<wbr></wbr>example.<wbr></wbr>com'
-             '/path_<wbr></wbr>(with_parens</a></p>'),
+             'http://<wbr />example.<wbr />com'
+             '/path_<wbr />(with_parens</a></p>'),
             ]
 
         self.assertEqual(
@@ -236,7 +236,7 @@
         expected_html = (
             '<p>This becomes a link: '
             '<a rel="nofollow" '
-                'href="apt:some-package">apt:some-<wbr></wbr>package</a></p>')
+                'href="apt:some-package">apt:some-<wbr />package</a></p>')
         self.assertEqual(expected_html, html)
 
         # Do it again for apt://
@@ -245,7 +245,7 @@
         expected_html = (
             '<p>This becomes a link: '
             '<a rel="nofollow" '
-            'href="apt://some-package">apt://some-<wbr></wbr>package</a></p>')
+            'href="apt://some-package">apt://some-<wbr />package</a></p>')
         self.assertEqual(expected_html, html)
 
     def test_file_is_not_linked(self):
@@ -253,7 +253,7 @@
         html = FormattersAPI(test_string).text_to_html()
         expected_html = (
             "<p>This doesn't become a link: "
-            "file://<wbr></wbr>some/file.<wbr></wbr>txt</p>")
+            "file://<wbr />some/file.<wbr />txt</p>")
         self.assertEqual(expected_html, html)
 
     def test_data_is_linked(self):
@@ -263,7 +263,7 @@
             "<p>This becomes a link: "
             '<a rel="nofollow" '
             'href="data:text/plain,test">'
-            'data:text/<wbr></wbr>plain,test</a></p>')
+            'data:text/<wbr />plain,test</a></p>')
         self.assertEqual(expected_html, html)
 
     def test_no_link_with_linkify_text_false(self):

=== modified file 'lib/lp/app/doc/displaying-paragraphs-of-text.txt'
--- lib/lp/app/doc/displaying-paragraphs-of-text.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/app/doc/displaying-paragraphs-of-text.txt	2012-04-02 05:52:24 +0000
@@ -85,7 +85,7 @@
     <p>This is a code sample written in Python.<br />
     &nbsp;&nbsp;&nbsp;&nbsp;def messageCount(self):<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""See IRosettaStats."""<br />
-    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.potemplate<wbr></wbr>.messageCount(<wbr></wbr>)</p>
+    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.potemplate<wbr />.messageCount(<wbr />)</p>
     <p>&nbsp;&nbsp;&nbsp;&nbsp;def currentCount(self, language=None):<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""See IRosettaStats."""<br />
     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self.currentCount</p>
@@ -103,12 +103,12 @@
     ...     'I have a Jabber account (jabber:foo@xxxxxxxxxxxxxxxxxx)\n'
     ...     'Foo Bar <mailto:foo.bar@xxxxxxxxxxx>')
     >>> print test_tales('foo/fmt:text-to-html', foo=text)
-    <p><a rel="nofollow" href="https://launchpad.net/";>https:/<wbr></wbr>/launchpad.<wbr></wbr>net/</a> is the new Launchpad site<br />
-    <a rel="nofollow" href="http://example.com/something?foo=bar&amp;hum=baz";>http://<wbr></wbr>example.<wbr></wbr>com/something?<wbr></wbr>foo=bar&amp;<wbr></wbr>hum=baz</a><br />
-    You can check the PPC md5sums at <a rel="nofollow" href="ftp://ftp.ubuntu.com/ubuntu/dists/breezy/main/installer-powerpc/current/images/MD5SUMS";>ftp://ftp.<wbr></wbr>ubuntu.<wbr></wbr>com/ubuntu/<wbr></wbr>dists/breezy/<wbr></wbr>main/installer-<wbr></wbr>powerpc/<wbr></wbr>current/<wbr></wbr>images/<wbr></wbr>MD5SUMS</a><br />
-    <a rel="nofollow" href="irc://irc.freenode.net/#launchpad">irc://irc.<wbr></wbr>freenode.<wbr></wbr>net/#launchpad</a></p>
-    <p>I have a Jabber account (<a rel="nofollow" href="jabber:foo@xxxxxxxxxxxxxxxxxx">jabber:<wbr></wbr>foo@jabber.<wbr></wbr>example.<wbr></wbr>com</a>)<br />
-    Foo Bar &lt;<a rel="nofollow" href="mailto:foo.bar@xxxxxxxxxxx";>mailto:<wbr></wbr>foo.bar@<wbr></wbr>example.<wbr></wbr>net</a>&gt;</p>
+    <p><a rel="nofollow" href="https://launchpad.net/";>https:/<wbr />/launchpad.<wbr />net/</a> is the new Launchpad site<br />
+    <a rel="nofollow" href="http://example.com/something?foo=bar&amp;hum=baz";>http://<wbr />example.<wbr />com/something?<wbr />foo=bar&amp;<wbr />hum=baz</a><br />
+    You can check the PPC md5sums at <a rel="nofollow" href="ftp://ftp.ubuntu.com/ubuntu/dists/breezy/main/installer-powerpc/current/images/MD5SUMS";>ftp://ftp.<wbr />ubuntu.<wbr />com/ubuntu/<wbr />dists/breezy/<wbr />main/installer-<wbr />powerpc/<wbr />current/<wbr />images/<wbr />MD5SUMS</a><br />
+    <a rel="nofollow" href="irc://irc.freenode.net/#launchpad">irc://irc.<wbr />freenode.<wbr />net/#launchpad</a></p>
+    <p>I have a Jabber account (<a rel="nofollow" href="jabber:foo@xxxxxxxxxxxxxxxxxx">jabber:<wbr />foo@jabber.<wbr />example.<wbr />com</a>)<br />
+    Foo Bar &lt;<a rel="nofollow" href="mailto:foo.bar@xxxxxxxxxxx";>mailto:<wbr />foo.bar@<wbr />example.<wbr />net</a>&gt;</p>
 
 
 URL linkification
@@ -158,43 +158,43 @@
     ... )
 
     >>> print test_tales('foo/fmt:text-to-html', foo=text)
-    <p><a rel="nofollow" href="http://localhost:8086/bar/baz/foo.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>html</a><br />
-    <a rel="nofollow" href="ftp://localhost:8086/bar/baz/foo.bar.html";>ftp://localhost<wbr></wbr>:8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a><br />
-    <a rel="nofollow" href="sftp://localhost:8086/bar/baz/foo.bar.html";>sftp://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>.<br />
-    <a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>;<br />
-    <a rel="nofollow" href="news://localhost:8086/bar/baz/foo.bar.html";>news://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>:<br />
-    <a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>?<br />
-    <a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>,<br />
-    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>&gt;<br />
-    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>&gt;,<br />
-    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>&gt;.<br />
-    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>&gt;;<br />
-    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>&gt;:<br />
-    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>&gt;?<br />
-    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>)<br />
-    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>),<br />
-    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>).<br />
-    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>);<br />
-    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr></wbr>localhost:<wbr></wbr>8086/bar/<wbr></wbr>baz/foo.<wbr></wbr>bar.html</a>):<br />
-    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr></wbr>localhost/<wbr></wbr>bar/baz/<wbr></wbr>foo.bar.<wbr></wbr>html?a=<wbr></wbr>b&amp;b=a</a><br />
-    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr></wbr>localhost/<wbr></wbr>bar/baz/<wbr></wbr>foo.bar.<wbr></wbr>html?a=<wbr></wbr>b&amp;b=a</a>.<br />
-    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr></wbr>localhost/<wbr></wbr>bar/baz/<wbr></wbr>foo.bar.<wbr></wbr>html?a=<wbr></wbr>b&amp;b=a</a>,<br />
-    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr></wbr>localhost/<wbr></wbr>bar/baz/<wbr></wbr>foo.bar.<wbr></wbr>html?a=<wbr></wbr>b&amp;b=a</a>;<br />
-    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr></wbr>localhost/<wbr></wbr>bar/baz/<wbr></wbr>foo.bar.<wbr></wbr>html?a=<wbr></wbr>b&amp;b=a</a>:<br />
-    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a:b;c@d_e%f~g#h,j!k-l+m$n*o'p">http://<wbr></wbr>localhost/<wbr></wbr>bar/baz/<wbr></wbr>foo.bar.<wbr></wbr>html?a=<wbr></wbr>b&amp;b=a:b;<wbr></wbr>c@d_e%f~<wbr></wbr>g#h,j!k-<wbr></wbr>l+m$n*o'<wbr></wbr>p</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/(parens).html">http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/(parens)<wbr></wbr>.html</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/-dash.html";>http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/-dash.<wbr></wbr>html</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/_underscore.html";>http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/_underscor<wbr></wbr>e.html</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/period.x.html";>http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/period.<wbr></wbr>x.html</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/!exclamation.html";>http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/!exclamati<wbr></wbr>on.html</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/~tilde.html";>http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/~tilde.<wbr></wbr>html</a><br />
-    <a rel="nofollow" href="http://www.searchtools.com/test/urls/*asterisk.html";>http://<wbr></wbr>www.searchtools<wbr></wbr>.com/test/<wbr></wbr>urls/*asterisk.<wbr></wbr>html</a><br />
-    <a rel="nofollow" href="irc://irc.freenode.net/launchpad">irc://irc.<wbr></wbr>freenode.<wbr></wbr>net/launchpad</a><br />
-    <a rel="nofollow" href="irc://irc.freenode.net/%23launchpad,isserver">irc://irc.<wbr></wbr>freenode.<wbr></wbr>net/%23launchpa<wbr></wbr>d,isserver</a><br />
-    <a rel="nofollow" href="mailto:noreply@xxxxxxxxxxxxx";>mailto:<wbr></wbr>noreply@<wbr></wbr>launchpad.<wbr></wbr>net</a><br />
-    <a rel="nofollow" href="jabber:noreply@xxxxxxxxxxxxx">jabber:<wbr></wbr>noreply@<wbr></wbr>launchpad.<wbr></wbr>net</a><br />
-    <a rel="nofollow" href="http://localhost/foo?xxx&amp;";>http://<wbr></wbr>localhost/<wbr></wbr>foo?xxx&amp;</a><br />
-    <a rel="nofollow" href="http://localhost?testing=[square-brackets-in-query]";>http://<wbr></wbr>localhost?<wbr></wbr>testing=<wbr></wbr>[square-<wbr></wbr>brackets-<wbr></wbr>in-query]</a></p>
+    <p><a rel="nofollow" href="http://localhost:8086/bar/baz/foo.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />html</a><br />
+    <a rel="nofollow" href="ftp://localhost:8086/bar/baz/foo.bar.html";>ftp://localhost<wbr />:8086/bar/<wbr />baz/foo.<wbr />bar.html</a><br />
+    <a rel="nofollow" href="sftp://localhost:8086/bar/baz/foo.bar.html";>sftp://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>.<br />
+    <a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>;<br />
+    <a rel="nofollow" href="news://localhost:8086/bar/baz/foo.bar.html";>news://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>:<br />
+    <a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>?<br />
+    <a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>,<br />
+    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>&gt;<br />
+    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>&gt;,<br />
+    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>&gt;.<br />
+    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>&gt;;<br />
+    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>&gt;:<br />
+    &lt;<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>&gt;?<br />
+    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>)<br />
+    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>),<br />
+    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>).<br />
+    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>);<br />
+    (<a rel="nofollow" href="http://localhost:8086/bar/baz/foo.bar.html";>http://<wbr />localhost:<wbr />8086/bar/<wbr />baz/foo.<wbr />bar.html</a>):<br />
+    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr />localhost/<wbr />bar/baz/<wbr />foo.bar.<wbr />html?a=<wbr />b&amp;b=a</a><br />
+    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr />localhost/<wbr />bar/baz/<wbr />foo.bar.<wbr />html?a=<wbr />b&amp;b=a</a>.<br />
+    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr />localhost/<wbr />bar/baz/<wbr />foo.bar.<wbr />html?a=<wbr />b&amp;b=a</a>,<br />
+    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr />localhost/<wbr />bar/baz/<wbr />foo.bar.<wbr />html?a=<wbr />b&amp;b=a</a>;<br />
+    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a";>http://<wbr />localhost/<wbr />bar/baz/<wbr />foo.bar.<wbr />html?a=<wbr />b&amp;b=a</a>:<br />
+    <a rel="nofollow" href="http://localhost/bar/baz/foo.bar.html?a=b&amp;b=a:b;c@d_e%f~g#h,j!k-l+m$n*o'p">http://<wbr />localhost/<wbr />bar/baz/<wbr />foo.bar.<wbr />html?a=<wbr />b&amp;b=a:b;<wbr />c@d_e%f~<wbr />g#h,j!k-<wbr />l+m$n*o'<wbr />p</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/(parens).html">http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/(parens)<wbr />.html</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/-dash.html";>http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/-dash.<wbr />html</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/_underscore.html";>http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/_underscor<wbr />e.html</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/period.x.html";>http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/period.<wbr />x.html</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/!exclamation.html";>http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/!exclamati<wbr />on.html</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/~tilde.html";>http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/~tilde.<wbr />html</a><br />
+    <a rel="nofollow" href="http://www.searchtools.com/test/urls/*asterisk.html";>http://<wbr />www.searchtools<wbr />.com/test/<wbr />urls/*asterisk.<wbr />html</a><br />
+    <a rel="nofollow" href="irc://irc.freenode.net/launchpad">irc://irc.<wbr />freenode.<wbr />net/launchpad</a><br />
+    <a rel="nofollow" href="irc://irc.freenode.net/%23launchpad,isserver">irc://irc.<wbr />freenode.<wbr />net/%23launchpa<wbr />d,isserver</a><br />
+    <a rel="nofollow" href="mailto:noreply@xxxxxxxxxxxxx";>mailto:<wbr />noreply@<wbr />launchpad.<wbr />net</a><br />
+    <a rel="nofollow" href="jabber:noreply@xxxxxxxxxxxxx">jabber:<wbr />noreply@<wbr />launchpad.<wbr />net</a><br />
+    <a rel="nofollow" href="http://localhost/foo?xxx&amp;";>http://<wbr />localhost/<wbr />foo?xxx&amp;</a><br />
+    <a rel="nofollow" href="http://localhost?testing=[square-brackets-in-query]";>http://<wbr />localhost?<wbr />testing=<wbr />[square-<wbr />brackets-<wbr />in-query]</a></p>
 
 
 The fmt:text-to-html formatter leaves a number of non-URIs unlinked:
@@ -203,7 +203,7 @@
     ...     'nothttp://launchpad.net/\n'
     ...     'http::No-cache=True\n')
     >>> print test_tales('foo/fmt:text-to-html', foo=text)
-    <p>nothttp:<wbr></wbr>//launchpad.<wbr></wbr>net/<br />
+    <p>nothttp:<wbr />//launchpad.<wbr />net/<br />
     http::No-cache=True</p>
 
 

=== modified file 'lib/lp/app/doc/tales-email-formatting.txt'
--- lib/lp/app/doc/tales-email-formatting.txt	2011-06-23 13:10:40 +0000
+++ lib/lp/app/doc/tales-email-formatting.txt	2012-04-02 05:52:24 +0000
@@ -122,8 +122,8 @@
     ...               '\n')
     >>> print test_tales('foo/fmt:email-to-html',
     ...                  foo='\n'.join([python, not_python]))
-    <p>&gt;&gt;&gt; tz = pytz.timezone(<wbr></wbr>"Asia/Calcutta"...
-    &gt;&gt;&gt; mydate = datetime.<wbr></wbr>datetime(<wbr></wbr>2007, 2, ...
+    <p>&gt;&gt;&gt; tz = pytz.timezone(<wbr />"Asia/Calcutta"...
+    &gt;&gt;&gt; mydate = datetime.<wbr />datetime(<wbr />2007, 2, ...
     2007-02-18 15:35:00+05:30</p>
     <p><span class="foldable-quoted">&gt; This line really is a quoted ...
     &gt;&gt;&gt; This does not invoke an exception rule.
@@ -154,11 +154,11 @@
     ...         '\n')
     >>> print test_tales('foo/fmt:email-to-html', foo=dpkg)
     <p>dpkg -l libdvdread3<br />
-    Desired=<wbr></wbr>Unknown/<wbr></wbr>Install/<wbr></wbr>...
-    | Status=<wbr></wbr>Not/Installed/<wbr></wbr>Config-<wbr></wbr>...
-    |/ Err?=(none)<wbr></wbr>/Hold/Reinst-<wbr></wbr>required/...
+    Desired=<wbr />Unknown/<wbr />Install/<wbr />...
+    | Status=<wbr />Not/Installed/<wbr />Config-<wbr />...
+    |/ Err?=(none)<wbr />/Hold/Reinst-<wbr />required/...
     ||/ Name Version Description<br />
-    +++-===<wbr></wbr>=======<wbr></wbr>====-==<wbr></wbr>=======...
+    +++-===<wbr />=======<wbr />====-==<wbr />=======...
     ii libdvdread3 0.9.7-2ubuntu1 library for reading DVDs</p>
 
     >>> bad_dpkg = ('When dpkg output is in text, possibly tampered with,\n'
@@ -175,11 +175,11 @@
     ...                  foo='\n'.join([bad_dpkg]))
     <p>When dpkg output is in text, possibly tampered with,<br />
     we must take care to identify '|' quoted passages.<br />
-    $ Desired=<wbr></wbr>Unknown/<wbr></wbr>Install/<wbr></wbr>Remove/...
+    $ Desired=<wbr />Unknown/<wbr />Install/<wbr />Remove/...
     |<br />
-    &nbsp;Status=<wbr></wbr>Not/Installed/<wbr></wbr>Config-...
-    |/ Err?=(none)<wbr></wbr>/Hold/Reinst-<wbr></wbr>required/...
+    &nbsp;Status=<wbr />Not/Installed/<wbr />Config-...
+    |/ Err?=(none)<wbr />/Hold/Reinst-<wbr />required/...
     ||/ Name Version Description<br />
-    +++-===<wbr></wbr>=======<wbr></wbr>====-==<wbr></wbr>=======...
+    +++-===<wbr />=======<wbr />====-==<wbr />=======...
     ii libdvdread3 0.9.7-2ubuntu1 library for reading DVDs</p>
 

=== modified file 'lib/lp/app/doc/tales.txt'
--- lib/lp/app/doc/tales.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/app/doc/tales.txt	2012-04-02 05:52:24 +0000
@@ -287,7 +287,7 @@
 
     >>> test_tales('foo/fmt:break-long-words',
     ...     foo='<http://launchpad.net/products/launchpad>')
-    '&lt;http:/<wbr></wbr>/launchpad.<wbr></wbr>...<wbr></wbr>launchpad&gt;'
+    '&lt;http:/<wbr />/launchpad.<wbr />...<wbr />launchpad&gt;'
 
 To get a int with its thousands separated by a comma, use fmt:intcomma.
 
@@ -924,8 +924,8 @@
     Version: GnuPG v1.4.1 (GNU/Linux)<br />
     Comment: Using GnuPG with Thunderbird<br />
     <br />
-    iD8DBQFED60Y0F+<wbr></wbr>nu1YWqI0RAqrNAJ<wbr></wbr>...
-    T2PIWy0CUJsX8RX<wbr></wbr>St/M51WE=<br />
+    iD8DBQFED60Y0F+<wbr />nu1YWqI0RAqrNAJ<wbr />...
+    T2PIWy0CUJsX8RX<wbr />St/M51WE=<br />
     =J2S5<br />
     -----END PGP SIGNATURE-----
     </span></p>
@@ -942,7 +942,7 @@
     Raise your hand if you can read this.</p>
     <p><span class="foldable"...>--<br />
     __C U R T I S  C.  H O V E Y_______<br />
-    sinzui.<wbr></wbr>is@example.<wbr></wbr>org<br />
+    sinzui.<wbr />is@example.<wbr />org<br />
     Guilty of stealing everything I am.
     </span></p>
 
@@ -1012,7 +1012,7 @@
     </span></p>
     <p><span class="foldable"...>--<br />
     __C U R T I S  C.  H O V E Y_______<br />
-    sinzui.<wbr></wbr>is@example.<wbr></wbr>org<br />
+    sinzui.<wbr />is@example.<wbr />org<br />
     Guilty of stealing everything I am.
     </span></p>
 

=== modified file 'lib/lp/blueprints/workitemmigration.py'
--- lib/lp/blueprints/workitemmigration.py	2012-03-22 23:21:24 +0000
+++ lib/lp/blueprints/workitemmigration.py	2012-04-02 05:52:24 +0000
@@ -75,7 +75,7 @@
 
 def milestone_extract(text, valid_milestones):
     words = text.replace('(', ' ').replace(')', ' ').replace(
-        '[', ' ').replace(']', ' ').replace('<wbr></wbr>', '').split()
+        '[', ' ').replace(']', ' ').replace('<wbr />', '').split()
     for milestone in valid_milestones:
         for word in words:
             if word == milestone.name:

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt	2012-02-21 22:30:41 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-comments-truncated.txt	2012-04-02 05:52:24 +0000
@@ -136,8 +136,13 @@
     >>> anon_browser.open('http://bugs.launchpad.dev/tomcat/+bug/2')
     >>> anon_browser.title.decode('utf-8')
     u'Bug #2 (blackhole) ... : Bugs : Tomcat'
+
+BeautifulSoup doesn't realise that wbr is a void element, so it has a
+habit of extending it to the next tag unless we close it separately.
+
     >>> text = find_tags_by_class(
-    ...     anon_browser.contents, 'boardCommentBody')[-1]
+    ...     anon_browser.contents.replace('<wbr />', '<wbr></wbr>'),
+    ...     'boardCommentBody')[-1]
     >>> print extract_text(text.findAll('p')[-2])
     --
     ______________________

=== modified file 'lib/lp/registry/browser/tests/person-views.txt'
--- lib/lp/registry/browser/tests/person-views.txt	2012-02-10 19:52:27 +0000
+++ lib/lp/registry/browser/tests/person-views.txt	2012-04-02 05:52:24 +0000
@@ -28,7 +28,7 @@
     >>> print view.homepage_content
     <p>line one &lt;script&gt;</p>
     <BLANKLINE>
-    <p><a rel="nofollow" href="http://aa.aa/";>http://<wbr></wbr>aa.aa/</a></p>
+    <p><a rel="nofollow" href="http://aa.aa/";>http://<wbr />aa.aa/</a></p>
 
 Teams are always valid and do not have probation rules; the homepage
 content is formatted HTML.
@@ -44,7 +44,7 @@
     >>> print view.homepage_content
     <p>line one &lt;script&gt;</p>
     <BLANKLINE>
-    <p><a rel="nofollow" href="http://aa.aa/";>http://<wbr></wbr>aa.aa/</a></p>
+    <p><a rel="nofollow" href="http://aa.aa/";>http://<wbr />aa.aa/</a></p>
 
 New users are on probation; the homepage content is escaped HTML.