Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
// Tests UrlbarResult.toWire()/fromWire() and the UrlbarQueryContext prototype
// restoration that the Urlbar actor pair relies on to carry results across the
// process boundary. Actor messages structured-clone their payload, which only
// copies own enumerable data properties: UrlbarResult keeps its data in
// private fields behind getters, so a bare structuredClone loses all of it.
"use strict";
function makeUrlResult({ heuristic = false } = {}) {
return new UrlbarResult({
type: UrlbarShared.RESULT_TYPE.URL,
source: UrlbarShared.RESULT_SOURCE.HISTORY,
heuristic,
payload: {
title: "Example page",
},
});
}
function makeSearchResult() {
return new UrlbarResult(
{
type: UrlbarShared.RESULT_TYPE.SEARCH,
source: UrlbarShared.RESULT_SOURCE.SEARCH,
payload: {
engine: "Test",
suggestion: "foo bar",
query: "foo",
lowerCaseSuggestion: "foo bar",
},
},
{ suggestion: [[0, 3]] }
);
}
// Sending a result through the structured-clone algorithm the way an actor
// message would: the wire form survives, the bare instance does not.
function roundTrip(result) {
return UrlbarResult.fromWire(structuredClone(result.toWire()));
}
add_task(function test_bare_structuredClone_loses_data() {
let result = makeUrlResult();
let cloned = structuredClone(result);
Assert.equal(
cloned.type,
undefined,
"A bare structuredClone of a UrlbarResult loses the private-field data"
);
Assert.equal(cloned.payload, undefined, "payload is lost too");
});
add_task(function test_url_result_roundtrip() {
let result = makeUrlResult({ heuristic: true });
result.rowIndex = 3;
let restored = roundTrip(result);
Assert.ok(restored instanceof UrlbarResult, "Reconstructed a UrlbarResult");
Assert.equal(restored.type, result.type, "type preserved");
Assert.equal(restored.source, result.source, "source preserved");
Assert.equal(restored.heuristic, true, "heuristic preserved");
Assert.deepEqual(restored.payload, result.payload, "payload preserved");
Assert.equal(restored.rowIndex, 3, "ad-hoc public rowIndex preserved");
// Derived getter works, which means the prototype is intact.
Assert.equal(restored.icon, result.icon, "derived icon getter works");
});
add_task(function test_search_result_with_highlights_roundtrip() {
let result = makeSearchResult();
let restored = roundTrip(result);
Assert.deepEqual(
restored.testHighlights,
result.testHighlights,
"highlights preserved"
);
// A method that reads the payload + highlights still works.
let { value } = restored.getDisplayableValueAndHighlights("suggestion");
Assert.equal(value, "foo bar", "getDisplayableValueAndHighlights works");
});
add_task(function test_providerType_roundtrip() {
let result = makeSearchResult();
// providerType is set on the result after construction (by the providers
// manager), via a setter rather than a constructor parameter.
result.providerType = UrlbarUtils.PROVIDER_TYPE.PROFILE;
let restored = roundTrip(result);
Assert.equal(
restored.providerType,
UrlbarUtils.PROVIDER_TYPE.PROFILE,
"set-only providerType preserved"
);
});
add_task(function test_queryContext_roundtrip() {
let context = createContext("foo bar", { providers: ["test"] });
let heuristic = makeUrlResult({ heuristic: true });
context.results = [heuristic, makeSearchResult()];
context.heuristicResult = heuristic;
// Simulate crossing the actor boundary: structured-clone the context's data
// (with results in wire form), then restore the class on the far side.
let wire = structuredClone({
...context,
results: context.results.map(r => r.toWire()),
heuristicResult: context.heuristicResult.toWire(),
});
Object.setPrototypeOf(wire, UrlbarQueryContext.prototype);
wire.results = wire.results.map(UrlbarResult.fromWire);
wire.heuristicResult = UrlbarResult.fromWire(wire.heuristicResult);
Assert.equal(wire.searchString, "foo bar", "searchString preserved");
Assert.deepEqual(wire.tokens, context.tokens, "tokens preserved");
Assert.ok(
wire.results.every(r => r instanceof UrlbarResult),
"nested results reconstructed as UrlbarResult instances"
);
// heuristicResult starts out as the same instance as results[0], but
// serializing each result on its own doesn't preserve that shared identity
// (the actor wiring will need to dedup, e.g. by index), so we compare data
// rather than identity. deepEqual on the instances wouldn't work: a result's
// data lives in private fields, which deepEqual can't see, so we compare the
// toWire() forms, where that data is exposed as plain properties.
Assert.deepEqual(
wire.heuristicResult.toWire(),
wire.results[0].toWire(),
"heuristicResult round-trips to the same data as the first result"
);
// A context method works after prototype restoration.
Assert.equal(
typeof wire.allowRemoteResults,
"function",
"allowRemoteResults is callable after prototype restoration"
);
Assert.equal(
typeof wire.allowRemoteResults(),
"boolean",
"allowRemoteResults returns a result"
);
});