summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/object/tests/browser_object.js
blob: 9f6d5132d3ecec4c6c82f209c2a5eecaa66568db (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// Test the ObjectCommand

add_task(async function testObjectRelease() {
  const tab = await addTab("data:text/html;charset=utf-8,Test page<script>var foo = { bar: 42 };</script>");

  const commands = await CommandsFactory.forTab(tab);
  await commands.targetCommand.startListening();

  const { objectCommand } = commands;

  const evaluationResponse = await commands.scriptCommand.execute(
    "window.foo"
  );

  // Execute a second time so that the WebConsoleActor set this._lastConsoleInputEvaluation to another value
  // and so we prevent freeing `window.foo` 
  await commands.scriptCommand.execute("");

  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    is(content.wrappedJSObject.foo.bar, 42);
    const weakRef = Cu.getWeakReference(content.wrappedJSObject.foo);

    // Hold off the weak reference on SpecialPowsers so that it can be accessed in the next SpecialPowers.spawn
    SpecialPowers.weakRef = weakRef;

    // Nullify this variable so that it should be freed
    // unless the DevTools inspection still hold it in memory
    content.wrappedJSObject.foo = null;

    Cu.forceGC();
    Cu.forceCC();

    ok(SpecialPowers.weakRef.get(), "The 'foo' object can't be freed because of DevTools keeping a reference on it");
  });

  info("Release the server side actors which are keeping the object in memory");
  const objectFront = evaluationResponse.result;
  await commands.objectCommand.releaseObjects([objectFront]);

  ok(objectFront.isDestroyed(), "The passed object front has been destroyed");

  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    await ContentTaskUtils.waitForCondition(() => {
      Cu.forceGC();
      Cu.forceCC();
      return !SpecialPowers.weakRef.get();
    }, "Wait for JS object to be freed", 500);

    ok(!SpecialPowers.weakRef.get(), "The 'foo' object has been freed");
  });

  await commands.destroy();
  BrowserTestUtils.removeTab(tab);
});

add_task(async function testMultiTargetObjectRelease() {
   // This test fails with EFT disabled
  if (!isEveryFrameTargetEnabled()) {
    return;
  }

  const tab = await addTab(`data:text/html;charset=utf-8,Test page<iframe src="data:text/html,bar">/iframe>`);

  const commands = await CommandsFactory.forTab(tab);
  await commands.targetCommand.startListening();

  const [,iframeTarget] = commands.targetCommand.getAllTargets(commands.targetCommand.ALL_TYPES);
  is(iframeTarget.url, "data:text/html,bar");

  const { objectCommand } = commands;

  const evaluationResponse1 = await commands.scriptCommand.execute(
    "window"
  );
  const evaluationResponse2 = await commands.scriptCommand.execute(
    "window", {
      selectedTargetFront: iframeTarget,
    }
  );
  const object1 = evaluationResponse1.result;
  const object2 = evaluationResponse2.result;
  isnot(object1, object2, "The two window object fronts are different");
  isnot(object1.targetFront, object2.targetFront, "The two window object fronts relates to two distinct targets");
  is(object2.targetFront, iframeTarget, "The second object relates to the iframe target");

  await commands.objectCommand.releaseObjects([object1, object2]);
  ok(object1.isDestroyed(), "The first object front is destroyed");
  ok(object2.isDestroyed(), "The second object front is destroyed");

  await commands.destroy();
  BrowserTestUtils.removeTab(tab);
});

add_task(async function testWorkerObjectRelease() {
  const workerUrl = `data:text/javascript,const foo = {}`;
  const tab = await addTab(`data:text/html;charset=utf-8,Test page<script>const worker = new Worker("${workerUrl}")</script>`);

  const commands = await CommandsFactory.forTab(tab);
  commands.targetCommand.listenForWorkers = true;
  await commands.targetCommand.startListening();

  const [,workerTarget] = commands.targetCommand.getAllTargets(commands.targetCommand.ALL_TYPES);
  is(workerTarget.url, workerUrl);

  const { objectCommand } = commands;

  const evaluationResponse = await commands.scriptCommand.execute(
    "foo", {
      selectedTargetFront: workerTarget,
    }
  );
  const object = evaluationResponse.result;
  is(object.targetFront, workerTarget, "The 'foo' object relates to the worker target");

  await commands.objectCommand.releaseObjects([object]);
  ok(object.isDestroyed(), "The object front is destroyed");

  await commands.destroy();
  BrowserTestUtils.removeTab(tab);
});