salesforce / kagekiri

Shadow DOM-piercing query APIs for the browser.
BSD 3-Clause "New" or "Revised" License
86 stars 9 forks source link

Kagekiri querySelector not working #95

Open shivamothkuri opened 6 months ago

shivamothkuri commented 6 months ago

This is my CSS selector div.filters > div:nth-child(1) select.templateInputs

This works for Puppeteer and devtools element search. But, don't work in Kagekiri

When I digged into the source code, found that this is what happening.

Step1: select.templateInputs has a parent div - which resolves to div.uiInput Step2: div.uiInput has any direct(because of > selector) parent with div.filters? No. It fails here!

Sample html

<div class="filters runtime_sales_emailtemplateuiTemplateInsertDialog" data-aura-rendered-by="3135:0"
                data-aura-class="runtime_sales_emailtemplateuiTemplateInsertDialog">
                <div>
                    <div class="uiInput uiInputSelect uiInput--default uiInput--select"
                        data-aura-class="uiInput uiInputSelect uiInput--default uiInput--select"><label for="3137:0"
                            class="uiLabel-top form-element__label uiLabel" data-aura-rendered-by="3238:0"
                            data-aura-class="uiLabel"><span id="3137:0-label" title="Templates" class=""
                                data-aura-rendered-by="3239:0">Templates</span></label>

                              <select size="1" aria-describedby="" id="" class="templateInputs select" data-aura-rendered-by="3142:0" data-interactive-lib-uid="14"><!--render facet: 3143:0-->
                                   <option label="My Templates" value="MyLightning">My Templates</option>
                                   <option label="All Templates" value="AllLightning">All Templates</option>
                             </select>
                 </div>
</div>
</div>
nolanlawson commented 6 months ago

Hi @shivamothkuri is there any chance you could provide a minimal reproducible test case using something like stackblitz.com or codepen.io?

nolanlawson commented 6 months ago

@shivamothkuri Actually scratch that, I threw together a CodePen repro based on your description: https://codepen.io/nolanlawson-the-selector/pen/jORKJvg

It indeed seems to demonstrate that kagekiri does not reproduce the same behavior as document.querySelector. Thank you!

nolanlawson commented 6 months ago

Reduction of the issue:

<div class="filters">
  <div>
    <div>
      <select class="templateInputs"></select>
    </div>
  </div>
</div>
// null
kagekiri.querySelector('div.filters > div:nth-child(1) select.templateInputs')

// <select class="templateInputs"></select>
document.querySelector('div.filters > div:nth-child(1) select.templateInputs')
nolanlawson commented 6 months ago

I think I understand the problem now - basically, as we are matching right-to-left, there is a problem of ambiguity. You can imagine adding multiple layers of <div>s above to reproduce the issue.

Essentially, kagekiri works right-to-left, and when it matches the div:nth-child(1), it continues matching to the left. But since it grabs the nearest div:nth-child(1), which is the one immediately surrounding .templateInputs, it thinks the selector does not match because it "should" have matched the <div> one level higher, whose parent is .filters.

You can imagine the algorithm for this getting fairly nasty, as there is potentially a combinatorial explosion of possibilities that kagekiri has to match against.