Update interfaces/layout-instability.idl and test (#23651)

Closes https://github.com/web-platform-tests/wpt/pull/22499
diff --git a/interfaces/layout-instability.idl b/interfaces/layout-instability.idl
index 0cd2262..3ee0afa 100644
--- a/interfaces/layout-instability.idl
+++ b/interfaces/layout-instability.idl
@@ -5,8 +5,16 @@
 
 [Exposed=Window]
 interface LayoutShift : PerformanceEntry {
-  readonly attribute long value;
+  readonly attribute double value;
   readonly attribute boolean hadRecentInput;
   readonly attribute DOMHighResTimeStamp lastInputTime;
+  readonly attribute FrozenArray<LayoutShiftAttribution> sources;
   [Default] object toJSON();
 };
+
+[Exposed=Window]
+interface LayoutShiftAttribution {
+  readonly attribute Node? node;
+  readonly attribute DOMRectReadOnly previousRect;
+  readonly attribute DOMRectReadOnly currentRect;
+};
diff --git a/layout-instability/idlharness.html b/layout-instability/idlharness.html
new file mode 100644
index 0000000..a3b2d1e
--- /dev/null
+++ b/layout-instability/idlharness.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<title>Layout Instability IDL tests</title>
+<meta name="timeout" content="long">
+<link rel="help" href="https://wicg.github.io/layout-instability/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+<script>
+'use strict';
+
+idl_test(
+  ['layout-instability'],
+  ['performance-timeline', 'geometry', 'dom', 'hr-time'],
+  async (idl_array, t) => {
+    idl_array.add_objects({
+      LayoutShift: ['layoutShift'],
+      LayoutShiftAttribution: ['layoutShiftAttribution'],
+    });
+
+    // If LayoutShift isn't supported, avoid the timeout below and just let the
+    // objects declared above be null. The tests will still fail, but we will
+    // consistently generate the same set of subtests on all platforms.
+    if (!PerformanceObserver ||
+        !PerformanceObserver.supportedEntryTypes ||
+        !PerformanceObserver.supportedEntryTypes.includes('layout-shift')) {
+      return;
+    }
+
+    self.layoutShift = await new Promise((resolve, reject) => {
+      const observer = new PerformanceObserver(entryList => {
+        resolve(entryList.getEntries()[0]);
+      });
+      observer.observe({type: 'layout-shift', buffered: true});
+      t.step_timeout(() => reject('Timed out waiting for LayoutShift entry'), 3000);
+
+      // Move the image, to cause layout shift.
+      image.style.marginTop = '100px';
+    });
+    self.layoutShiftAttribution = layoutShift.sources[0];
+  }
+);
+</script>
+
+<img id="image" src="/images/green-100x50.png">
diff --git a/layout-instability/idlharness.window.js b/layout-instability/idlharness.window.js
deleted file mode 100644
index 7d97446..0000000
--- a/layout-instability/idlharness.window.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// META: script=/resources/WebIDLParser.js
-// META: script=/resources/idlharness.js
-
-// https://wicg.github.io/layout-instability/
-
-'use strict';
-
-idl_test(
-  ['layout-instability'],
-  ['performance-timeline'],
-  idl_array => {
-    idl_array.add_objects({
-      // LayoutShift: [ TODO ]
-    });
-  }
-);