Changeset 286836 in webkit


Ignore:
Timestamp:
Dec 10, 2021 12:15:09 AM (7 months ago)
Author:
graouts@webkit.org
Message:

[Model] Add load and error events to distinguish resource load from model readiness
https://bugs.webkit.org/show_bug.cgi?id=233706
rdar://85922697

Reviewed by Chris Dumez and Dean Jackson.

Source/WebCore:

Test: model-element/model-element-error-and-load-events.html

Prior to this patch, <model> elements had a "ready" promise which resolved once the resource had been loaded.
However, this promise should be used when the <model> is fully ready, and this is done on macOS and iOS asynchronously
after the resource has been loaded by the supporting ARQL framework. So we need a way to monitor success or failure of
the resource load specifically.

To that end, and matching the <img> element, we dispatch "load" and "error" events on <model> elements and add a
"complete" property to indicate whether the resource is loaded.

Meanwhile, the "ready" promise is now resolved when the model is fully loaded by the supporting framework, indicating
that further APIs are safe to use.

Since creating the support ARQL object for macOS and iOS also requires the <model> element's renderer being available,
we opt into "custom style resolve callbacks" so that we may implement didAttachRenderers() on HTMLModelElement and keep
track of renderer availability before attempting to create the ModelPlayer.

  • Modules/model-element/HTMLModelElement.cpp:

(WebCore::HTMLModelElement::HTMLModelElement):
(WebCore::HTMLModelElement::create):
(WebCore::HTMLModelElement::setSourceURL):
(WebCore::HTMLModelElement::didAttachRenderers):
(WebCore::HTMLModelElement::notifyFinished):
(WebCore::HTMLModelElement::modelDidChange):
(WebCore::HTMLModelElement::createModelPlayer):
(WebCore::HTMLModelElement::didFinishLoading):
(WebCore::HTMLModelElement::didFailLoading):
(WebCore::HTMLModelElement::activeDOMObjectName const):
(WebCore::HTMLModelElement::virtualHasPendingActivity const):

  • Modules/model-element/HTMLModelElement.h:
  • Modules/model-element/HTMLModelElement.idl:

Tools:

Use the "load" event instead of the "ready" promise for this test which only requires monitoring
the <model> resource being loaded.

  • TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:

(TestWebKitAPI::TEST):

LayoutTests:

Remove existing tests around resource loading and recreate them in terms of "load" and "error"
events in model-element/model-element-error-and-load-events.html and in terms of the ready
promise in model-element/model-element-ready.html.

Other tests using model.ready for other purposes are also rewritten using events.

  • model-element/model-element-contents-layer-updates-with-clipping.html:
  • model-element/model-element-contents-layer-updates.html:
  • model-element/model-element-error-and-load-events-expected.txt: Added.
  • model-element/model-element-error-and-load-events.html: Added.
  • model-element/model-element-graphics-layers-opacity.html:
  • model-element/model-element-graphics-layers.html:
  • model-element/model-element-ready-expected.txt:
  • model-element/model-element-ready-load-aborted-expected.txt: Removed.
  • model-element/model-element-ready-load-aborted.html: Removed.
  • model-element/model-element-ready-load-failed-expected.txt: Removed.
  • model-element/model-element-ready-load-failed.html: Removed.
  • model-element/model-element-ready.html:
  • model-element/resources/model-element-test-utils.js: Added.

(const.createModelAndSource):

  • platform/ios-simulator/TestExpectations:
  • platform/mac/TestExpectations:
Location:
trunk
Files:
3 added
4 deleted
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r286829 r286836  
     12021-12-09  Antoine Quint  <graouts@webkit.org>
     2
     3        [Model] Add load and error events to distinguish resource load from model readiness
     4        https://bugs.webkit.org/show_bug.cgi?id=233706
     5        rdar://85922697
     6
     7        Reviewed by Chris Dumez and Dean Jackson.
     8
     9        Remove existing tests around resource loading and recreate them in terms of "load" and "error"
     10        events in model-element/model-element-error-and-load-events.html and in terms of the ready
     11        promise in model-element/model-element-ready.html.
     12
     13        Other tests using model.ready for other purposes are also rewritten using events.
     14
     15        * model-element/model-element-contents-layer-updates-with-clipping.html:
     16        * model-element/model-element-contents-layer-updates.html:
     17        * model-element/model-element-error-and-load-events-expected.txt: Added.
     18        * model-element/model-element-error-and-load-events.html: Added.
     19        * model-element/model-element-graphics-layers-opacity.html:
     20        * model-element/model-element-graphics-layers.html:
     21        * model-element/model-element-ready-expected.txt:
     22        * model-element/model-element-ready-load-aborted-expected.txt: Removed.
     23        * model-element/model-element-ready-load-aborted.html: Removed.
     24        * model-element/model-element-ready-load-failed-expected.txt: Removed.
     25        * model-element/model-element-ready-load-failed.html: Removed.
     26        * model-element/model-element-ready.html:
     27        * model-element/resources/model-element-test-utils.js: Added.
     28        (const.createModelAndSource):
     29        * platform/ios-simulator/TestExpectations:
     30        * platform/mac/TestExpectations:
     31
    1322021-12-09  Arcady Goldmints-Orlov  <agoldmints@igalia.com>
    233
  • trunk/LayoutTests/model-element/model-element-contents-layer-updates-with-clipping.html

    r276562 r286836  
    77<pre id="layers"></pre>
    88<script>
    9     let layers = document.getElementById("layers");
    10     let source = document.getElementsByTagName("source")[0];
     9    window.testRunner?.waitUntilDone();
     10    window.testRunner?.dumpAsText();
    1111
    12     if (window.testRunner) {
    13         testRunner.waitUntilDone();
    14         testRunner.dumpAsText();
    15     } else
    16         layers.textContent = "This test requires testRunner.";
     12    const layers = document.getElementById("layers");
     13    const source = document.querySelector("source");
     14    const model = document.getElementById("model");
    1715
    18     let model = document.getElementById("model");
     16    const modelDidLoadFirstSource = () => {
     17        layers.textContent = "Before Changing Source:\n";
     18        layers.textContent += window.internals?.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS) ?? "This test requires testRunner.";
    1919
    20     model.ready.then(value => {
    21         layers.textContent = "Before Changing Source:\n";
    22         layers.textContent += window.internals.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS);
     20        model.addEventListener("load", event => {
     21            layers.textContent += "After Changing Source:\n";
     22            layers.textContent += window.internals?.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS) ?? "This test requires testRunner.";
     23            window.testRunner?.notifyDone();
     24        }, { once: true });
     25
     26        model.addEventListener("error", event => {
     27            layers.textContent = `Failed. Second model did not load.`;
     28            window.testRunner?.notifyDone();
     29        }, { once: true });
    2330
    2431        source.src = "resources/cube.usdz";
    25         model.ready.then(value => {
    26             if (window.testRunner) {
    27                 layers.textContent += "After Changing Source:\n";
    28                 layers.textContent += window.internals.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS);
    29             }
    30         }, reason => {
    31             layers.textContent = `Failed. Second model did not load: ${reason}`;
    32         }).finally(() => {
    33             if (window.testRunner)
    34                 testRunner.notifyDone();
    35         });
    36        
    37     }, reason => {
    38         layers.textContent = `Failed. First model did not load: ${reason}`;
    39         if (window.testRunner)
    40             testRunner.notifyDone();
    41     });
     32    }
     33
     34    if (model.complete)
     35        modelDidLoadFirstSource();
     36    else {
     37        model.addEventListener("load", modelDidLoadFirstSource, { once: true });
     38        model.addEventListener("error", event => {
     39            layers.textContent = `Failed. First model did not load.`;
     40            window.testRunner?.notifyDone();
     41        }, { once: true });
     42    }
    4243</script>
    4344</body>
  • trunk/LayoutTests/model-element/model-element-contents-layer-updates.html

    r276562 r286836  
    77<pre id="layers"></pre>
    88<script>
    9     let layers = document.getElementById("layers");
    10     let source = document.getElementsByTagName("source")[0];
     9    window.testRunner?.waitUntilDone();
     10    window.testRunner?.dumpAsText();
    1111
    12     if (window.testRunner) {
    13         testRunner.waitUntilDone();
    14         testRunner.dumpAsText();
    15     } else
    16         layers.textContent = "This test requires testRunner.";
     12    const layers = document.getElementById("layers");
     13    const source = document.querySelector("source");
     14    const model = document.getElementById("model");
    1715
    18     let model = document.getElementById("model");
     16    const modelDidLoadFirstSource = () => {
     17        layers.textContent = "Before Changing Source:\n";
     18        layers.textContent += window.internals?.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS) ?? "This test requires testRunner.";
    1919
    20     model.ready.then(value => {
    21         layers.textContent = "Before Changing Source:\n";
    22         layers.textContent += window.internals.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS);
     20        model.addEventListener("load", event => {
     21            layers.textContent += "After Changing Source:\n";
     22            layers.textContent += window.internals?.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS) ?? "This test requires testRunner.";
     23            window.testRunner?.notifyDone();
     24        }, { once: true });
     25
     26        model.addEventListener("error", event => {
     27            layers.textContent = `Failed. Second model did not load.`;
     28            window.testRunner?.notifyDone();
     29        }, { once: true });
    2330
    2431        source.src = "resources/cube.usdz";
    25         model.ready.then(value => {
    26             if (window.testRunner) {
    27                 layers.textContent += "After Changing Source:\n";
    28                 layers.textContent += window.internals.platformLayerTreeAsText(model, window.internals.PLATFORM_LAYER_TREE_INCLUDE_MODELS);
    29             }
    30         }, reason => {
    31             layers.textContent = `Failed. Second model did not load: ${reason}`;
    32         }).finally(() => {
    33             if (window.testRunner)
    34                 testRunner.notifyDone();
    35         });
    36        
    37     }, reason => {
    38         layers.textContent = `Failed. First model did not load: ${reason}`;
    39         if (window.testRunner)
    40             testRunner.notifyDone();
    41     });
     32    }
     33
     34    if (model.complete)
     35        modelDidLoadFirstSource();
     36    else {
     37        model.addEventListener("load", modelDidLoadFirstSource, { once: true });
     38        model.addEventListener("error", event => {
     39            layers.textContent = `Failed. First model did not load.`;
     40            window.testRunner?.notifyDone();
     41        }, { once: true });
     42    }
    4243</script>
    4344</body>
  • trunk/LayoutTests/model-element/model-element-graphics-layers-opacity.html

    r276562 r286836  
    1414<pre id="layers"></pre>
    1515<script>
    16     let layers = document.getElementById("layers");
     16    window.testRunner?.waitUntilDone();
     17    window.testRunner?.dumpAsText();
    1718
    18     if (window.testRunner) {
    19         testRunner.waitUntilDone();
    20         testRunner.dumpAsText();
    21     } else
    22         layers.textContent = "This test requires testRunner.";
     19    const layers = document.getElementById("layers");
     20    const model = document.getElementById("model");
    2321
    24     let model = document.getElementById("model");
     22    const modelDidLoad = () => {
     23        layers.innerText = window.internals?.platformLayerTreeAsText(model) ?? "This test requires testRunner.";
     24        model.remove();
     25        window.testRunner?.notifyDone();
     26    };
    2527
    26     model.ready.then(value => {
    27         if (window.testRunner)
    28             layers.innerText = window.internals.platformLayerTreeAsText(model);
    29         model.remove();
    30     }, reason => {
    31         layers.textContent = `Failed. Model did not load: ${reason}`;
    32     }).finally(() => {
    33         if (window.testRunner)
    34             testRunner.notifyDone();
    35     });
     28    if (model.complete)
     29        modelDidLoad();
     30    else {
     31        model.addEventListener("load", modelDidLoad);
     32        model.addEventListener("error", event => {
     33            layers.textContent = `Failed. Model did not load.`;
     34            window.testRunner?.notifyDone();
     35        });
     36    }
    3637</script>
    3738</body>
  • trunk/LayoutTests/model-element/model-element-graphics-layers.html

    r276327 r286836  
    77<pre id="layers"></pre>
    88<script>
    9     let layers = document.getElementById("layers");
     9    window.testRunner?.waitUntilDone();
     10    window.testRunner?.dumpAsText();
    1011
    11     if (window.testRunner) {
    12         testRunner.waitUntilDone();
    13         testRunner.dumpAsText();
    14     } else
    15         layers.textContent = "This test requires testRunner.";
     12    const layers = document.getElementById("layers");
     13    const model = document.getElementById("model");
    1614
    17     let model = document.getElementById("model");
     15    const modelDidLoad = () => {
     16        layers.innerText = window.internals?.layerTreeAsText(document) ?? "This test requires testRunner.";
     17        model.remove();
     18        window.testRunner?.notifyDone();
     19    }
    1820
    19     model.ready.then(value => {
    20         if (window.testRunner)
    21             layers.innerText = window.internals.layerTreeAsText(document);
    22         model.remove();
    23     }, reason => {
    24         layers.textContent = `Failed. Model did not load: ${reason}`;
    25     }).finally(() => {
    26         if (window.testRunner)
    27             testRunner.notifyDone();
    28     });
     21    if (model.complete)
     22        modelDidLoad();
     23    else {
     24        model.addEventListener("load", modelDidLoad);
     25        model.addEventListener("error", event => {
     26            layers.textContent = `Failed. Model did not load.`;
     27            window.testRunner?.notifyDone();
     28        });
     29    }
    2930</script>
    3031</body>
  • trunk/LayoutTests/model-element/model-element-ready-expected.txt

    r273290 r286836  
    1 This test passes if you see the word "Passed" below:
    21
    3 Passed
     2PASS <model> rejects the ready promise when provided with an unknown resoure.
     3PASS <model> rejects the ready promise when its resource load is aborted.
     4PASS <model> resolves the ready promise when provided with a known resource.
     5
  • trunk/LayoutTests/model-element/model-element-ready.html

    r273290 r286836  
    1 <!DOCTYPE html><!-- webkit-test-runner [ ModelElementEnabled=true ] -->
    2 <html>
     1<!doctype html>
     2<meta charset="utf-8">
     3<title>&lt;model> ready promise</title>
     4<script src="../resources/testharness.js"></script>
     5<script src="../resources/testharnessreport.js"></script>
     6<script src="resources/model-element-test-utils.js"></script>
    37<body>
    4 <model id="model">
    5     <source src="resources/heart.usdz">
    6 </model>
    7 <p>This test passes if you see the word "Passed" below:</p>
    8 <p id="result">Failed</p>
    98<script>
    10     if (window.testRunner) {
    11         testRunner.waitUntilDone();
    12         testRunner.dumpAsText();
    13     }
     9'use strict';
    1410
    15     let result = document.getElementById("result");
    16     let model = document.getElementById("model");
     11promise_test(async t => {
     12    const [model, source] = createModelAndSource(t, "resources/does-not-exist.usdz");
     13    return model.ready.then(
     14        value => assert_unreached("Unexpected ready promise resolution."),
     15        reason => assert_true(reason.toString().includes("NetworkError"), "The ready promise is rejected with a NetworkError.")
     16    );
     17}, `<model> rejects the ready promise when provided with an unknown resoure.`);
    1718
    18     model.ready.then(value => {
    19         result.textContent = `Passed`;
    20     }, reason => {
    21         result.textContent = `Failed. Model did not load: ${reason}`;
    22     }).finally(() => {
    23         if (window.testRunner)
    24             testRunner.notifyDone();
    25     });
     19promise_test(async t => {
     20    const [model, source] = createModelAndSource(t, "resources/heart.usdz");
     21    const modelReady = model.ready;
     22
     23    source.remove();
     24    assert_not_equals(model.ready, modelReady, "Removing the <source> child resets the ready promise.");
     25
     26    return modelReady.then(
     27        value => assert_unreached("Unexpected ready promise resolution."),
     28        reason => assert_true(reason.toString().includes("AbortError"), "The ready promise is rejected with a NetworkError.")
     29    );
     30}, `<model> rejects the ready promise when its resource load is aborted.`);
     31
     32promise_test(async t => {
     33    const [model, source] = createModelAndSource(t, "resources/cube.usdz");
     34    await model.ready;
     35}, `<model> resolves the ready promise when provided with a known resource.`);
     36
    2637</script>
    2738</body>
    28 </html>
  • trunk/LayoutTests/platform/ios-simulator/TestExpectations

    r285394 r286836  
    153153
    154154webkit.org/b/223949 crypto/crypto-random-values-oom.html [ Pass Timeout ]
     155
     156# This test relies on ARQL APIs which are not available in the Simulator
     157model-element/model-element-ready.html [ Skip ]
  • trunk/LayoutTests/platform/mac/TestExpectations

    r286335 r286836  
    22832283[ Monterey ] imported/w3c/web-platform-tests/fetch/connection-pool/network-partition-key.html [ Failure ]
    22842284
    2285 # webkit.org/b/228200 Setting multiple test expectations for Monetery on OpenSource:
     2285# webkit.org/b/228200 Setting multiple test expectations for Monterey on OpenSource:
    22862286[ Monterey ] model-element/model-element-graphics-layers-opacity.html [ Pass Failure ]
    22872287[ Monterey Debug arm64 ] imported/w3c/web-platform-tests/webrtc/RTCPeerConnection-restartIce.https.html [ Pass Failure Crash ]
     
    24292429
    24302430webkit.org/b/221230 [ BigSur+ ] imported/w3c/web-platform-tests/media-source/mediasource-addsourcebuffer.html [ Pass Failure ]
     2431
     2432# <model> tests involving the ready promise can only work on Monterey and up
     2433[ Catalina BigSur ] model-element/model-element-ready.html [ Skip ]
  • trunk/Source/WebCore/ChangeLog

    r286835 r286836  
     12021-12-09  Antoine Quint  <graouts@webkit.org>
     2
     3        [Model] Add load and error events to distinguish resource load from model readiness
     4        https://bugs.webkit.org/show_bug.cgi?id=233706
     5        rdar://85922697
     6
     7        Reviewed by Chris Dumez and Dean Jackson.
     8
     9        Test: model-element/model-element-error-and-load-events.html
     10
     11        Prior to this patch, <model> elements had a "ready" promise which resolved once the resource had been loaded.
     12        However, this promise should be used when the <model> is fully ready, and this is done on macOS and iOS asynchronously
     13        after the resource has been loaded by the supporting ARQL framework. So we need a way to monitor success or failure of
     14        the resource load specifically.
     15
     16        To that end, and matching the <img> element, we dispatch "load" and "error" events on <model> elements and add a
     17        "complete" property to indicate whether the resource is loaded.
     18
     19        Meanwhile, the "ready" promise is now resolved when the model is fully loaded by the supporting framework, indicating
     20        that further APIs are safe to use.
     21
     22        Since creating the support ARQL object for macOS and iOS also requires the <model> element's renderer being available,
     23        we opt into "custom style resolve callbacks" so that we may implement didAttachRenderers() on HTMLModelElement and keep
     24        track of renderer availability before attempting to create the ModelPlayer.
     25
     26        * Modules/model-element/HTMLModelElement.cpp:
     27        (WebCore::HTMLModelElement::HTMLModelElement):
     28        (WebCore::HTMLModelElement::create):
     29        (WebCore::HTMLModelElement::setSourceURL):
     30        (WebCore::HTMLModelElement::didAttachRenderers):
     31        (WebCore::HTMLModelElement::notifyFinished):
     32        (WebCore::HTMLModelElement::modelDidChange):
     33        (WebCore::HTMLModelElement::createModelPlayer):
     34        (WebCore::HTMLModelElement::didFinishLoading):
     35        (WebCore::HTMLModelElement::didFailLoading):
     36        (WebCore::HTMLModelElement::activeDOMObjectName const):
     37        (WebCore::HTMLModelElement::virtualHasPendingActivity const):
     38        * Modules/model-element/HTMLModelElement.h:
     39        * Modules/model-element/HTMLModelElement.idl:
     40
    1412021-12-10  Said Abou-Hallawa  <said@apple.com>
    242
  • trunk/Source/WebCore/Modules/model-element/HTMLModelElement.cpp

    r286406 r286836  
    6666HTMLModelElement::HTMLModelElement(const QualifiedName& tagName, Document& document)
    6767    : HTMLElement(tagName, document)
     68    , ActiveDOMObject(document)
    6869    , m_readyPromise { makeUniqueRef<ReadyPromise>(*this, &HTMLModelElement::readyPromiseResolve) }
    6970{
     71    setHasCustomStyleResolveCallbacks();
    7072}
    7173
     
    8082Ref<HTMLModelElement> HTMLModelElement::create(const QualifiedName& tagName, Document& document)
    8183{
    82     return adoptRef(*new HTMLModelElement(tagName, document));
     84    auto model = adoptRef(*new HTMLModelElement(tagName, document));
     85    model->suspendIfNeeded();
     86    return model;
    8387}
    8488
     
    132136
    133137    m_readyPromise = makeUniqueRef<ReadyPromise>(*this, &HTMLModelElement::readyPromiseResolve);
    134 
    135     if (m_sourceURL.isEmpty())
    136         return;
     138    m_shouldCreateModelPlayerUponRendererAttachment = false;
     139
     140    if (m_sourceURL.isEmpty()) {
     141        queueTaskToDispatchEvent(*this, TaskSource::DOMManipulation, Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
     142        return;
     143    }
    137144
    138145    ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
     
    146153    auto resource = document().cachedResourceLoader().requestModelResource(WTFMove(request));
    147154    if (!resource.has_value()) {
     155        queueTaskToDispatchEvent(*this, TaskSource::DOMManipulation, Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
    148156        m_readyPromise->reject(Exception { NetworkError });
    149157        return;
     
    174182{
    175183    return createRenderer<RenderModel>(*this, WTFMove(style));
     184}
     185
     186void HTMLModelElement::didAttachRenderers()
     187{
     188    if (!m_shouldCreateModelPlayerUponRendererAttachment)
     189        return;
     190
     191    m_shouldCreateModelPlayerUponRendererAttachment = false;
     192    createModelPlayer();
    176193}
    177194
     
    198215        m_data = nullptr;
    199216
     217        queueTaskToDispatchEvent(*this, TaskSource::DOMManipulation, Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
     218
    200219        invalidateResourceHandleAndUpdateRenderer();
    201220
     
    207226    m_model = Model::create(m_data.releaseNonNull().get(), resource.mimeType(), resource.url());
    208227
     228    queueTaskToDispatchEvent(*this, TaskSource::DOMManipulation, Event::create(eventNames().loadEvent, Event::CanBubble::No, Event::IsCancelable::No));
     229
    209230    invalidateResourceHandleAndUpdateRenderer();
    210231
    211     m_readyPromise->resolve(*this);
    212 
    213232    modelDidChange();
    214233}
     
    218237void HTMLModelElement::modelDidChange()
    219238{
    220     // FIXME: For the early returns here, we should probably inform the page that things have
    221     // failed to render. For the case of no-renderer, we should probably also build the model
    222     // when/if a renderer is created.
    223 
    224     auto page = document().page();
    225     if (!page)
    226         return;
     239    auto* page = document().page();
     240    if (!page) {
     241        m_readyPromise->reject(Exception { AbortError });
     242        return;
     243    }
    227244
    228245    auto* renderer = this->renderer();
    229     if (!renderer)
    230         return;
    231 
    232     m_modelPlayer = page->modelPlayerProvider().createModelPlayer(*this);
    233     if (!m_modelPlayer)
    234         return;
     246    if (!renderer) {
     247        m_shouldCreateModelPlayerUponRendererAttachment = true;
     248        return;
     249    }
     250
     251    createModelPlayer();
     252}
     253
     254void HTMLModelElement::createModelPlayer()
     255{
     256    ASSERT(document().page());
     257    m_modelPlayer = document().page()->modelPlayerProvider().createModelPlayer(*this);
     258    if (!m_modelPlayer) {
     259        m_readyPromise->reject(Exception { AbortError });
     260        return;
     261    }
    235262
    236263    // FIXME: We need to tell the player if the size changes as well, so passing this
    237264    // in with load probably doesn't make sense.
    238     auto size = renderer->absoluteBoundingBoxRect(false).size();
     265    ASSERT(renderer());
     266    auto size = renderer()->absoluteBoundingBoxRect(false).size();
    239267    m_modelPlayer->load(*m_model, size);
    240268}
     
    256284    if (auto* renderer = this->renderer())
    257285        renderer->updateFromElement();
     286
     287    m_readyPromise->resolve(*this);
    258288}
    259289
     
    261291{
    262292    ASSERT_UNUSED(modelPlayer, &modelPlayer == m_modelPlayer);
     293    m_readyPromise->reject(Exception { AbortError });
    263294}
    264295
     
    553584}
    554585
     586const char* HTMLModelElement::activeDOMObjectName() const
     587{
     588    return "HTMLModelElement";
     589}
     590
     591bool HTMLModelElement::virtualHasPendingActivity() const
     592{
     593    // We need to ensure the JS wrapper is kept alive if a load is in progress and we may yet dispatch
     594    // "load" or "error" events, ie. as long as we have a resource, meaning we are in the process of loading.
     595    return m_resource;
     596}
     597
    555598#if PLATFORM(COCOA)
    556599Vector<RetainPtr<id>> HTMLModelElement::accessibilityChildren()
  • trunk/Source/WebCore/Modules/model-element/HTMLModelElement.h

    r286406 r286836  
    2828#if ENABLE(MODEL_ELEMENT)
    2929
     30#include "ActiveDOMObject.h"
    3031#include "CachedRawResource.h"
    3132#include "CachedRawResourceClient.h"
     
    5051template<typename IDLType> class DOMPromiseProxyWithResolveCallback;
    5152
    52 class HTMLModelElement final : public HTMLElement, private CachedRawResourceClient, public ModelPlayerClient {
     53class HTMLModelElement final : public HTMLElement, private CachedRawResourceClient, public ModelPlayerClient, public ActiveDOMObject {
    5354    WTF_MAKE_ISO_ALLOCATED(HTMLModelElement);
    5455public:
     
    5859    void sourcesChanged();
    5960    const URL& currentSrc() const { return m_sourceURL; }
     61    bool complete() const { return m_dataComplete; }
    6062
    6163    // MARK: DOM Functions and Attributes
     
    107109    void setSourceURL(const URL&);
    108110    void modelDidChange();
     111    void createModelPlayer();
    109112
    110113    HTMLModelElement& readyPromiseResolve();
     114
     115    // ActiveDOMObject
     116    const char* activeDOMObjectName() const final;
     117    bool virtualHasPendingActivity() const final;
    111118
    112119    // DOM overrides.
     
    115122    // Rendering overrides.
    116123    RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&) final;
     124    void didAttachRenderers() final;
    117125
    118126    // CachedRawResourceClient overrides.
     
    139147    bool m_dataComplete { false };
    140148    bool m_isDragging { false };
     149    bool m_shouldCreateModelPlayerUponRendererAttachment { false };
    141150
    142151    RefPtr<ModelPlayer> m_modelPlayer;
  • trunk/Source/WebCore/Modules/model-element/HTMLModelElement.idl

    r286643 r286836  
    3131    [URL] readonly attribute USVString currentSrc;
    3232
     33    readonly attribute boolean complete;
    3334    readonly attribute Promise<HTMLModelElement> ready;
    3435
  • trunk/Tools/ChangeLog

    r286823 r286836  
     12021-12-09  Antoine Quint  <graouts@webkit.org>
     2
     3        [Model] Add load and error events to distinguish resource load from model readiness
     4        https://bugs.webkit.org/show_bug.cgi?id=233706
     5        rdar://85922697
     6
     7        Reviewed by Chris Dumez and Dean Jackson.
     8
     9        Use the "load" event instead of the "ready" promise for this test which only requires monitoring
     10        the <model> resource being loaded.
     11
     12        * TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm:
     13        (TestWebKitAPI::TEST):
     14
    1152021-12-09  Aditya Keerthi  <akeerthi@apple.com>
    216
  • trunk/Tools/TestWebKitAPI/Tests/ios/DragAndDropTestsIOS.mm

    r283563 r286836  
    21982198   
    21992199    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()]);
    2200     [webView synchronouslyLoadHTMLString:@"<model><source src='model://cube.usdz'></model><script>document.getElementsByTagName('model')[0].ready.then(() => { window.webkit.messageHandlers.modelLoading.postMessage('READY') });</script>"];
     2200    [webView synchronouslyLoadHTMLString:@"<model><source src='model://cube.usdz'></model><script>document.querySelector('model').addEventListener('load', event => window.webkit.messageHandlers.modelLoading.postMessage('READY'));</script>"];
    22012201
    22022202    while (![messageHandler didLoadModel])
Note: See TracChangeset for help on using the changeset viewer.