summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js
blob: 7ac3b5158679202cd961a4e3c9a414dae2019fa7 (plain)
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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

const EXAMPLE_COM_URI =
  "https://example.com/document-builder.sjs?html=<div id=com>com";
const EXAMPLE_NET_URI =
  "https://example.net/document-builder.sjs?html=<div id=net>net";

const ORG_URL_ROOT = URL_ROOT.replace("example.com", "example.org");
const TEST_ORG_URI =
  ORG_URL_ROOT + "doc_inspector_fission_frame_navigation.html";

add_task(async function () {
  const { inspector } = await openInspectorForURL(TEST_ORG_URI);
  const tree = `
    id="root"
      iframe
        #document
          html
            head
            body
              id="org"`;
  // Note: the assertMarkupViewAsTree uses very high level APIs and is similar
  // to what should happen when a user interacts with the markup view.
  // It is important to avoid explicitly fetching walkers or node fronts during
  // the test, as it might cause reparenting of remote frames and make the test
  // succeed without actually testing that the feature works correctly.
  await assertMarkupViewAsTree(tree, "#root", inspector);

  await navigateIframeTo(inspector, EXAMPLE_COM_URI);
  const treeAfterLoadingCom = `
    id="root"
      iframe
        #document
          html
            head
            body
              id="com"`;
  await assertMarkupViewAsTree(treeAfterLoadingCom, "#root", inspector);

  await navigateIframeTo(inspector, EXAMPLE_NET_URI);
  const treeAfterLoadingNet = `
    id="root"
      iframe
        #document
          html
            head
            body
              id="net"`;
  await assertMarkupViewAsTree(treeAfterLoadingNet, "#root", inspector);
});

/**
 * This test will check the behavior when navigating a frame which is not
 * visible in the markup view, because its parentNode has not been expanded yet.
 *
 * We expect a root-node resource to be emitted, but ideally we should not
 * initialize the walker and inspector actors for the target of this root-node
 * resource.
 */
add_task(async function navigateFrameNotExpandedInMarkupView() {
  if (!isFissionEnabled()) {
    // This test only makes sense with Fission and remote frames, otherwise the
    // root-node resource will not be emitted for a frame navigation.
    return;
  }

  const { inspector } = await openInspectorForURL(TEST_ORG_URI);
  const resourceCommand = inspector.toolbox.resourceCommand;

  // At this stage the expected layout of the markup view is
  // v html     (expanded)
  //   v body   (expanded)
  //     > p    (collapsed)
  //     > div  (collapsed)
  //
  // The iframe we are about to navigate is therefore hidden and we are not
  // watching it - ie, it is not in the list of known NodeFronts/Actors.
  const resource = await navigateIframeTo(inspector, EXAMPLE_COM_URI);

  is(
    resource.resourceType,
    resourceCommand.TYPES.ROOT_NODE,
    "A resource with resourceType ROOT_NODE was received when navigating"
  );

  // This highlights what doesn't work with the current approach.
  // Since the root-node resource is a NodeFront watched via the WalkerFront,
  // watching it for a new remote frame target implies initializing the
  // inspector & walker fronts.
  //
  // If the resource was not a front (eg ContentDOMreference?) and was emitted
  // by the target actor instead of the walker actor, the client can decide if
  // the inspector and walker fronts should be initialized or not.
  //
  // In this test scenario, the children of the iframe were not known by the
  // inspector before this navigation. Per the explanation above, the new target
  // should not have an already instantiated inspector front.
  //
  // This should be fixed when implementing the RootNode resource on the server
  // in https://bugzilla.mozilla.org/show_bug.cgi?id=1644190
  todo(
    !resource.targetFront.getCachedFront("inspector"),
    "The inspector front for the new target should not be initialized"
  );
});

async function navigateIframeTo(inspector, url) {
  info("Navigate the test iframe to " + url);

  const { commands } = inspector;
  const { resourceCommand } = inspector.toolbox;
  const onTargetProcessed = waitForTargetProcessed(commands, url);

  const { onResource: onNewRoot } = await resourceCommand.waitForNextResource(
    resourceCommand.TYPES.ROOT_NODE,
    {
      ignoreExistingResources: true,
    }
  );

  info("Update the src attribute of the iframe tag");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [url], function (_url) {
    content.document.querySelector("iframe").setAttribute("src", _url);
  });

  info("Wait for frameLoad/newRoot to resolve");
  const newRootResult = await onNewRoot;

  info("Wait for pending children updates");
  await inspector.markup._waitForChildren();

  if (isFissionEnabled()) {
    info("Wait until the new target has been processed by TargetCommand");
    await onTargetProcessed;
  }

  // Note: the newRootResult changes when the test runs with or without fission.
  return newRootResult;
}

/**
 * Returns a promise that waits until the provided commands's TargetCommand has fully
 * processed a target with the provided URL.
 * This will avoid navigating again before the new resource command  have fully
 * attached to the new target.
 */
function waitForTargetProcessed(commands, url) {
  return new Promise(resolve => {
    const onTargetProcessed = targetFront => {
      if (targetFront.url !== encodeURI(url)) {
        return;
      }
      commands.targetCommand.off(
        "processed-available-target",
        onTargetProcessed
      );
      resolve();
    };
    commands.targetCommand.on("processed-available-target", onTargetProcessed);
  });
}