ChatView: allow loading css style sheets

Change the webview creation process so that we can load style sheets
from resources. In the future, we can load large CSS libraries without
having to include them in the HTML file.

Tuleap: #1073
Change-Id: Ie3a8cfd53b120c9f61a2dd3c33abb620817726c6
diff --git a/src/webkitchatcontainer.cpp b/src/webkitchatcontainer.cpp
index f72afdc..a968cd5 100644
--- a/src/webkitchatcontainer.cpp
+++ b/src/webkitchatcontainer.cpp
@@ -53,6 +53,7 @@
 struct _WebKitChatContainerPrivate
 {
     GtkWidget* webview_chat;
+    GtkWidget* box_webview_chat;
 
     bool       chatview_debug;
 
@@ -90,14 +91,10 @@
 {
     G_OBJECT_CLASS(klass)->dispose = webkit_chat_container_dispose;
 
-    /* This must be called at least once before we render chatview.ui
-     * in order to allow us to use WebKitWebView in the template file. */
-    webkit_web_view_get_type();
-
     gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
                                                 "/cx/ring/RingGnome/webkitchatcontainer.ui");
 
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), WebKitChatContainer, webview_chat);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), WebKitChatContainer, box_webview_chat);
 
     /* add signals */
     webkit_chat_container_signals[READY] = g_signal_new("ready",
@@ -349,6 +346,26 @@
         priv->chatview_debug = TRUE;
     }
 
+    /* Prepare WebKitUserContentManager */
+    WebKitUserContentManager* webkit_content_manager = webkit_user_content_manager_new();
+
+    WebKitUserStyleSheet* chatview_style_sheet = webkit_user_style_sheet_new(
+        (gchar*) g_bytes_get_data(
+            g_resources_lookup_data(
+                "/cx/ring/RingGnome/chatview.css",
+                G_RESOURCE_LOOKUP_FLAGS_NONE,
+                NULL
+            ),
+            NULL
+        ),
+        WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+        WEBKIT_USER_STYLE_LEVEL_USER,
+        NULL,
+        NULL
+    );
+    webkit_user_content_manager_add_style_sheet(webkit_content_manager, chatview_style_sheet);
+
+    /* Prepare WebKitSettings */
     WebKitSettings* webkit_settings = webkit_settings_new_with_settings(
         "enable-javascript", TRUE,
         "enable-developer-extras", priv->chatview_debug,
@@ -358,6 +375,20 @@
         "enable-smooth-scrolling", TRUE,
         NULL
     );
+
+    /* Create the WebKitWebView */
+    priv->webview_chat = GTK_WIDGET(
+        webkit_web_view_new_with_user_content_manager(
+            webkit_content_manager
+        )
+    );
+
+    gtk_container_add(GTK_CONTAINER(priv->box_webview_chat), priv->webview_chat);
+    gtk_widget_show(priv->webview_chat);
+    gtk_widget_set_vexpand(GTK_WIDGET(priv->webview_chat), TRUE);
+    gtk_widget_set_hexpand(GTK_WIDGET(priv->webview_chat), TRUE);
+
+    /* Set the WebKitSettings */
     webkit_web_view_set_settings(WEBKIT_WEB_VIEW(priv->webview_chat), webkit_settings);
 
     g_signal_connect(priv->webview_chat, "load-changed", G_CALLBACK(webview_chat_load_changed), view);
diff --git a/ui/webkitchatcontainer.ui b/ui/webkitchatcontainer.ui
index 9a401b9..63bc5bc 100644
--- a/ui/webkitchatcontainer.ui
+++ b/ui/webkitchatcontainer.ui
@@ -3,16 +3,11 @@
   <requires lib="gtk+" version="3.10"/>
   <template class="WebKitChatContainer" parent="GtkBox">
     <child>
-      <object class="GtkBox">
+      <object class="GtkBox" id="box_webview_chat">
         <property name="visible">True</property>
         <property name="orientation">vertical</property>
-        <child>
-          <object class="WebKitWebView" id="webview_chat">
-            <property name="visible">True</property>
-            <property name="hexpand">True</property>
-            <property name="vexpand">True</property>
-          </object>
-        </child>
+        <property name="hexpand">True</property>
+        <property name="vexpand">True</property>
       </object>
     </child>
   </template>
diff --git a/web/chatview.css b/web/chatview.css
new file mode 100644
index 0000000..1d0c503
--- /dev/null
+++ b/web/chatview.css
@@ -0,0 +1,249 @@
+.status_circle {
+  fill: #A0A0A0;
+  -webkit-animation: circle-dance;
+  -webkit-animation-duration: 0.8s;
+  -webkit-animation-iteration-count: infinite;
+  -webkit-animation-direction: alternate;
+  -webkit-animation-timing-function: ease-in-out;
+}
+
+.anim-first {
+  -webkit-animation-delay: 0.7s;
+}
+
+.anim-second {
+  -webkit-animation-delay: 0.9s;
+}
+
+.anim-third {
+  -webkit-animation-delay: 1.1s;
+}
+
+@-webkit-keyframes circle-dance {
+  0%,50% {
+    -webkit-transform: translateY(0);
+    fill: #A0A0A0;
+  }
+  100% {
+    -webkit-transform: translateY(-8px);
+    fill: #000;
+  }
+}
+
+.status-check {
+  stroke-dasharray: 17;
+  stroke-dashoffset: 17;
+  -webkit-animation: dash-check;
+  -webkit-animation-duration: 0.4s;
+  -webkit-animation-delay: 0.7s;
+  -webkit-animation-fill-mode: forwards;
+  -webkit-animation-timing-function: ease-in-out;
+}
+
+@-webkit-keyframes dash-check{
+  from {
+    stroke-dashoffset: 17;
+  }
+  to {
+    stroke-dashoffset: 0;
+  }
+}
+
+.status-x {
+  stroke-dasharray: 12;
+  stroke-dashoffset: 12;
+  -webkit-animation: dash-x;
+  -webkit-animation-duration: 0.2s;
+  -webkit-animation-fill-mode: forwards;
+  -webkit-animation-timing-function: ease-in-out;
+}
+
+.x-first {
+  -webkit-animation-delay: 0.7s;
+}
+
+.x-second {
+  -webkit-animation-delay: 0.9s;
+}
+
+@-webkit-keyframes dash-x{
+  from {
+    stroke-dashoffset: 12;
+  }
+  to {
+    stroke-dashoffset: 0;
+  }
+}
+
+.message_wrapper {
+    transform: scale(0);
+    max-width: 50%;
+    margin: 10px 0;
+    display: inline-block;
+    padding: 10px 10px 10px 10px;
+    border-radius: 10px;
+    position: relative;
+    -webkit-animation: talk;
+    -webkit-animation-duration: 0.3s;
+    -webkit-animation-delay: 0.1s;
+    -webkit-animation-fill-mode: forwards;
+    -webkit-animation-timing-function: ease-in-out;
+}
+
+.message {
+    font: 0.875em "Open sans",Helvetica,"Segoe UI",sans-serif;
+    margin: 0;
+    display: flex;
+    justify-content: flex-start;
+    align-items: top;
+    overflow: hidden;
+}
+
+.message_out {
+    flex-direction: row-reverse;
+}
+
+.message_sender {
+    display: none;
+}
+
+.sender_image {
+    border-radius: 50%;
+    transition: transform 0.2s ease-in-out;
+    margin: 10px 10px 10px 10px;
+}
+
+.message_in .sender_image {
+    -webkit-animation-name: enter-stage-left;
+    -webkit-animation-duration: 0.2s;
+    -webkit-animation-timing-function: ease-in-out;
+}
+
+.message_out .sender_image {
+    -webkit-animation-name: enter-stage-right;
+    -webkit-animation-duration: 0.2s;
+    -webkit-animation-timing-function: ease-in-out;
+}
+
+@-webkit-keyframes enter-stage-left {
+    from {
+      transform: translateX(-100%);
+    }
+    to {
+      transform: translateX(0);
+    }
+}
+
+@-webkit-keyframes enter-stage-right {
+    from {
+      transform: translateX(100%);
+    }
+    to {
+      transform: translateX(0);
+    }
+}
+
+.message_out > .message_wrapper {
+    background-color: #00BFD3;
+    color: #fff;
+    border-top-right-radius: 0;
+    transform-origin: top right;
+}
+
+.message_out + .message_out > .message_wrapper {
+    border-top-right-radius: 10px;
+}
+
+.message_in > .message_wrapper {
+    background-color: #DEDEE0;
+    border-top-left-radius: 0;
+    transform-origin: top left;
+}
+
+.message_in + .message_in > .message_wrapper {
+    border-top-left-radius: 10px;
+}
+
+.message_timestamp {
+    font-size: 0.8em;
+    margin-top: 0.3em;
+    opacity: 1;
+    transition: opacity 0.5s ease-in-out;
+    display: block;
+}
+
+.message_in .message_timestamp {
+    color: #9E9E9E;
+}
+
+@-webkit-keyframes fade-in {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+}
+
+.message_text {
+    word-wrap: break-word;
+}
+
+@-webkit-keyframes talk {
+    from {
+      transform: scale(0);
+    }
+    to {
+      transform: scale(1);
+    }
+}
+
+.message_in > .message_wrapper:before {
+    content: "";
+    width: 10px;
+    height: 10px;
+    position: absolute;
+    left: -10px;
+    top: 0;
+    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M10,0 L0,0 C10,0 10,10 10,10' fill='%23DEDEE0'/%3E %3C/svg%3E");
+    transform-origin: top left;
+}
+
+.message_in + .message_in > .message_wrapper:before {
+    display: none;
+}
+
+.message_out + .message_out > .message_wrapper:after {
+    display: none;
+}
+
+.message_out > .message_wrapper:after {
+    content: "";
+    width: 10px;
+    height: 10px;
+    position: absolute;
+    right: -10px;
+    top: 0;
+    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M0,0 L10,0 C0,0 0,10 0,10' fill='%2300BFD3'/%3E %3C/svg%3E");
+    transform-origin: top right;
+}
+
+.message_delivery_status {
+    margin: 10px 10px;
+    color: #A0A0A0;
+    opacity: 0;
+    -webkit-animation-name: fade-in;
+    -webkit-animation-duration: 0.2s;
+    -webkit-animation-timing-function: ease-in-out;
+    -webkit-animation-delay: 0.4s;
+    -webkit-animation-fill-mode: forwards;
+    transition: opacity 0.5s ease-in-out;
+}
+
+.message_in + .message_in .sender_image {
+    opacity: 0;
+}
+
+.message_out + .message_out .sender_image {
+    opacity: 0;
+}
diff --git a/web/chatview.html b/web/chatview.html
index 151870f..dff86f1 100644
--- a/web/chatview.html
+++ b/web/chatview.html
@@ -20,259 +20,6 @@
 <script src="https://soapbox.github.io/linkifyjs/js/linkify/linkify-html.min.js"></script>
 -->
 
-<style>
-.status_circle {
-  fill: #A0A0A0;
-  -webkit-animation: circle-dance;
-  -webkit-animation-duration: 0.8s;
-  -webkit-animation-iteration-count: infinite;
-  -webkit-animation-direction: alternate;
-  -webkit-animation-timing-function: ease-in-out;
-}
-
-.anim-first {
-  -webkit-animation-delay: 0.7s;
-}
-
-.anim-second {
-  -webkit-animation-delay: 0.9s;
-}
-
-.anim-third {
-  -webkit-animation-delay: 1.1s;
-}
-
-@-webkit-keyframes circle-dance {
-  0%,50% {
-    -webkit-transform: translateY(0);
-    fill: #A0A0A0;
-  }
-  100% {
-    -webkit-transform: translateY(-8px);
-    fill: #000;
-  }
-}
-
-.status-check {
-  stroke-dasharray: 17;
-  stroke-dashoffset: 17;
-  -webkit-animation: dash-check;
-  -webkit-animation-duration: 0.4s;
-  -webkit-animation-delay: 0.7s;
-  -webkit-animation-fill-mode: forwards;
-  -webkit-animation-timing-function: ease-in-out;
-}
-
-@-webkit-keyframes dash-check{
-  from {
-    stroke-dashoffset: 17;
-  }
-  to {
-    stroke-dashoffset: 0;
-  }
-}
-
-.status-x {
-  stroke-dasharray: 12;
-  stroke-dashoffset: 12;
-  -webkit-animation: dash-x;
-  -webkit-animation-duration: 0.2s;
-  -webkit-animation-fill-mode: forwards;
-  -webkit-animation-timing-function: ease-in-out;
-}
-
-.x-first {
-  -webkit-animation-delay: 0.7s;
-}
-
-.x-second {
-  -webkit-animation-delay: 0.9s;
-}
-
-@-webkit-keyframes dash-x{
-  from {
-    stroke-dashoffset: 12;
-  }
-  to {
-    stroke-dashoffset: 0;
-  }
-}
-
-.message_wrapper {
-    transform: scale(0);
-    max-width: 50%;
-    margin: 10px 0;
-    display: inline-block;
-    padding: 10px 10px 10px 10px;
-    border-radius: 10px;
-    position: relative;
-    -webkit-animation: talk;
-    -webkit-animation-duration: 0.3s;
-    -webkit-animation-delay: 0.1s;
-    -webkit-animation-fill-mode: forwards;
-    -webkit-animation-timing-function: ease-in-out;
-}
-
-.message {
-    font: 0.875em "Open sans",Helvetica,"Segoe UI",sans-serif;
-    margin: 0;
-    display: flex;
-    justify-content: flex-start;
-    align-items: top;
-    overflow: hidden;
-}
-
-.message_out {
-    flex-direction: row-reverse;
-}
-
-.message_sender {
-    display: none;
-}
-
-.sender_image {
-    border-radius: 50%;
-    transition: transform 0.2s ease-in-out;
-    margin: 10px 10px 10px 10px;
-}
-
-.message_in .sender_image {
-    -webkit-animation-name: enter-stage-left;
-    -webkit-animation-duration: 0.2s;
-    -webkit-animation-timing-function: ease-in-out;
-}
-
-.message_out .sender_image {
-    -webkit-animation-name: enter-stage-right;
-    -webkit-animation-duration: 0.2s;
-    -webkit-animation-timing-function: ease-in-out;
-}
-
-@-webkit-keyframes enter-stage-left {
-    from {
-      transform: translateX(-100%);
-    }
-    to {
-      transform: translateX(0);
-    }
-}
-
-@-webkit-keyframes enter-stage-right {
-    from {
-      transform: translateX(100%);
-    }
-    to {
-      transform: translateX(0);
-    }
-}
-
-.message_out > .message_wrapper {
-    background-color: #00BFD3;
-    color: #fff;
-    border-top-right-radius: 0;
-    transform-origin: top right;
-}
-
-.message_out + .message_out > .message_wrapper {
-    border-top-right-radius: 10px;
-}
-
-.message_in > .message_wrapper {
-    background-color: #DEDEE0;
-    border-top-left-radius: 0;
-    transform-origin: top left;
-}
-
-.message_in + .message_in > .message_wrapper {
-    border-top-left-radius: 10px;
-}
-
-.message_timestamp {
-    font-size: 0.8em;
-    margin-top: 0.3em;
-    opacity: 1;
-    transition: opacity 0.5s ease-in-out;
-    display: block;
-}
-
-.message_in .message_timestamp {
-    color: #9E9E9E;
-}
-
-@-webkit-keyframes fade-in {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-}
-
-.message_text {
-    word-wrap: break-word;
-}
-
-@-webkit-keyframes talk {
-    from {
-      transform: scale(0);
-    }
-    to {
-      transform: scale(1);
-    }
-}
-
-.message_in > .message_wrapper:before {
-    content: "";
-    width: 10px;
-    height: 10px;
-    position: absolute;
-    left: -10px;
-    top: 0;
-    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M10,0 L0,0 C10,0 10,10 10,10' fill='%23DEDEE0'/%3E %3C/svg%3E");
-    transform-origin: top left;
-}
-
-.message_in + .message_in > .message_wrapper:before {
-    display: none;
-}
-
-.message_out + .message_out > .message_wrapper:after {
-    display: none;
-}
-
-.message_out > .message_wrapper:after {
-    content: "";
-    width: 10px;
-    height: 10px;
-    position: absolute;
-    right: -10px;
-    top: 0;
-    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E %3Cpath d= 'M0,0 L10,0 C0,0 0,10 0,10' fill='%2300BFD3'/%3E %3C/svg%3E");
-    transform-origin: top right;
-}
-
-.message_delivery_status {
-    margin: 10px 10px;
-    color: #A0A0A0;
-    opacity: 0;
-    -webkit-animation-name: fade-in;
-    -webkit-animation-duration: 0.2s;
-    -webkit-animation-timing-function: ease-in-out;
-    -webkit-animation-delay: 0.4s;
-    -webkit-animation-fill-mode: forwards;
-    transition: opacity 0.5s ease-in-out;
-}
-
-.message_in + .message_in .sender_image {
-    opacity: 0;
-}
-
-.message_out + .message_out .sender_image {
-    opacity: 0;
-}
-
-</style>
-
 <script>
 var ring = {}; // ring namespace
 
diff --git a/web/web.gresource.xml b/web/web.gresource.xml
index 33ab294..8749de3 100644
--- a/web/web.gresource.xml
+++ b/web/web.gresource.xml
@@ -10,5 +10,8 @@
       <file>linkify-string.js</file>
       <file>linkify-html.js</file>
       <file>linkify-jquery.js</file>
+
+      <!-- CSS -->
+      <file>chatview.css</file>
     </gresource>
   </gresources>