summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/tests/chrome/test_cows.xhtml
blob: 69d7d3e9e6ffaa2aa6e74185f0ade30823d16e57 (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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                 type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=500931
-->
<window title="Mozilla Bug 522764"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>

  <!-- test results are displayed in the html:body -->
  <body xmlns="http://www.w3.org/1999/xhtml">
  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=522764 "
     target="_blank">Mozilla Bug 522764 </a>
  </body>

  <!-- test code goes here -->
  <script type="application/javascript"><![CDATA[
add_task(async () => {
var sandbox = new Cu.Sandbox("about:blank");

await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
                                          true]]});

/* eslint-disable no-eval */

function getCOW(x) {
  if (typeof x != 'object' && typeof x != 'function')
    return x;
  x = Cu.waiveXrays(x);
  var rval = {};
  if (typeof x == "function")
    rval = eval(`(${x.toString()})`);
  for (var i in x) {
    if (x.__lookupGetter__(i))
      rval.__defineGetter__(i, eval(`(${x.__lookupGetter__(i).toString()})`))
    else
      rval[i] = getCOW(x[i]);
  }
  return rval;
}

// Give the sandbox a way to create ChromeObjectWrapped objects.
sandbox.getCOW = getCOW;

// Define test API functions in the sandbox.
const TEST_API = ['is', 'isnot', 'ok', 'todo_is', 'todo_isnot', 'todo'];
TEST_API.forEach(function(name) { sandbox[name] = window[name]; });

sandbox.chromeGet = function (obj, prop) { return obj[prop]; };

function COWTests() {
    function getNames(cow) {
        let names = [];
        for (let name in cow) {
            names.push(name);
        }
        return names;
    }

    // This function is actually stringified and run inside a
    // sandbox with content privileges.

    // TODO: This could use some refactoring; creating helper
    // functions like assertIsWritable(myObj, 'someproperty') might
    // be useful.

    function isPropHidden(obj, propName, desc) {
      try {
          is(obj[propName], undefined,
             "getting " + propName + " on " + desc + " should return undefined");
          ok(!(propName in obj),
             propName + " on " + desc + " should act as if it doesn't exist");
          ok(!Object.hasOwnProperty.call(obj, propName),
             propName + " on " + desc + " should act as if it doesn't exist");
      } catch (e) {
          ok(false, "getting " + propName + " on " + desc + " threw " + e);
      }
    }

    const PROPS_TO_TEST = ['foo', 'bar', 'prototype'];

    var empty = {};
    var nonempty = {foo: 42, bar: 33};
    is(getCOW(empty).foo, undefined,
       "shouldn't throw when accessing exposed properties that don't exist");

    PROPS_TO_TEST.forEach(function(name) {
        isPropHidden(getCOW(nonempty), name, "object without exposedProps");
    });

    // Test function objects.
    var func = function(x) { return 42; };
    func.foo = "foo property";
    var funcCOW = getCOW(func);
    try {
      funcCOW.foo;
      ok(false, 'Functions are no longer COWable');
    } catch (e) {
      ok(/denied|insecure/.test(e), 'Functions are no longer COWable');
    }
    is(funcCOW(), 42, "Chrome functions should be callable");

    // Test writable property
    var writable = getCOW({});
    try {
        ok(!("foo" in writable),
           "non-existing write-only property shouldn't exist");
        writable.foo = 5;
        ok(false, "writing to a write-only exposed prop should throw");
    } catch (e) {
        ok(/Permission denied/.test(e),
           "writing to a write-only exposed prop should throw the right error");
    }
    is(writable.foo, undefined,
       "reading from a write-only exposed prop should return undefined");
    try {
        delete writable.foo;
        ok(false, "deleting a write-only exposed prop should throw");
    } catch (e) {
        ok(true, "deleting a write-only exposed prop should throw " + e);
    }

    // Test readable property
    var readable = { foo: 5,
                     bar: 6 };
    try {
        isPropHidden(getCOW(readable), "foo", undefined,
                     "reading from a readable exposed prop shouldn't work");
    } catch (e) {
        ok(false, "reading from a readable exposed prop shouldn't throw " + e);
    }
    try {
        getCOW(readable).foo = 1;
        ok(false, "writing to a read-only exposed prop should fail");
    } catch (e) {
        ok(/Permission denied/.test(e),
           "writing to a read-only exposed prop should fail");
    }
    try {
        delete getCOW(readable).foo;
        ok(false, "deleting a read-only exposed prop shouldn't work");
    } catch (e) {
        ok(/Permission denied/.test(e),
           "deleting a read-only exposed prop should throw error");
    }

    try {
        var props = getNames(getCOW(readable));
        is(props.length, 0, "COW w/ one exposed prop should not enumerate");
    } catch (e) {
        ok(false, "COW w/ a readable prop should not raise exc " +
                  "on enumeration: " + e);
    }

    // Test read/write property
    var readwrite = getCOW({});
    try {
        ok(!("foo" in readwrite),
           "non-existing readwrite property shouldn't exist");
        readwrite.foo = 5;
        ok(false, "writing to a readwrite exposed prop should throw");
    } catch (e) {
        ok(/Permission denied/.test(e),
           "writing to a readwrite exposed prop should throw the right error");
    }
    try {
        delete readwrite.foo;
        ok(false, "deleting a readwrite prop should throw");
    } catch (e) {
        ok(/Permission denied/.test(e),
           "deleting a readwrite exposed prop should throw the right error");
    }

    // Readables and functions
    try {
        var COWFunc = getCOW((function() { return 5; }));
        is(COWFunc(), 5, "COWed functions should be callable");
    } catch (e) {
        todo(false, "COWed functions should not raise " + e);
    }
}

// Stringify the COW test suite and directly evaluate it in the sandbox.
Cu.evalInSandbox('(' + COWTests.toString() + ')()', sandbox);

// Test that COWed objects passing from content to chrome get unwrapped.
function returnCOW() {
    return getCOW({bar: 6});
}

var unwrapped = Cu.evalInSandbox(
    '(' + returnCOW.toString() + ')()',
    sandbox
);

try {
    is(unwrapped.bar, 6,
       "COWs should be unwrapped when entering chrome space");
} catch (e) {
    todo(false, "COWs should be unwrapped when entering chrome space, " +
                "not raise " + e);
}
});
  ]]></script>
</window>