~midori/midori/gtk3WebKit2only

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
 Copyright (C) 2007-2013 Christian Dywan <christian@twotoasts.de>
 Copyright (C) 2009 Jean-François Guchens <zcx000@gmail.com>
 Copyright (C) 2011 Peter Hatina <phatina@redhat.com>

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 See the file COPYING for the full license text.
*/

namespace Midori {
    public enum NewView {
        TAB,
        BACKGROUND,
        WINDOW,
    }
    /* Since: 0.1.2 */

    public enum Security {
        NONE, /* The connection is neither encrypted nor verified. */
        UNKNOWN, /* The security is unknown, due to lack of validation. */
        TRUSTED /* The security is validated and trusted. */
    }
    /* Since: 0.2.5 */

    [CCode (cprefix = "MIDORI_LOAD_")]
    public enum LoadStatus {
        FINISHED, /* The current website is fully loaded. */
        COMMITTED, /* Data is being loaded and rendered. */
        PROVISIONAL /* A new URI was scheduled. */
    }

    [CCode (cprefix = "MIDORI_LOAD_ERROR_")]
    public enum LoadError {
        NONE,
        DELAYED,
        SECURITY,
        CRASH,
        NETWORK
    }

    [DBus (name = "org.midori.Remote.tab")]
    public class TabRemote : Gtk.VBox {
        public uint64 id { get; protected set; }
        public string color { get; set; }

        construct {
            notify["id"].connect ((pspec) => {
                try {
                    Application.get_default ().get_dbus_connection ().register_object (
                        "/org/midori/Remote/tab/" + id.to_string (), this);
                } catch (IOError error) {
                    critical ("Failed to expose tab %s on DBus: %s".printf (id.to_string (), error.message));
                }
            });
        }
    }

    public class Tab : TabRemote {
        public Tab related { get; set construct; }
        public WebKit.WebView web_view { get; private set; }
        /* Native widget currently displayed.
           Since: 0.6.0 */
        Gtk.Widget? native_widget = null;
        public Gtk.Widget? widget { get {
            return native_widget;
        } set {
            if (value is Midori.Oops) {
                uri = (value as Midori.Oops).uri;
                this.set ("title", (value as Midori.Oops).title);
            }
            native_widget = value;
            web_view.hide ();
            (web_view.parent as Gtk.Box).pack_start (native_widget, true, true);
            native_widget.show ();
        } }

        private string current_uri = "about:blank";
        public string uri { get {
            return current_uri;
        }
        protected set {
            current_uri = Midori.URI.format_for_display (value);
            if (native_widget != null) {
                native_widget.hide ();
                (web_view.parent as Gtk.Box).remove (native_widget);
                native_widget = null;
                web_view.show ();
                notify_property ("widget");
            }
        }
        }

        /* Special is an error, blank or delayed page */
        public bool special { get; protected set; default = false; }
        /* Minimizing a tab indicates that only the icon should be shown.
           Since: 0.1.8 */
        public bool minimized { get; set; default = false; }
        /* Since: 0.4.8 */
        public string mime_type { get; protected set; default = "text/plain"; }
        /* Since: 0.1.2 */
        public Security security { get; protected set; default = Security.NONE; }
        public LoadStatus load_status { get; protected set; default = LoadStatus.FINISHED; }
        public LoadError load_error { get; protected set; default = LoadError.NONE; }
        public string? statusbar_text { get; protected set; default = null; }
        /* Since: 0.5.0 */

        public Gdk.Color? fg_color { get; protected set; default = null; }
        private Gdk.Color? bg_color_ = null;
        public Gdk.Color? bg_color { get {
            return bg_color_;
        } protected set {
            bg_color_ = value;
            colors_changed ();
        } }
        /* After fg_color and bg_color have changed.
           Since: 0.5.7 */
        public signal void colors_changed ();

        /* Special pages don't convey progress */
        private double current_progress = 0.0;
        public double progress { get {
            return special ? 0.0 : current_progress;
        }
        protected set {
            /* When we are finished, we don't want to *see* progress anymore */
            if (load_status == LoadStatus.FINISHED)
                current_progress = 0.0;
            /* Full progress but not finished: presumably all loaded */
            else if (value == 1.0)
                current_progress = 0.0;
            /* When loading we want to see at minimum 10% progress */
            else
                current_progress = value.clamp (0.1, 1.0);
        }
        }

        /* Emitted when a uri is attempted to be loaded.
           Returns FALSE if the URI could not be handled by Midori or any
           external application.
           Since: 0.5.8
         */
        public signal bool open_uri (string uri);
        /* Since: 0.5.8 */
        public signal bool navigation_requested (string uri);
        public signal void console_message (string message, int line, string source_id);
        public signal void attach_inspector (WebKit.WebView inspector_view);
        /* Emitted when an open inspector that was previously
           attached to the window is now detached again.
           Since: 0.3.4
         */
        public signal void detach_inspector (WebKit.WebView inspector_view);
        /* Allow the browser to provide the find bar */
        public signal void search_text (bool found, string typing);

       /* Since: 0.5.5 */
        public signal void context_menu (WebKit.HitTestResult hit_test_result, ContextAction menu);

        /* A dialog tab has a fixed size, limited GUI and is transient.
           Since: 0.5.6 */
        public bool is_dialog { get; protected set; }

        public bool is_blank () {
            return URI.is_blank (uri);
        }

        construct {
            orientation = Gtk.Orientation.VERTICAL;

            var content_manager = Addons.get_default ().content_manager;
            if (related != null)
                web_view = new Object (typeof (WebKit.WebView),
                    "related-view", related.web_view as WebKit.WebView,
                    "user-content-manager", content_manager) as WebKit.WebView;
            else
                web_view = new WebKit.WebView.with_user_content_manager (content_manager);
            /* Load something to avoid a bug where WebKit might not set a main frame */
            web_view.load_uri ("");

            id = web_view.get_page_id ();
            color = "";
            notify["color"].connect ((pspec)=> {
                if (!Gdk.Color.parse (color, out bg_color_))
                    bg_color_ = null;
                colors_changed ();
            });
        }

        public void inject_stylesheet (string stylesheet) {
#if !HAVE_WEBKIT2
            var dom = web_view.get_dom_document ();
            return_if_fail (dom.head != null);
            try {
                var style = dom.create_element ("style");
                style.set_attribute ("type", "text/css");
                style.append_child (dom.create_text_node (stylesheet));
                dom.head.append_child (style);
            }
            catch (Error error) {
                critical (_("Failed to inject stylesheet: %s"), error.message);
            }
#endif
        }

        /* Since: 0.5.1 */
        public bool view_source { get {
            return false;
        }
        set {
        }
        }

        public bool can_view_source () {
            if (view_source)
                return false;
            string content_type = ContentType.from_mime_type (mime_type);
#if HAVE_WIN32
            /* On Win32 text/plain maps to ".txt" but is_a expects "text" */
            string text_type = "text";
#else
            string text_type = ContentType.from_mime_type ("text/plain");
#endif
            return ContentType.is_a (content_type, text_type);
        }

        public static string get_display_title (string? title, string uri) {
            /* Render filename as title of patches */
            if (title == null && (uri.has_suffix (".diff") || uri.has_suffix (".patch")))
                return File.new_for_uri (uri).get_basename ();

            /* Work-around libSoup not setting a proper directory title */
            if (title == null || (title == "OMG!" && uri.has_prefix ("file://")))
                return Midori.URI.strip_prefix_for_display (uri);

#if !HAVE_WIN32
            /* If left-to-right text is combined with right-to-left text the default
               behaviour of Pango can result in awkwardly aligned text. For example
               "‪بستيان نوصر (hadess) | An era comes to an end - Midori" becomes
               "hadess) | An era comes to an end - Midori) بستيان نوصر". So to prevent
               this we insert an LRE character before the title which indicates that
               we want left-to-right but retains the direction of right-to-left text. */
            if (!title.has_prefix ("‪"))
                return "‪" + title;
#endif
            return title;
        }

        public static Pango.EllipsizeMode get_display_ellipsize (string title, string uri) {
            if (title == uri)
                return Pango.EllipsizeMode.START;

            if (title.has_suffix (".diff") || title.has_suffix (".patch"))
                return Pango.EllipsizeMode.START;

            string[] parts = title.split (" ");
            if (parts[0] != null && uri.has_suffix (parts[parts.length - 1].down ()))
                return Pango.EllipsizeMode.START;

            return Pango.EllipsizeMode.END;
        }

        /* Since: 0.4.3 */
        public bool can_save () {
            if (is_blank () || special)
                return false;
            if (view_source)
                return false;
            return true;
        }

        /*
          Saves the current website to @file.
          Since: 0.6.0
         */
        public async void save_to_file (File file, Cancellable? cancellable=null) {
            try {
                if (file.get_uri ().has_suffix (".mht"))
                    yield web_view.save_to_file (file, WebKit.SaveMode.MHTML, cancellable);
                else {
                    var output = yield file.replace_async (null, false,
                        FileCreateFlags.REPLACE_DESTINATION | FileCreateFlags.PRIVATE);
                    uint8[] data = yield web_view.get_main_resource ().get_data (cancellable);
                    var input = new MemoryInputStream.from_data (data, null);
                    yield output.splice_async (input,
                        OutputStreamSpliceFlags.CLOSE_TARGET);
                }
                file_saved (file, null);
            } catch (Error error) {
                if (!file_saved (file, error))
                    warning (_("Failed to save \"%s\" to disk: %s"), file.get_uri (), error.message);
            }
        }

        /*
          Emitted after saving a file using save_to_file.
          Return true to prevent the default error handling.
          Since: 0.6.0
         */
        public signal bool file_saved (File file, Error? error);

        public void stop_loading () {
            web_view.stop_loading ();
        }

        public bool can_go_forward () {
            return web_view.can_go_forward ();
        }

        public void go_forward () {
            web_view.go_forward ();
        }

        public void unmark_text_matches () {
#if !HAVE_WEBKIT2
            web_view.unmark_text_matches ();
#endif
        }

        public bool find (string text, bool case_sensitive, bool forward) {
            var controller = web_view.get_find_controller ();
            uint options = WebKit.FindOptions.WRAP_AROUND;
            if (!case_sensitive)
                options += WebKit.FindOptions.CASE_INSENSITIVE;
            if (!forward)
                options += WebKit.FindOptions.BACKWARDS;
            controller.search (text, options, 0);
            // FIXME: mark matches, count matches, not found
            return true;
        }

        /*
          Updates all editing actions with regard to text selection.

          Since: 0.5.8
         */
        public async void update_actions (Gtk.ActionGroup actions) {
            try {
                actions.get_action ("Undo").sensitive = yield web_view.can_execute_editing_command ("Undo", null);
                actions.get_action ("Redo").sensitive = yield web_view.can_execute_editing_command ("Redo", null);
                actions.get_action ("Cut").sensitive = yield web_view.can_execute_editing_command ("Cut", null);
                actions.get_action ("Copy").sensitive = yield web_view.can_execute_editing_command ("Copy", null);
                actions.get_action ("Paste").sensitive = yield web_view.can_execute_editing_command ("Paste", null);
                actions.get_action ("Delete").sensitive = yield web_view.can_execute_editing_command ("Cut", null);
                actions.get_action ("SelectAll").sensitive = yield web_view.can_execute_editing_command ("SelectAll", null);
            } catch (Error error) {
                critical ("Failed to update actions: %s", error.message);
            }
        }
    }
}