Implement GetWindowList() on X11

This change implements GetWindowList() on X11. WindowCapturerLinux and
GetWindowUnderPoint() can share the logic of this function.

Bug: webrtc:7950
Change-Id: Ida746840d6f51d31e0470e5ae4955b6f5a4cfaf2
Reviewed-on: https://chromium-review.googlesource.com/606560
Reviewed-by: Jamie Walch <jamiewalch@chromium.org>
Commit-Queue: Zijie He <zijiehe@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#19314}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: ad501d19881fd06a622980fd1d27f95ba89f3348
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index 3b025c5..019f39d 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -264,6 +264,10 @@
       "window_capturer_x11.cc",
       "x11/shared_x_display.cc",
       "x11/shared_x_display.h",
+      "x11/window_list_utils.cc",
+      "x11/window_list_utils.h",
+      "x11/x_atom_cache.cc",
+      "x11/x_atom_cache.h",
       "x11/x_error_trap.cc",
       "x11/x_error_trap.h",
       "x11/x_server_pixel_buffer.cc",
diff --git a/modules/desktop_capture/mac/window_list_utils.h b/modules/desktop_capture/mac/window_list_utils.h
index 6492315..a3cd507 100644
--- a/modules/desktop_capture/mac/window_list_utils.h
+++ b/modules/desktop_capture/mac/window_list_utils.h
@@ -8,8 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_
-#define WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
 
 #include <ApplicationServices/ApplicationServices.h>
 
@@ -24,8 +24,8 @@
 // Iterates all on-screen windows in decreasing z-order and sends them
 // one-by-one to |on_window| function. If |on_window| returns false, this
 // function returns immediately. GetWindowList() returns false if native APIs
-// failed. Menus, dock, minimized windows and any windows which do not have a
-// valid window id or title will be ignored.
+// failed. Menus, dock, minimized windows (if |ignore_minimized| is true) and
+// any windows which do not have a valid window id or title will be ignored.
 bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
                    bool ignore_minimized);
 
@@ -56,4 +56,4 @@
 
 }  // namespace webrtc
 
-#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_WINDOW_LIST_UTILS_H_
+#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
diff --git a/modules/desktop_capture/window_capturer_x11.cc b/modules/desktop_capture/window_capturer_x11.cc
index 28f5b96..eb4ece1 100644
--- a/modules/desktop_capture/window_capturer_x11.cc
+++ b/modules/desktop_capture/window_capturer_x11.cc
@@ -8,21 +8,20 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include <assert.h>
-#include <string.h>
-#include <X11/Xatom.h>
 #include <X11/extensions/Xcomposite.h>
 #include <X11/extensions/Xrender.h>
 #include <X11/Xutil.h>
 
-#include <algorithm>
+#include <utility>
 
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_capturer.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
-#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
+#include "webrtc/modules/desktop_capture/x11/window_list_utils.h"
+#include "webrtc/modules/desktop_capture/x11/x_atom_cache.h"
 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
+#include "webrtc/rtc_base/checks.h"
 #include "webrtc/rtc_base/constructormagic.h"
 #include "webrtc/rtc_base/logging.h"
 #include "webrtc/rtc_base/scoped_ref_ptr.h"
@@ -31,56 +30,6 @@
 
 namespace {
 
-// Convenience wrapper for XGetWindowProperty() results.
-template <class PropertyType>
-class XWindowProperty {
- public:
-  XWindowProperty(Display* display, Window window, Atom property) {
-    const int kBitsPerByte = 8;
-    Atom actual_type;
-    int actual_format;
-    unsigned long bytes_after;  // NOLINT: type required by XGetWindowProperty
-    int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
-                                    AnyPropertyType, &actual_type,
-                                    &actual_format, &size_,
-                                    &bytes_after, &data_);
-    if (status != Success) {
-      data_ = nullptr;
-      return;
-    }
-    if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
-      size_ = 0;
-      return;
-    }
-
-    is_valid_ = true;
-  }
-
-  ~XWindowProperty() {
-    if (data_)
-      XFree(data_);
-  }
-
-  // True if we got properly value successfully.
-  bool is_valid() const { return is_valid_; }
-
-  // Size and value of the property.
-  size_t size() const { return size_; }
-  const PropertyType* data() const {
-    return reinterpret_cast<PropertyType*>(data_);
-  }
-  PropertyType* data() {
-    return reinterpret_cast<PropertyType*>(data_);
-  }
-
- private:
-  bool is_valid_ = false;
-  unsigned long size_ = 0;  // NOLINT: type required by XGetWindowProperty
-  unsigned char* data_ = nullptr;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
-};
-
 class WindowCapturerLinux : public DesktopCapturer,
                             public SharedXDisplay::XEventHandler {
  public:
@@ -100,43 +49,25 @@
  private:
   Display* display() { return x_display_->display(); }
 
-  // Iterates through |window| hierarchy to find first visible window, i.e. one
-  // that has WM_STATE property set to NormalState.
-  // See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
-  ::Window GetApplicationWindow(::Window window);
-
-  // Returns true if the |window| is a desktop element.
-  bool IsDesktopElement(::Window window);
-
   // Returns window title for the specified X |window|.
   bool GetWindowTitle(::Window window, std::string* title);
 
-  // Return WM_STATE property of the |window|.
-  int32_t GetWindowState(::Window window);
-
   Callback* callback_ = nullptr;
 
   rtc::scoped_refptr<SharedXDisplay> x_display_;
 
-  Atom wm_state_atom_;
-  Atom window_type_atom_;
-  Atom normal_window_type_atom_;
   bool has_composite_extension_ = false;
 
   ::Window selected_window_ = 0;
   XServerPixelBuffer x_server_pixel_buffer_;
+  XAtomCache atom_cache_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux);
 };
 
 WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options)
-    : x_display_(options.x_display()) {
-  // Create Atoms so we don't need to do it every time they are used.
-  wm_state_atom_ = XInternAtom(display(), "WM_STATE", True);
-  window_type_atom_ = XInternAtom(display(), "_NET_WM_WINDOW_TYPE", True);
-  normal_window_type_atom_ = XInternAtom(
-      display(), "_NET_WM_WINDOW_TYPE_NORMAL", True);
-
+    : x_display_(options.x_display()),
+      atom_cache_(display()) {
   int event_base, error_base, major_version, minor_version;
   if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
       XCompositeQueryVersion(display(), &major_version, &minor_version) &&
@@ -155,43 +86,15 @@
 }
 
 bool WindowCapturerLinux::GetSourceList(SourceList* sources) {
-  SourceList result;
-
-  XErrorTrap error_trap(display());
-
-  int num_screens = XScreenCount(display());
-  for (int screen = 0; screen < num_screens; ++screen) {
-    ::Window root_window = XRootWindow(display(), screen);
-    ::Window parent;
-    ::Window *children;
-    unsigned int num_children;
-    int status = XQueryTree(display(), root_window, &root_window, &parent,
-                            &children, &num_children);
-    if (status == 0) {
-      LOG(LS_ERROR) << "Failed to query for child windows for screen "
-                    << screen;
-      continue;
-    }
-
-    for (unsigned int i = 0; i < num_children; ++i) {
-      // Iterate in reverse order to return windows from front to back.
-      ::Window app_window =
-          GetApplicationWindow(children[num_children - 1 - i]);
-      if (app_window && !IsDesktopElement(app_window)) {
-        Source w;
-        w.id = app_window;
-        if (GetWindowTitle(app_window, &w.title))
-          result.push_back(w);
-      }
-    }
-
-    if (children)
-      XFree(children);
-  }
-
-  sources->swap(result);
-
-  return true;
+  return GetWindowList(&atom_cache_,
+                       [this, sources](::Window window) {
+                         Source w;
+                         w.id = window;
+                         if (this->GetWindowTitle(window, &w.title)) {
+                           sources->push_back(w);
+                         }
+                         return true;
+                       });
 }
 
 bool WindowCapturerLinux::SelectSource(SourceId id) {
@@ -265,8 +168,8 @@
 }
 
 void WindowCapturerLinux::Start(Callback* callback) {
-  assert(!callback_);
-  assert(callback);
+  RTC_DCHECK(!callback_);
+  RTC_DCHECK(callback);
 
   callback_ = callback;
 }
@@ -289,7 +192,7 @@
     return;
   }
 
-  if (GetWindowState(selected_window_) == IconicState) {
+  if (GetWindowState(&atom_cache_, selected_window_) == IconicState) {
     // Window is in minimized. Return a 1x1 frame as same as OSX/Win does.
     std::unique_ptr<DesktopFrame> frame(
         new BasicDesktopFrame(DesktopSize(1, 1)));
@@ -327,75 +230,6 @@
   return false;
 }
 
-// TODO(zijiehe): This function should return the ancestor window of |window|
-// other than the root_window.
-::Window WindowCapturerLinux::GetApplicationWindow(::Window window) {
-  int32_t state = GetWindowState(window);
-  if (state == NormalState) {
-    // Window has WM_STATE==NormalState. Return it.
-    return window;
-  } else if (state == IconicState) {
-    // Window is in minimized. Skip it.
-    return 0;
-  }
-
-  // If the window is in WithdrawnState then look at all of its children.
-  ::Window root, parent;
-  ::Window *children;
-  unsigned int num_children;
-  if (!XQueryTree(display(), window, &root, &parent, &children,
-                  &num_children)) {
-    LOG(LS_ERROR) << "Failed to query for child windows although window"
-                  << "does not have a valid WM_STATE.";
-    return 0;
-  }
-  ::Window app_window = 0;
-  for (unsigned int i = 0; i < num_children; ++i) {
-    app_window = GetApplicationWindow(children[i]);
-    if (app_window)
-      break;
-  }
-
-  if (children)
-    XFree(children);
-  return app_window;
-}
-
-bool WindowCapturerLinux::IsDesktopElement(::Window window) {
-  if (window == 0)
-    return false;
-
-  // First look for _NET_WM_WINDOW_TYPE. The standard
-  // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
-  // says this hint *should* be present on all windows, and we use the existence
-  // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
-  // a desktop element (that is, only "normal" windows should be shareable).
-  XWindowProperty<uint32_t> window_type(display(), window, window_type_atom_);
-  if (window_type.is_valid() && window_type.size() > 0) {
-    uint32_t* end = window_type.data() + window_type.size();
-    bool is_normal = (end != std::find(
-        window_type.data(), end, normal_window_type_atom_));
-    return !is_normal;
-  }
-
-  // Fall back on using the hint.
-  XClassHint class_hint;
-  Status status = XGetClassHint(display(), window, &class_hint);
-  bool result = false;
-  if (status == 0) {
-    // No hints, assume this is a normal application window.
-    return result;
-  }
-
-  if (strcmp("gnome-panel", class_hint.res_name) == 0 ||
-      strcmp("desktop_window", class_hint.res_name) == 0) {
-    result = true;
-  }
-  XFree(class_hint.res_name);
-  XFree(class_hint.res_class);
-  return result;
-}
-
 bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) {
   int status;
   bool result = false;
@@ -425,14 +259,6 @@
   return result;
 }
 
-int32_t WindowCapturerLinux::GetWindowState(::Window window) {
-  // Get WM_STATE property of the window.
-  XWindowProperty<uint32_t> window_state(display(), window, wm_state_atom_);
-
-  // WM_STATE is considered to be set to WithdrawnState when it missing.
-  return window_state.is_valid() ? *window_state.data() : WithdrawnState;
-}
-
 }  // namespace
 
 // static
diff --git a/modules/desktop_capture/x11/window_list_utils.cc b/modules/desktop_capture/x11/window_list_utils.cc
new file mode 100644
index 0000000..1148acc
--- /dev/null
+++ b/modules/desktop_capture/x11/window_list_utils.cc
@@ -0,0 +1,219 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/x11/window_list_utils.h"
+
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#include <algorithm>
+
+#include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
+#include "webrtc/rtc_base/checks.h"
+#include "webrtc/rtc_base/constructormagic.h"
+#include "webrtc/rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+class DeferXFree {
+ public:
+  explicit DeferXFree(void* data) : data_(data) {}
+  ~DeferXFree();
+
+ private:
+  void* const data_;
+};
+
+DeferXFree::~DeferXFree() {
+  if (data_)
+    XFree(data_);
+}
+
+// Convenience wrapper for XGetWindowProperty() results.
+template <class PropertyType>
+class XWindowProperty {
+ public:
+  XWindowProperty(Display* display, Window window, Atom property) {
+    const int kBitsPerByte = 8;
+    Atom actual_type;
+    int actual_format;
+    unsigned long bytes_after;  // NOLINT: type required by XGetWindowProperty
+    int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
+                                    AnyPropertyType, &actual_type,
+                                    &actual_format, &size_,
+                                    &bytes_after, &data_);
+    if (status != Success) {
+      data_ = nullptr;
+      return;
+    }
+    if (sizeof(PropertyType) * kBitsPerByte != actual_format) {
+      size_ = 0;
+      return;
+    }
+
+    is_valid_ = true;
+  }
+
+  ~XWindowProperty() {
+    if (data_)
+      XFree(data_);
+  }
+
+  // True if we got properly value successfully.
+  bool is_valid() const { return is_valid_; }
+
+  // Size and value of the property.
+  size_t size() const { return size_; }
+  const PropertyType* data() const {
+    return reinterpret_cast<PropertyType*>(data_);
+  }
+  PropertyType* data() {
+    return reinterpret_cast<PropertyType*>(data_);
+  }
+
+ private:
+  bool is_valid_ = false;
+  unsigned long size_ = 0;  // NOLINT: type required by XGetWindowProperty
+  unsigned char* data_ = nullptr;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(XWindowProperty);
+};
+
+// Iterates through |window| hierarchy to find first visible window, i.e. one
+// that has WM_STATE property set to NormalState.
+// See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
+::Window GetApplicationWindow(XAtomCache* cache, ::Window window) {
+  int32_t state = GetWindowState(cache, window);
+  if (state == NormalState) {
+    // Window has WM_STATE==NormalState. Return it.
+    return window;
+  } else if (state == IconicState) {
+    // Window is in minimized. Skip it.
+    return 0;
+  }
+
+  RTC_DCHECK_EQ(state, WithdrawnState);
+  // If the window is in WithdrawnState then look at all of its children.
+  ::Window root, parent;
+  ::Window *children;
+  unsigned int num_children;
+  if (!XQueryTree(cache->display(), window, &root, &parent, &children,
+                  &num_children)) {
+    LOG(LS_ERROR) << "Failed to query for child windows although window"
+                  << "does not have a valid WM_STATE.";
+    return 0;
+  }
+  ::Window app_window = 0;
+  for (unsigned int i = 0; i < num_children; ++i) {
+    app_window = GetApplicationWindow(cache, children[i]);
+    if (app_window)
+      break;
+  }
+
+  if (children)
+    XFree(children);
+  return app_window;
+}
+
+// Returns true if the |window| is a desktop element.
+bool IsDesktopElement(XAtomCache* cache, ::Window window) {
+  RTC_DCHECK(cache);
+  if (window == 0)
+    return false;
+
+  // First look for _NET_WM_WINDOW_TYPE. The standard
+  // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
+  // says this hint *should* be present on all windows, and we use the existence
+  // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
+  // a desktop element (that is, only "normal" windows should be shareable).
+  XWindowProperty<uint32_t> window_type(
+      cache->display(), window, cache->WindowType());
+  if (window_type.is_valid() && window_type.size() > 0) {
+    uint32_t* end = window_type.data() + window_type.size();
+    bool is_normal = (end != std::find(
+        window_type.data(),
+        end,
+        cache->WindowTypeNormal()));
+    return !is_normal;
+  }
+
+  // Fall back on using the hint.
+  XClassHint class_hint;
+  Status status = XGetClassHint(cache->display(), window, &class_hint);
+  if (status == 0) {
+    // No hints, assume this is a normal application window.
+    return false;
+  }
+
+  DeferXFree free_res_name(class_hint.res_name);
+  DeferXFree free_res_class(class_hint.res_class);
+  return strcmp("gnome-panel", class_hint.res_name) == 0 ||
+         strcmp("desktop_window", class_hint.res_name) == 0;
+}
+
+}  // namespace
+
+int32_t GetWindowState(XAtomCache* cache, ::Window window) {
+  // Get WM_STATE property of the window.
+  XWindowProperty<uint32_t> window_state(
+      cache->display(), window, cache->WmState());
+
+  // WM_STATE is considered to be set to WithdrawnState when it missing.
+  return window_state.is_valid() ? *window_state.data() : WithdrawnState;
+}
+
+bool GetWindowList(XAtomCache* cache,
+                   rtc::FunctionView<bool(::Window)> on_window) {
+  RTC_DCHECK(cache);
+  RTC_DCHECK(on_window);
+  ::Display* const display = cache->display();
+  XErrorTrap error_trap(display);
+
+  int failed_screens = 0;
+  const int num_screens = XScreenCount(display);
+  for (int screen = 0; screen < num_screens; screen++) {
+    ::Window root_window = XRootWindow(display, screen);
+    ::Window parent;
+    ::Window* children;
+    unsigned int num_children;
+    if (XQueryTree(display,
+                   root_window,
+                   &root_window,
+                   &parent,
+                   &children,
+                   &num_children) == 0) {
+      failed_screens++;
+      LOG(LS_ERROR) << "Failed to query for child windows for screen "
+                    << screen;
+      continue;
+    }
+
+    DeferXFree free_children(children);
+
+    for (unsigned int i = 0; i < num_children; i++) {
+      // Iterates in reverse order to return windows from front to back.
+      ::Window app_window =
+          GetApplicationWindow(cache, children[num_children - 1 - i]);
+      if (app_window && !IsDesktopElement(cache, app_window)) {
+        if (!on_window(app_window)) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return failed_screens < num_screens;
+}
+
+}  // namespace webrtc
diff --git a/modules/desktop_capture/x11/window_list_utils.h b/modules/desktop_capture/x11/window_list_utils.h
new file mode 100644
index 0000000..8a64594
--- /dev/null
+++ b/modules/desktop_capture/x11/window_list_utils.h
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_WINDOW_LIST_UTILS_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_WINDOW_LIST_UTILS_H_
+
+#include <X11/Xlib.h>
+
+#include "webrtc/modules/desktop_capture/x11/x_atom_cache.h"
+#include "webrtc/rtc_base/function_view.h"
+
+namespace webrtc {
+
+// Synchronously iterates all on-screen windows in |cache|.display() in
+// decreasing z-order and sends them one-by-one to |on_window| function before
+// GetWindowList() returns. If |on_window| returns false, this function ignores
+// other windows and returns immediately. GetWindowList() returns false if
+// native APIs failed. If multiple screens are attached to the |display|, this
+// function returns false only when native APIs failed on all screens. Menus,
+// panels and minimized windows will be ignored.
+bool GetWindowList(XAtomCache* cache,
+                   rtc::FunctionView<bool(::Window)> on_window);
+
+// Returns WM_STATE property of the |window|. This function returns
+// WithdrawnState if the |window| is missing.
+int32_t GetWindowState(XAtomCache* cache, ::Window window);
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_X11_WINDOW_LIST_UTILS_H_
diff --git a/modules/desktop_capture/x11/x_atom_cache.cc b/modules/desktop_capture/x11/x_atom_cache.cc
new file mode 100644
index 0000000..161c8e9
--- /dev/null
+++ b/modules/desktop_capture/x11/x_atom_cache.cc
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/desktop_capture/x11/x_atom_cache.h"
+
+#include "webrtc/rtc_base/checks.h"
+
+namespace webrtc {
+
+XAtomCache::XAtomCache(::Display* display) : display_(display) {
+  RTC_DCHECK(display_);
+}
+
+XAtomCache::~XAtomCache() = default;
+
+::Display* XAtomCache::display() const {
+  return display_;
+}
+
+Atom XAtomCache::WmState() {
+  return CreateIfNotExist(&wm_state_, "WM_STATE");
+}
+
+Atom XAtomCache::WindowType() {
+  return CreateIfNotExist(&window_type_, "_NET_WM_WINDOW_TYPE");
+}
+
+Atom XAtomCache::WindowTypeNormal() {
+  return CreateIfNotExist(&window_type_normal_, "_NET_WM_WINDOW_TYPE_NORMAL");
+}
+
+Atom XAtomCache::CreateIfNotExist(Atom* atom, const char* name) {
+  RTC_DCHECK(atom);
+  if (*atom == None) {
+    *atom = XInternAtom(display(), name, True);
+  }
+  return *atom;
+}
+
+}  // namespace webrtc
diff --git a/modules/desktop_capture/x11/x_atom_cache.h b/modules/desktop_capture/x11/x_atom_cache.h
new file mode 100644
index 0000000..8ccf2cb
--- /dev/null
+++ b/modules/desktop_capture/x11/x_atom_cache.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ATOM_CACHE_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ATOM_CACHE_H_
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+namespace webrtc {
+
+// A cache of Atom. Each Atom object is created on demand.
+class XAtomCache final {
+ public:
+  explicit XAtomCache(::Display* display);
+  ~XAtomCache();
+
+  ::Display* display() const;
+
+  Atom WmState();
+  Atom WindowType();
+  Atom WindowTypeNormal();
+
+ private:
+  // If |*atom| is None, this function uses XInternAtom() to retrieve an Atom.
+  Atom CreateIfNotExist(Atom* atom, const char* name);
+
+  ::Display* const display_;
+  Atom wm_state_ = None;
+  Atom window_type_ = None;
+  Atom window_type_normal_ = None;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_X11_X_ATOM_CACHE_H_