Prefix CSS classes in message HTML to avoid collisions with HTML messages.

* ui/conversation-web-view.css: Prefix the names of base Geary-internal
  elements with "geary_" to reduce the odds of them colliding with class
  names in HTML messages. Chase the name changes in classes that generate
  them.

* src/client/conversation-viewer/conversation-message.vala
  (ConversationMessage): Use constants for class names and
  WebKit.DOMElement::class_list to add/remove them.
This commit is contained in:
Michael James Gratton 2016-08-11 17:31:08 +10:00
parent b5e7c27c58
commit 5ea4b0a051
3 changed files with 64 additions and 51 deletions

View file

@ -251,7 +251,7 @@ public class ConversationFindBar : Gtk.Layout {
private void switch_to_search_selection_color() {
try {
web_view.get_dom_document().get_body().get_class_list().add("search_coloring");
web_view.get_dom_document().get_body().get_class_list().add("geary_search_coloring");
} catch (Error error) {
warning("Error setting body class for search selection coloring: %s", error.message);
}
@ -259,7 +259,7 @@ public class ConversationFindBar : Gtk.Layout {
private void switch_to_usual_selection_color() {
try {
web_view.get_dom_document().get_body().get_class_list().remove("search_coloring");
web_view.get_dom_document().get_body().get_class_list().remove("geary_search_coloring");
} catch (Error error) {
warning("Error setting body class for search selection coloring: %s", error.message);
}

View file

@ -78,8 +78,12 @@ public class ConversationMessage : Gtk.Box {
"image/x-xbitmap",
"image/x-xbm"
};
private const string REPLACED_IMAGE_CLASS = "replaced_inline_image";
private const string DATA_IMAGE_CLASS = "data_inline_image";
private const string QUOTE_CONTAINER_CLASS = "geary_quote_container";
private const string QUOTE_CONTROLLABLE_CLASS = "controllable";
private const string QUOTE_HIDE_CLASS = "hide";
private const string SIGNATURE_CONTAINER_CLASS = "geary_signature";
private const string REPLACED_IMAGE_CLASS = "geary_replaced_inline_image";
private const string DATA_IMAGE_CLASS = "geary_data_inline_image";
private const int MAX_INLINE_IMAGE_MAJOR_DIM = 1024;
private const float QUOTE_SIZE_THRESHOLD = 2.0f;
@ -421,14 +425,22 @@ public class ConversationMessage : Gtk.Box {
error.message);
}
}
Util.DOM.bind_event(this.web_view, "html", "contextmenu",
(Callback) on_context_menu, this);
Util.DOM.bind_event(this.web_view, "body a", "click",
(Callback) on_link_clicked, this);
Util.DOM.bind_event(this.web_view, ".quote_container > .shower", "click",
(Callback) on_show_quote_clicked, this);
Util.DOM.bind_event(this.web_view, ".quote_container > .hider", "click",
(Callback) on_hide_quote_clicked, this);
Util.DOM.bind_event(
this.web_view, "html", "contextmenu",
(Callback) on_context_menu, this
);
Util.DOM.bind_event(
this.web_view, "body a", "click",
(Callback) on_link_clicked, this
);
Util.DOM.bind_event(
this.web_view, ".%s > .shower".printf(QUOTE_CONTAINER_CLASS),
"click",
(Callback) on_show_quote_clicked, this);
Util.DOM.bind_event(
this.web_view, ".%s > .hider".printf(QUOTE_CONTAINER_CLASS),
"click",
(Callback) on_hide_quote_clicked, this);
// XXX Not actually true since remote images will
// still be loading.
@ -500,7 +512,7 @@ public class ConversationMessage : Gtk.Box {
// Remove the chrome we put around quotes, leaving
// only the blockquote element.
WebKit.DOM.NodeList quotes =
dummy.query_selector_all(".quote_container");
dummy.query_selector_all("." + QUOTE_CONTAINER_CLASS);
for (int i = 0; i < quotes.length; i++) {
WebKit.DOM.Element div = (WebKit.DOM.Element) quotes.item(i);
WebKit.DOM.Element blockquote = div.query_selector("blockquote");
@ -821,10 +833,6 @@ public class ConversationMessage : Gtk.Box {
continue;
}
// parent
// quote_container
// blockquote
// sibling
WebKit.DOM.Element quote_container = create_quote_container();
Util.DOM.select(quote_container, ".quote").append_child(blockquote_node);
if (next_sibling == null) {
@ -878,7 +886,7 @@ public class ConversationMessage : Gtk.Box {
// and the ALT to its filename, if supplied
img.remove_attribute("src"); // Work around a WebKitGTK+ crash. Bug 764152
img.set_attribute("src", Util.DOM.assemble_data_uri(mimetype, image_content));
img.set_attribute("class", DATA_IMAGE_CLASS);
img.class_list.add(DATA_IMAGE_CLASS);
if (!Geary.String.is_empty(filename))
img.set_attribute("alt", filename);
@ -911,12 +919,12 @@ public class ConversationMessage : Gtk.Box {
return text;
}
}
private WebKit.DOM.HTMLElement create_quote_container() throws Error {
WebKit.DOM.HTMLElement quote_container = web_view.create("div");
quote_container.set_attribute(
"class", "quote_container controllable hide"
);
quote_container.class_list.add(QUOTE_CONTAINER_CLASS);
quote_container.class_list.add(QUOTE_CONTROLLABLE_CLASS);
quote_container.class_list.add(QUOTE_HIDE_CLASS);
// New lines are preserved within blockquotes, so this string
// needs to be new-line free.
quote_container.set_inner_html("""<div class="shower"><input type="button" value=" " /></div><div class="hider"><input type="button" value=" " /></div><div class="quote"></div>""");
@ -952,7 +960,7 @@ public class ConversationMessage : Gtk.Box {
WebKit.DOM.Node elem = div_list.item(i) as WebKit.DOM.Node;
WebKit.DOM.Element parent = elem.get_parent_element();
WebKit.DOM.HTMLElement signature_container = web_view.create("div");
signature_container.set_attribute("class", "signature");
signature_container.class_list.add(SIGNATURE_CONTAINER_CLASS);
do {
// Get its sibling _before_ we move it into the signature div.
WebKit.DOM.Node? sibling = elem.get_next_sibling();
@ -963,8 +971,9 @@ public class ConversationMessage : Gtk.Box {
}
private void unset_controllable_quotes(WebKit.DOM.HTMLElement element) throws GLib.Error {
WebKit.DOM.NodeList quote_list =
element.query_selector_all(".quote_container.controllable");
WebKit.DOM.NodeList quote_list = element.query_selector_all(
".%s.%s".printf(QUOTE_CONTAINER_CLASS, QUOTE_CONTROLLABLE_CLASS)
);
for (int i = 0; i < quote_list.length; ++i) {
WebKit.DOM.Element quote_container = quote_list.item(i) as WebKit.DOM.Element;
long outer_client_height = quote_container.client_height;
@ -974,8 +983,8 @@ public class ConversationMessage : Gtk.Box {
// substantial amount hidden.
if (scroll_height > 0 &&
scroll_height <= outer_client_height * QUOTE_SIZE_THRESHOLD) {
quote_container.class_list.remove("controllable");
quote_container.class_list.remove("hide");
quote_container.class_list.remove(QUOTE_CONTROLLABLE_CLASS);
quote_container.class_list.remove(QUOTE_HIDE_CLASS);
}
}
}
@ -1108,7 +1117,9 @@ public class ConversationMessage : Gtk.Box {
private static void on_show_quote_clicked(WebKit.DOM.Element element,
WebKit.DOM.Event event) {
try {
((WebKit.DOM.HTMLElement) element.parent_node).class_list.remove("hide");
((WebKit.DOM.HTMLElement) element.parent_node).class_list.remove(
QUOTE_HIDE_CLASS
);
} catch (Error error) {
warning("Error showing quote: %s", error.message);
}
@ -1118,7 +1129,9 @@ public class ConversationMessage : Gtk.Box {
WebKit.DOM.Event event,
ConversationMessage message) {
try {
((WebKit.DOM.HTMLElement) element.parent_node).class_list.add("hide");
((WebKit.DOM.HTMLElement) element.parent_node).class_list.add(
QUOTE_HIDE_CLASS
);
message.web_view.queue_resize();
} catch (Error error) {
warning("Error toggling quote: %s", error.message);

View file

@ -66,19 +66,19 @@ pre {
* Message chrome style.
*/
.signature {
.geary_signature {
color: #777;
display: inline;
}
.signature a,
.quote_container a {
.geary_signature a,
.geary_quote_container a {
color: #5fb2e7;
}
@media screen {
.replaced_inline_image {
.geary_replaced_inline_image {
display: block;
max-width: 100%;
margin-top: 1em;
@ -86,7 +86,7 @@ pre {
/* Inline collapsable quote blocks */
.quote_container {
.geary_quote_container {
position: relative;
margin: 0.5em 0;
border-radius: 4px;
@ -94,11 +94,11 @@ pre {
color: #303030;
background-color: #e8e8e8;/* recv-quoted */
}
.sent .quote_container {
.geary_sent .geary_quote_container {
background-color: #e8e8e8;/* sent-quoted */
}
.quote_container > .quote {
.geary_quote_container > .quote {
position: relative;
margin: 0;
border: 0;
@ -106,18 +106,18 @@ pre {
overflow: hidden;
z-index: 0;
}
.quote_container.controllable.hide > .quote {
.geary_quote_container.controllable.hide > .quote {
/* Use a fraction value to cut the last visible line off half way. */
max-height: 7.75em;
}
.quote_container.controllable > .quote > blockquote {
.geary_quote_container.controllable > .quote > blockquote {
/* Add space between the quote and the hider button */
margin-bottom: 18px;
}
.quote_container > .shower,
.quote_container > .hider {
.geary_quote_container > .shower,
.geary_quote_container > .hider {
position: absolute;
display: none;
left: 0;
@ -128,30 +128,30 @@ pre {
-webkit-user-drag: none;
}
.quote_container > .shower > input,
.quote_container > .hider > input {
.geary_quote_container > .shower > input,
.geary_quote_container > .hider > input {
width: 100%;
height: 16px;
padding: 0;
font-size: 8px; /* Absolute size in pixels for graphics */
color: #888;
}
.quote_container > .shower:hover > input,
.quote_container > .hider:hover > input {
.geary_quote_container > .shower:hover > input,
.geary_quote_container > .hider:hover > input {
color: #000;
}
.quote_container.controllable.hide > .hider {
.geary_quote_container.controllable.hide > .hider {
display: none;
}
.quote_container.controllable.hide > .shower,
.quote_container.controllable > .hider {
.geary_quote_container.controllable.hide > .shower,
.geary_quote_container.controllable > .hider {
display: block;
}
/* Highlight search terms */
.search_coloring *::selection {
.geary_search_coloring *::selection {
background-color: #00ddff;
}
}
@ -161,14 +161,14 @@ pre {
background-color: white !important;
}
#part_container {
#geary_part_container {
display: none !important;
}
#print_container {
#geary_print_container {
display: inline-block !important;
background-color: white !important;
}
#print_container .preview {
#geary_print_container .preview {
display: none !important;
}
}