Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a new attemptCollapse function #7532

Merged
merged 5 commits into from Feb 16, 2017

Conversation

zhouyx
Copy link
Contributor

@zhouyx zhouyx commented Feb 14, 2017

when we try to collapse an element we do things like:

element.attemptChangeHeight(0).then(() => {
  element.collapse();
})

We do some measurements after height is set to 0, and before we call element.collapse(); However with display:inline-block, it is still possible for layout to change from height = 0 to display: none. [Looks like a well know CSS issue] Then the previous measure will give wrong result. https://github.com/ampproject/amphtml/blob/master/src/service/resources-impl.js#L1036

In this PR I am trying to introduce a new method call attemptCollapse, and it will call resources scheduleChangeSize_ with a callback function to set display none.

Still need to work on adding test coverage.

return this.attemptChangeHeight(0).then(() => {
this./*OK*/collapse();
}, () => {});
return this.attemptCollapse().then(() => {}, () => {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just need a catch block.

*/
attemptCollapse() {
return this.element.getResources().attemptCollapse(
this.element, 0, /* newWidth */ undefined);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to pass width and height?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fail to cleanup, will delete

@@ -412,6 +413,10 @@ export class Resource {

this.element.updateLayoutBox(box);
}
/**
* Collapse on changing height/width to 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My fault. Will clean up

* @return {!Promise}
*/
attemptCollapse(element) {
return new Promise((resolve, reject) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just call #attemptChangeSize:

attemptCollapse(element) {
  this.attemptChangeSize(element, 0, undefined).then(() => {
    const resource = Resource.forElement(element);
    resource.completeCollapse();
  });
}

Copy link
Contributor Author

@zhouyx zhouyx Feb 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the thing that I want to fix in this PR.

#attemptChangeSize success, resources manager does some calculation to adjust scrollTop, we then immediately collapse the element the previous measurement gives incorrect value which results in a page jump.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh. 👍

@jridgewell
Copy link
Contributor

We need a really good test that will fail if anyone ever does #7532 (comment).

ads/_ping_.js Outdated
@@ -71,6 +71,8 @@ export function _ping_(global, data) {
});
}
} else {
global.context.noContentAvailable();
window.setTimeout(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this work: global.setTimeout(global.context.noContentAvailable, 1000) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, I changed to using global

@zhouyx
Copy link
Contributor Author

zhouyx commented Feb 14, 2017

@jridgewell You have any ideas on the test?
The best I can think of is a presubmit check of calling #attemptChangeHeight(0)

@jridgewell
Copy link
Contributor

I don't understand where the measure is coming from that is incorrect. But you could stub that, and make sure the element is display: none.

@zhouyx
Copy link
Contributor Author

zhouyx commented Feb 14, 2017

@jridgewell that won't help me from preventing anyone from calling.

attemptChangeHeight(0).then(() => {
  collapse()
});

As a call like this is still valid. And they would both trigger measurement.

attemptChangeHeight(0).then(() => {
  // do something that don't affect change layout
});

@jridgewell
Copy link
Contributor

Sorry, I missed the second sentence in your comment. dev.assert() that newWidth and newHeight do not equal 0.

But, I was talking about prevent someone from doing the refactor I suggested. We need a test to make sure that doesn't happen.

/**
* Return a promise that request the runtime to collapse one element
* @return {!Promise}
* @public
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted

@zhouyx
Copy link
Contributor Author

zhouyx commented Feb 15, 2017

@jridgewell Finally add some test coverage. Ugly test though...
BTW I add a presubmit check to warn on calling of attemptChangeHeight(0) I think when people try to change height to 0, they are most likely trying to do the same thing as attemptCollapse().

@zhouyx
Copy link
Contributor Author

zhouyx commented Feb 15, 2017

👀

@@ -566,6 +566,7 @@ var forbiddenTermsSrcInclusive = {
// Functions
'\\.changeHeight\\(': bannedTermsHelpString,
'\\.changeSize\\(': bannedTermsHelpString,
'\\.attemptChangeHeight\\(0\\)': 'please consider using `attemptCollapse()`',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use a dev().assert? We could catch both height = 0 and width = 0 changes then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good point. But it is possible that an iframe request leads runtime to call attemptChangeSize(newWidth=0, newHeight=0). It is perfectly valid without setting element display to none afterwards.
The thing I want avoid is us explicitly put 0 as the input. dev().assert is too strict, maybe I can switch to dev().warn. honestly i feel presubmit check should be sufficient.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, let's forward to #attemptCollapse when we receive 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was planning to do at first, just add a check inside #attemptChangeSize. Then we realize once an iframe send us request to resize to (0, 0), they will never be able to resize again because we don't remove the display:none for them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍. Let's leave as is.

@@ -1824,6 +1833,67 @@ describe('Resources mutateElement and collapse', () => {
});
});

it('attemptCollapse should not call attemptChangeSize', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're targeting the scrollAdjSet path, right?

If so, we should be able to avoid the getScrollHeight stub, and just assert that resource1#completeCollapse has ben called before resource1#getLayoutBox.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but #completeCollapse gets called after #getLayoutBox.
https://github.com/ampproject/amphtml/blob/master/src/service/resources-impl.js#L1029
Or should I reorder the call, as I feel the order doesn't matter here. It makes more sense to call #changeSize first and then measure layout box.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I confused the sync and async paths. I think re-ordering the calls might be best.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will go with re-ordering calls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for going back and forth. But few things I found:

  1. request.callback() can do something to affect element layoutBox. For example we are setting resource.isFixed to false when we call #completeCollapse in the callback. This won't be an issue for us because we will also manually update element layoutBox in #completeCollapse call.
  2. So I can still go with re-ordering calls, and assert #completeCollapse gets called before #getLayoutBox. However just like what I do with #getScrollHeight #getLayoutBox gets called 2 times in the test path. here and here. I fell I will still need to stub #getLayoutBox to assert on the second call. which makes no difference between my current test approach.

One other thing that's not related to this PR:
if we resize an fixed element, is it likely that it won't affect other elements layout on the page. Maybe we can add some optimization by not updating minTop here if element is position fixed.

@zhouyx zhouyx merged commit 3e02f1c into ampproject:master Feb 16, 2017
@zhouyx zhouyx mentioned this pull request Feb 21, 2017
mrjoro pushed a commit to mrjoro/amphtml that referenced this pull request Apr 28, 2017
* still need tests

* need to fix test

* fix lint

* nit

* add test coverage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants