LCOV - code coverage report
Current view: top level - widget/gtk - nsAppShell.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 70 129 54.3 %
Date: 2017-07-14 16:53:18 Functions: 5 12 41.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim:expandtab:shiftwidth=4:tabstop=4:
       3             :  */
       4             : /* This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include <sys/types.h>
       9             : #include <unistd.h>
      10             : #include <fcntl.h>
      11             : #include <errno.h>
      12             : #include <gdk/gdk.h>
      13             : #include "nsAppShell.h"
      14             : #include "nsWindow.h"
      15             : #include "mozilla/Logging.h"
      16             : #include "prenv.h"
      17             : #include "mozilla/HangMonitor.h"
      18             : #include "mozilla/Unused.h"
      19             : #include "GeckoProfiler.h"
      20             : #include "nsIPowerManagerService.h"
      21             : #ifdef MOZ_ENABLE_DBUS
      22             : #include "WakeLockListener.h"
      23             : #endif
      24             : #include "gfxPlatform.h"
      25             : #include "ScreenHelperGTK.h"
      26             : #include "HeadlessScreenHelper.h"
      27             : #include "mozilla/widget/ScreenManager.h"
      28             : 
      29             : using mozilla::Unused;
      30             : using mozilla::widget::ScreenHelperGTK;
      31             : using mozilla::widget::HeadlessScreenHelper;
      32             : using mozilla::widget::ScreenManager;
      33             : using mozilla::LazyLogModule;
      34             : 
      35             : #define NOTIFY_TOKEN 0xFA
      36             : 
      37             : LazyLogModule gWidgetLog("Widget");
      38             : LazyLogModule gWidgetFocusLog("WidgetFocus");
      39             : LazyLogModule gWidgetDragLog("WidgetDrag");
      40             : LazyLogModule gWidgetDrawLog("WidgetDraw");
      41             : 
      42             : static GPollFunc sPollFunc;
      43             : 
      44             : // Wrapper function to disable hang monitoring while waiting in poll().
      45             : static gint
      46        1311 : PollWrapper(GPollFD *ufds, guint nfsd, gint timeout_)
      47             : {
      48        1311 :     mozilla::HangMonitor::Suspend();
      49        1311 :     profiler_thread_sleep();
      50        1311 :     gint result = (*sPollFunc)(ufds, nfsd, timeout_);
      51        1311 :     profiler_thread_wake();
      52        1311 :     mozilla::HangMonitor::NotifyActivity();
      53        1311 :     return result;
      54             : }
      55             : 
      56             : #if MOZ_WIDGET_GTK == 3
      57             : // For bug 726483.
      58             : static decltype(GtkContainerClass::check_resize) sReal_gtk_window_check_resize;
      59             : 
      60             : static void
      61           0 : wrap_gtk_window_check_resize(GtkContainer *container)
      62             : {
      63           0 :     GdkWindow* gdk_window = gtk_widget_get_window(&container->widget);
      64           0 :     if (gdk_window) {
      65           0 :         g_object_ref(gdk_window);
      66             :     }
      67             : 
      68           0 :     sReal_gtk_window_check_resize(container);
      69             : 
      70           0 :     if (gdk_window) {
      71           0 :         g_object_unref(gdk_window);
      72             :     }
      73           0 : }
      74             : 
      75             : // Emit resume-events on GdkFrameClock if flush-events has not been
      76             : // balanced by resume-events at dispose.
      77             : // For https://bugzilla.gnome.org/show_bug.cgi?id=742636
      78             : static decltype(GObjectClass::constructed) sRealGdkFrameClockConstructed;
      79             : static decltype(GObjectClass::dispose) sRealGdkFrameClockDispose;
      80             : static GQuark sPendingResumeQuark;
      81             : 
      82             : static void
      83           0 : OnFlushEvents(GObject* clock, gpointer)
      84             : {
      85           0 :     g_object_set_qdata(clock, sPendingResumeQuark, GUINT_TO_POINTER(1));
      86           0 : }
      87             : 
      88             : static void
      89           0 : OnResumeEvents(GObject* clock, gpointer)
      90             : {
      91           0 :     g_object_set_qdata(clock, sPendingResumeQuark, nullptr);
      92           0 : }
      93             : 
      94             : static void
      95           0 : WrapGdkFrameClockConstructed(GObject* object)
      96             : {
      97           0 :     sRealGdkFrameClockConstructed(object);
      98             : 
      99             :     g_signal_connect(object, "flush-events",
     100           0 :                      G_CALLBACK(OnFlushEvents), nullptr);
     101             :     g_signal_connect(object, "resume-events",
     102           0 :                      G_CALLBACK(OnResumeEvents), nullptr);
     103           0 : }
     104             : 
     105             : static void
     106           0 : WrapGdkFrameClockDispose(GObject* object)
     107             : {
     108           0 :     if (g_object_get_qdata(object, sPendingResumeQuark)) {
     109           0 :         g_signal_emit_by_name(object, "resume-events");
     110             :     }
     111             : 
     112           0 :     sRealGdkFrameClockDispose(object);
     113           0 : }
     114             : #endif
     115             : 
     116             : /*static*/ gboolean
     117         286 : nsAppShell::EventProcessorCallback(GIOChannel *source, 
     118             :                                    GIOCondition condition,
     119             :                                    gpointer data)
     120             : {
     121         286 :     nsAppShell *self = static_cast<nsAppShell *>(data);
     122             : 
     123             :     unsigned char c;
     124         286 :     Unused << read(self->mPipeFDs[0], &c, 1);
     125         286 :     NS_ASSERTION(c == (unsigned char) NOTIFY_TOKEN, "wrong token");
     126             : 
     127         286 :     self->NativeEventCallback();
     128         286 :     return TRUE;
     129             : }
     130             : 
     131           0 : nsAppShell::~nsAppShell()
     132             : {
     133           0 :     if (mTag)
     134           0 :         g_source_remove(mTag);
     135           0 :     if (mPipeFDs[0])
     136           0 :         close(mPipeFDs[0]);
     137           0 :     if (mPipeFDs[1])
     138           0 :         close(mPipeFDs[1]);
     139           0 : }
     140             : 
     141             : nsresult
     142           3 : nsAppShell::Init()
     143             : {
     144             :     // For any versions of Glib before 2.36, g_type_init must be explicitly called
     145             :     // to safely use the library. Failure to do so may cause various failures/crashes
     146             :     // in any code that uses Glib, Gdk, or Gtk. In later versions of Glib, this call
     147             :     // is a no-op.
     148           3 :     g_type_init();
     149             : 
     150             : #ifdef MOZ_ENABLE_DBUS
     151           3 :     if (XRE_IsParentProcess()) {
     152             :         nsCOMPtr<nsIPowerManagerService> powerManagerService =
     153           2 :             do_GetService(POWERMANAGERSERVICE_CONTRACTID);
     154             : 
     155           1 :         if (powerManagerService) {
     156           1 :             powerManagerService->AddWakeLockListener(WakeLockListener::GetSingleton());
     157             :         } else {
     158           0 :             NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
     159             :         }
     160             :     }
     161             : #endif
     162             : 
     163           3 :     if (!sPollFunc) {
     164           3 :         sPollFunc = g_main_context_get_poll_func(nullptr);
     165           3 :         g_main_context_set_poll_func(nullptr, &PollWrapper);
     166             :     }
     167             : 
     168           3 :     if (XRE_IsParentProcess()) {
     169           1 :         ScreenManager& screenManager = ScreenManager::GetSingleton();
     170           1 :         if (gfxPlatform::IsHeadless()) {
     171           0 :             screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
     172             :         } else {
     173           1 :             screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperGTK>());
     174             :         }
     175             :     }
     176             : 
     177             : #if MOZ_WIDGET_GTK == 3
     178           6 :     if (!sReal_gtk_window_check_resize &&
     179           3 :         gtk_check_version(3,8,0) != nullptr) { // GTK 3.0 to GTK 3.6.
     180             :         // GtkWindow is a static class and so will leak anyway but this ref
     181             :         // makes sure it isn't recreated.
     182           0 :         gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_WINDOW);
     183           0 :         auto check_resize = &GTK_CONTAINER_CLASS(gtk_plug_class)->check_resize;
     184           0 :         sReal_gtk_window_check_resize = *check_resize;
     185           0 :         *check_resize = wrap_gtk_window_check_resize;
     186             :     }
     187             : 
     188           6 :     if (!sPendingResumeQuark &&
     189           3 :         gtk_check_version(3,14,7) != nullptr) { // GTK 3.0 to GTK 3.14.7.
     190             :         // GTK 3.8 - 3.14 registered this type when creating the frame clock
     191             :         // for the root window of the display when the display was opened.
     192           0 :         GType gdkFrameClockIdleType = g_type_from_name("GdkFrameClockIdle");
     193           0 :         if (gdkFrameClockIdleType) { // not in versions prior to 3.8
     194           0 :             sPendingResumeQuark = g_quark_from_string("moz-resume-is-pending");
     195             :             auto gdk_frame_clock_idle_class =
     196           0 :                 G_OBJECT_CLASS(g_type_class_peek_static(gdkFrameClockIdleType));
     197           0 :             auto constructed = &gdk_frame_clock_idle_class->constructed;
     198           0 :             sRealGdkFrameClockConstructed = *constructed;
     199           0 :             *constructed = WrapGdkFrameClockConstructed;
     200           0 :             auto dispose = &gdk_frame_clock_idle_class->dispose;
     201           0 :             sRealGdkFrameClockDispose = *dispose;
     202           0 :             *dispose = WrapGdkFrameClockDispose;
     203             :         }
     204             :     }
     205             : 
     206             :     // Workaround for bug 1209659 which is fixed by Gtk3.20
     207           3 :     if (gtk_check_version(3, 20, 0) != nullptr)
     208           0 :         unsetenv("GTK_CSD");
     209             : #endif
     210             : 
     211           3 :     if (PR_GetEnv("MOZ_DEBUG_PAINTS"))
     212           0 :         gdk_window_set_debug_updates(TRUE);
     213             : 
     214             :     // Whitelist of only common, stable formats - see bugs 1197059 and 1203078
     215           3 :     GSList* pixbufFormats = gdk_pixbuf_get_formats();
     216          48 :     for (GSList* iter = pixbufFormats; iter; iter = iter->next) {
     217          45 :         GdkPixbufFormat* format = static_cast<GdkPixbufFormat*>(iter->data);
     218          45 :         gchar* name = gdk_pixbuf_format_get_name(format);
     219          87 :         if (strcmp(name, "jpeg") &&
     220          81 :             strcmp(name, "png") &&
     221          75 :             strcmp(name, "gif") &&
     222          69 :             strcmp(name, "bmp") &&
     223          63 :             strcmp(name, "ico") &&
     224          57 :             strcmp(name, "xpm") &&
     225          27 :             strcmp(name, "svg")) {
     226          24 :           gdk_pixbuf_format_set_disabled(format, TRUE);
     227             :         }
     228          45 :         g_free(name);
     229             :     }
     230           3 :     g_slist_free(pixbufFormats);
     231             : 
     232           3 :     int err = pipe(mPipeFDs);
     233           3 :     if (err)
     234           0 :         return NS_ERROR_OUT_OF_MEMORY;
     235             : 
     236             :     GIOChannel *ioc;
     237             :     GSource *source;
     238             : 
     239             :     // make the pipe nonblocking
     240             : 
     241           3 :     int flags = fcntl(mPipeFDs[0], F_GETFL, 0);
     242           3 :     if (flags == -1)
     243           0 :         goto failed;
     244           3 :     err = fcntl(mPipeFDs[0], F_SETFL, flags | O_NONBLOCK);
     245           3 :     if (err == -1)
     246           0 :         goto failed;
     247           3 :     flags = fcntl(mPipeFDs[1], F_GETFL, 0);
     248           3 :     if (flags == -1)
     249           0 :         goto failed;
     250           3 :     err = fcntl(mPipeFDs[1], F_SETFL, flags | O_NONBLOCK);
     251           3 :     if (err == -1)
     252           0 :         goto failed;
     253             : 
     254           3 :     ioc = g_io_channel_unix_new(mPipeFDs[0]);
     255           3 :     source = g_io_create_watch(ioc, G_IO_IN);
     256           3 :     g_io_channel_unref(ioc);
     257           3 :     g_source_set_callback(source, (GSourceFunc)EventProcessorCallback, this, nullptr);
     258           3 :     g_source_set_can_recurse(source, TRUE);
     259           3 :     mTag = g_source_attach(source, nullptr);
     260           3 :     g_source_unref(source);
     261             : 
     262           3 :     return nsBaseAppShell::Init();
     263             : failed:
     264           0 :     close(mPipeFDs[0]);
     265           0 :     close(mPipeFDs[1]);
     266           0 :     mPipeFDs[0] = mPipeFDs[1] = 0;
     267           0 :     return NS_ERROR_FAILURE;
     268             : }
     269             : 
     270             : void
     271         289 : nsAppShell::ScheduleNativeEventCallback()
     272             : {
     273         289 :     unsigned char buf[] = { NOTIFY_TOKEN };
     274         289 :     Unused << write(mPipeFDs[1], buf, 1);
     275         289 : }
     276             : 
     277             : bool
     278        1314 : nsAppShell::ProcessNextNativeEvent(bool mayWait)
     279             : {
     280        1314 :     return g_main_context_iteration(nullptr, mayWait);
     281             : }

Generated by: LCOV version 1.13