Closed Bug 272471 Opened 20 years ago Closed 20 years ago

[FIX]Revamp the docshell/docloader relationship

Categories

(Core :: DOM: Navigation, defect, P1)

x86
Linux
defect

Tracking

()

RESOLVED FIXED
mozilla1.8alpha6

People

(Reporter: bzbarsky, Assigned: bzbarsky)

References

(Blocks 1 open bug)

Details

Attachments

(1 file, 7 obsolete files)

The plan is as follows: 1) Make docshell inherit from docloader 2) Remove some methods on scriptable apis that should never have been exposed 3) Simplify a bunch of code 4) Take the first step to finally killing off nsIWebShell
Attached patch Proposed patch (obsolete) — Splinter Review
Comment on attachment 167472 [details] [diff] [review] Proposed patch jst, darin, could you review? biesi, any comments you have would also be quite welcome! Summary of changes: nsIDocumentLoader/nsDocLoader: * remove createDocumentLoader and all callers * remove destroy() method * remove ability to set container (leave the getter for now, but I think we can remove that too). * Add some utility methods * Convert child storage to not hold refs to the kids * Make QI threadsafe (As docshell's was) nsIURILoader/nsURILoader: * remove getDocumentLoaderForContext * remove SetupLoadCookie * remove docloader contractid (so they can't be instantiated on their own, except via the service's contractid). nsIDocShell*/nsDocShell: * Inherit from nsDocLoaderImpl * Clean up inheritance tree and QI/GetInterface impls to eliminate things that nsDocLoaderImpl inherits from or QIs to or hands out via GetInterface. * Fix GetInterface and QI to forward to nsDocLoaderImpl * Make newly-created docshells be kids of the root docloader * Remove ability to set parent treeitems (forces use of addChild/removeChild apis). * Eliminate mLoadCookie and fix relevant code to just use |this|. * Enforce that all kids of docshells are nsDocLoaderImpl instances nsIWebShell/nsWebShell: * Remove unused code, clean up GetInterface impl a lot. * Remove GetDocumentLoader Mailnews: * Stop using GetDocumentLoader and nsIWebShell in some places nsWebBrowser: * Make changes to deal with nsIDocShellTreeItem api change. Plan is to move the parent of treeitem altogether, since it makes no sense for nsWebBrowser.
Attachment #167472 - Flags: superreview?(jst)
Attachment #167472 - Flags: review?(darin)
Priority: -- → P1
Summary: Revamp the docshell/docloader relationship → [FIX]Revamp the docshell/docloader relationship
Target Milestone: --- → mozilla1.8alpha6
Very interesting. Expect that it'll take me some time to review this carefully.
Makes sense. This definitely needs a careful review... biesi said he'd look too. My only constraint is that I will likely be unable to check anything in between Dec 10 and Jan 2, but it seems that 1.8a6 closes on Jan 4, so I can still get it in before freeze if it gets reviews by then. I'd dearly like to get this (and better yet the final removal of nsIWebShell/nsIWebShellContainer that I can do once this lands) in 1.8a6...
Comment on attachment 167472 [details] [diff] [review] Proposed patch I noticed a place where I forgot a null-check.
Attachment #167472 - Flags: superreview?(jst)
Attachment #167472 - Flags: superreview-
Attachment #167472 - Flags: review?(darin)
Attachment #167472 - Flags: review-
Attached patch With that null-check (obsolete) — Splinter Review
Attachment #167472 - Attachment is obsolete: true
Attachment #167874 - Flags: superreview?(jst)
Attachment #167874 - Flags: review?(darin)
Comment on attachment 167874 [details] [diff] [review] With that null-check Wrong file....
Attachment #167874 - Flags: superreview?(jst)
Attachment #167874 - Flags: superreview-
Attachment #167874 - Flags: review?(darin)
Attachment #167874 - Flags: review-
Attached patch Right file this time (obsolete) — Splinter Review
Attachment #167874 - Attachment is obsolete: true
Attachment #167878 - Flags: superreview?(jst)
Attachment #167878 - Flags: review?(darin)
Comment on attachment 167878 [details] [diff] [review] Right file this time uriloader/base/nsIDocumentLoader.idl void fireOnLocationChange(in nsIWebProgress aWebProgress, this can now be removed from the idl and be a protected method it seems there won't be much left here :-) uriloader/base/nsDocLoader.h + // Needed to deal with ambiguous inheritance from nsISupports... + static nsISupports* GetAsSupports(nsDocLoaderImpl* aDocLoaderImpl) { I find this kinda ugly... but ok. but, can't you make this an instance method? then you need no parameter.. + nsIDocumentLoader* ChildAt(PRInt32 i) { + return NS_STATIC_CAST(nsDocLoaderImpl*, mChildList[i]); wish we had a template array class for stuff like this... seems like it should be pretty easy to wrap one around nsVoidArray... Hm... looks like you are eliminating SetLoadCookie. this slightly worries me... code might have made assumptions about it... it does exist on a frozen interface, after all... there's also: http://lxr.mozilla.org/seamonkey/source/mailnews/compose/src/nsURLFetcher.cpp#1 93 and there are various other impls that you could clean up, and eliminate member vars from... that said, I like the change :-) uriloader/base/nsCURILoader.idl hm... am I misreading this, or does this file contain _two_ #defines for NS_DOCUMENTLOADER_SERVICE_CID, only one of which you are removing? this file could use more comments... well, not in this bug :) uriloader/base/nsDocLoader.cpp + // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refocount to 1, is "refcount" :-) + if (!aSupports) { + return nsnull; is that nullcheck needed? + nsDocLoaderImpl* ptr; + aSupports->QueryInterface(kThisImplCID, (void**) &ptr); hmm, with use of NS_DEFINE_STATIC_IID_ACCESSOR you could use CallQueryInterface here... And that would also simplify AddDocLoaderAsChildToRoot (hm.. maybe...) nsDocLoaderImpl::GetContainer(nsISupports** aResult) loader.container == loader? nice :) + for (i=0; i < count; i++) could declare the var in the for, and please add a space around = also, can you move the count decl to where it's used? NS_IMETHODIMP nsDocLoaderImpl::GetDOMWindow(nsIDOMWindow **aResult) + return CallGetInterface(this, aResult); I don't like this... why not return null and override this in the docshell? (or return error here) docshell/base/nsDocShell.h + // Need this here because otherwise nsIWebNavigation::Stop + // overrides the docloader's Stop() more to come!
Comment on attachment 167878 [details] [diff] [review] Right file this time docshell/base/nsDocShell.h + NS_FORWARD_NSISECURITYEVENTSINK(nsDocLoaderImpl::); that trailing comma will cause problems with gcc 3.4 and -pedantic hm, docshell implements ridiculously many interfaces... docshell/base/nsWebShell.h + // That overrides an nsIDocumentLoader method, so redeclare and forward + NS_IMETHOD GetContainer(nsISupports** aContainer) { + return nsDocLoaderImpl::GetContainer(aContainer); I don't understand this comment... can you explain? what's "that"? docshell/base/nsDocShell.cpp else { - return QueryInterface(aIID, aSink); + return nsDocLoaderImpl::GetInterface(aIID, aSink); hm, interesting... I assume you verified that this is safe :-) + // Only allow setting the type on root docshells I see that this code did that before too, but why? + nsCOMPtr<nsIDocShellTreeItem> parent = + do_QueryInterface(GetAsSupports(mParent)); hmm, you're inconsistent with constructor-style vs using = here... + nsCOMPtr<nsIWebProgress> webProgress = + do_QueryInterface(GetAsSupports(this)); same... + nsRefPtr<nsDocLoaderImpl> childAsDocloader = GetAsDocLoaderImpl(aChild); can you use consistent capitalization here? ;) (Docloader vs DocLoader) + // Make sure to remove the child from its current parent. + nsDocLoaderImpl* childsParent = childAsDocloader->GetParent(); + childsParent->RemoveChildGroup(childAsDocloader); what if the child does not have a parent? oh... guess all docloaders have a parent (the root, if nothing else); maybe you should mention that somewhere (GetParent documentation, or here). does that mean GetParent() should be renamed to Parent()? + nsresult res = AddChildGroup(childAsDocloader); + NS_ENSURE_SUCCESS(res, res); can we stay with "rv"? oh... you moved this var... ok, so leave it as res if you want // XXX in that case docshell hierarchyand SH hierarchy won't match. wanna add the missing space here, while touching the code around it? + nsresult res = RemoveChildGroup(childAsDocloader); can you name this rv? (or is "res" the style of this file?) + NS_WARN_IF_FALSE(aIndex >=0 && aIndex < mChildList.Count(), "index of child element is out of range!"); wanna add a space after >=, and maybe wrap this so it fits into 80 cols? + // XXXbz We could also pass |this| to nsIURILoader::Stop. That will + // just call Stop() on us as an nsIDocumentLoader... We need fewer + // redundant apis! + Stop(); if you ask me, we should put loadgroups into loadgroups and call mLoadGroup->Cancel(NS_BINDING_ABORTED); anyway... (guess we need stop anyway, via nsIWebNavigation) there are a few more inconsistencies of "= do_QI(...)" vs "(do_QI(..))" - srcContainer->GetChildAt(i, getter_AddRefs(srcChild)); + srcContainer->GetChildAt (i, getter_AddRefs(srcChild)); why this? doesn't seem to be file style, cf. GetChildCount a few lines above - if(aIID.Equals(NS_GET_IID(nsILinkHandler))) why can you remove this? oh I see... nsDocLoaderImpl implements GetInterface via QueryInterface... so I guess you can ignore my above comment about the replacing of "return QI" with "return doclaoderimpl::GI"; but I'm not sure it's wise to rely on this... webshell/public/nsIWebShell.h - /** - * Return the nsIDocumentLoader associated with the WebShell. - */ - NS_IMETHOD GetDocumentLoader(nsIDocumentLoader*& aResult) = 0; you should change the IID uriloader/exthandler/nsExternalHelperAppService.cpp - nsCOMPtr<nsIDocumentLoader> origContextLoader; - uriLoader->GetDocumentLoaderForContext(mWindowContext, getter_AddRefs(origContextLoader)); + nsCOMPtr<nsIDocumentLoader> origContextLoader = + do_GetInterface(mWindowContext); great! it seems like uriLoader is unused now, can you remove it? NS_IMETHODIMP nsMsgWindow::GetRootDocShell(nsIDocShell * *aDocShell) { - if(!aDocShell) - return NS_ERROR_NULL_POINTER; is this safe? this is mailnews after all... mailnews/base/util/nsMsgMailNewsUrl.cpp + m_loadGroup = do_GetInterface(docShell); is this correctly indented? seems to me like it doesn't match with the } above embedding/browser/webBrowser/nsWebBrowser.cpp NS_IMETHODIMP nsWebBrowser::GetParent(nsIDocShellTreeItem** aParent) + *aParent = nsnull; hm, hope that's OK... this patch is great stuff! thanks for doing this.
Good catch with FireOnLocationChange. I'd rather not have GetAsSupports an instance, so I can safely call it on null pointers. > wish we had a template array class for stuff like this I do too... If I run into it again, it'll happen. > Hm... looks like you are eliminating SetLoadCookie. Yeah. That bothered me too, a little. On the other hand, this is clearly documented as an opaque pointer. People using it really deserve what they get. I could fix the URLFetcher code, or I could just keep calling SetLoadCookie in URILoader (and assert that it's the docshell in the docshell code). Darin, do you have an opinion here? Calling it is kinda ugly, but less likely to break people, I suppose... > does this file contain _two_ #defines It does. I eliminated one of them and left the other so people have the define after all.... ;) > is that nullcheck needed? I think so... callers just pass random stuff (some of it potentially coming from JS) to this function, and CallQI will crash if passed null. > loader.container == loader? nice :) Yes. I left the getter for backwards compat for now, but this method could easily go away too.... > I don't like this... why not return null and override this in the docshell? Because that would be more code? This basically preserves the old codepath, and means we don't have to worry about where the DOMWindow comes from, exactly. > I don't understand this comment... can you explain? The "that" is the GetContainer method immediately above this one... I'll clarify the comment. > hm, interesting... I assume you verified that this is safe :-) Absolutely. ;) > I see that this code did that before too, but why? Security reasons, I suspect... in any case, I was trying to not change existing docshell functionality... this patch is scary enough as-is. > hmm, you're inconsistent with constructor-style vs using = here... I use the one more likely to fit in 80 chars, basically... and line-breaking in the middle of the constructor-style one is annoying. > what if the child does not have a parent? Then we crash. I needed a null-check here (eg kids of a destroyed docshell would have no parent). Thanks for catching this! > can we stay with "rv"? Not unless I update the rest of this function. And there's no reason to touch all that code. > if you ask me, we should put loadgroups into loadgroups Yes, but not this patch... ;) > (guess we need stop anyway, via nsIWebNavigation) That's a different Stop(); it takes an argument. So we have 3 different ways to call Stop() on a docshell -- via docloader, uriloader, and webnavigation. Hence "fewer redundant apis". > hm, hope that's OK... So do I, and from talking to danm it sounded like it ought to be... the parent stuff should really not live on the same interface as what nsWebBrowser implements, but refactoring the nsIDocShell* interfaces is a battle for a separate patch. ;) I'll fix the other things you pointed out.
Attached patch Updated to biesi's comments (obsolete) — Splinter Review
Attachment #167878 - Attachment is obsolete: true
Attachment #167878 - Flags: superreview?(jst)
Attachment #167878 - Flags: superreview-
Attachment #167878 - Flags: review?(darin)
Attachment #167878 - Flags: review-
Attachment #167899 - Flags: superreview?(jst)
Attachment #167899 - Flags: review?(darin)
Blocks: 261086
Blocks: 273319
Comment on attachment 167899 [details] [diff] [review] Updated to biesi's comments class nsDocLoaderImpl : public nsIDocumentLoader, ... Wanna drop "Impl" from the class name while you're at it, it's not like nsDocLoader would conflict with anything, is it? And drop it from GetAsDocLoaderImpl() too, maybe? Not a big deal, but I find it very unnecessary to put "Impl" in classnames for no good reason... - In nsDocLoaderImpl::DestroyChildren(): + if (count > 0) + { + for (i=0; i < count; i++) ... + mChildList.Clear(); I guess I don't see the point of that if check, all it saves us is the for loop that won't do anything, and a mChildList.Clear() call on an already empty list. - In nsDocShell::IsFrame(): - if (mParent) { + nsCOMPtr<nsIDocShellTreeItem> parent = + do_QueryInterface(GetAsSupports(mParent)); + if (parent) { PRInt32 parentType = ~mItemType; // Not us - mParent->GetItemType(&parentType); + parent->GetItemType(&parentType); if (parentType == mItemType) // This is a frame return PR_TRUE; } Couldn't this use GetSameTypeParent() to save us a bit of code duplication? sr=jst
Attachment #167899 - Flags: superreview?(jst) → superreview+
I'll make those changes. I also made two more changes: 1) I added the following block of code to addChild: // Make sure we're not creating a loop in the docshell tree nsDocLoaderImpl* ancestor = this; do { if (childAsDocLoader == ancestor) { return NS_ERROR_ILLEGAL_VALUE; } ancestor = ancestor->GetParent(); } while (ancestor); and added comments to nsIDocShellTreeNode to indicate that this exception would be thrown in this circumstance. 2) I fixed nsDocShell::GetInterface not to return NS_OK if it's setting the out param to null (which it could do in some cases).
Attached patch Updated to jst's sr comments (obsolete) — Splinter Review
Attachment #168169 - Flags: review?(darin)
Attachment #167899 - Flags: review?(darin)
Comment on attachment 168169 [details] [diff] [review] Updated to jst's sr comments >Index: uriloader/base/nsIDocumentLoader.idl > * An nsIDocumentLoader is a component responsible for tracking groups of loads nsIDocumentLoader is not a component; it's an interface ;-) >+ readonly attribute nsILoadGroup loadGroup; > > readonly attribute nsIChannel documentChannel; documentChannel == loadGroup.defaultLoadRequest, right? care to document more of nsIDocumentLoader? >Index: uriloader/base/nsDocLoader.h >+ static nsresult AddDocLoaderAsChildToRoot(nsDocLoader* aDocLoader); ...ChildOfRoot perhaps? >+ // Remove aChild from our childlist. This nulls out the child's mParent >+ // pointer. >+ nsresult RemoveChildGroup(nsDocLoader *aChild); >+ // Add aChild to our child list. This will set aChild's mParent pointer to >+ // |this|. >+ nsresult AddChildGroup(nsDocLoader* aChild); RemoveChildFromGroup / AddChildToGroup? or maybe just Remove/AddChild? >Index: uriloader/base/nsDocLoader.cpp >-NS_INTERFACE_MAP_END >+ if (aIID.Equals(kThisImplCID)) >+ foundInterface = NS_STATIC_CAST(nsIDocumentLoader *, this); >+ else >+NS_INTERFACE_MAP_END_THREADSAFE you can just use NS_INTERFACE_MAP_END; there's nothing special about NS_INTERFACE_MAP_END_THREADSAFE. i think it was intended to be deprecated. it's only AddRef and Release that have the concept of threadsafe vs. non-threadsafe. >+nsDocLoader::AddDocLoaderAsChildToRoot(nsDocLoader* aDocLoader) >+{ >+ nsresult rv; >+ nsCOMPtr<nsIDocumentLoader> docLoaderService = >+ do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv); any value in caching a pointer to the root docloader? >+nsDocLoader::GetContainer(nsISupports** aResult) > { >+ NS_ADDREF(*aResult = NS_STATIC_CAST(nsIDocumentLoader*, this)); > > return NS_OK; > } what does this mean anyways? can we fix the callers to just use the docloader as the container? >Index: docshell/base/nsDocShell.h >@@ -184,32 +184,33 @@ class nsDocShell : public nsIDocShell, ... >+ public nsDocLoader it might be better to list nsDocLoader first in the inherits from list. that way you avoid creating thunks for any methods that you override. >+ NS_IMETHOD Stop() { >+ // Need this here because otherwise nsIWebNavigation::Stop >+ // overrides the docloader's Stop() >+ return nsDocLoader::Stop(); >+ } Hmm... is this going to be fragile across compilers? Should we rename the stop method on nsIDocumentLoader? >+ // Need to implement (and forward) nsISecurityEventSink, because >+ // nsIWebProgressListener has methods with identical names... >+ NS_FORWARD_NSISECURITYEVENTSINK(nsDocLoader::) I wonder if it wouldn't make sense to create helper classes for some of these interfaces with overlapping methods. The same problem occurs with nsPipe (where we have both nsIInputStream::close and nsIOutputStream::close). >Index: docshell/base/nsIDocShellTreeNode.idl >+ alive only as long as it's referenced from outside the docshell tree. >+ @throws NS_ERROR_ILLEGAL_VALUE if child corresponds to the same >+ object as this treenode or an ancestor of this treenode. >+ // XXXbz this should take an nsIDocShellTreeNode, I think. > */ indentation nits in this file. looks like the existing code may use tabs. >Index: docshell/base/nsDocShell.cpp >+ prompter.swap((nsIPrompt*) *aSink); this might not compile on MSVC 6. i ran into trouble trying to do this with similar code before. nothing major, r=darin
Attachment #168169 - Flags: review?(darin) → review+
Darin, what are your thoughts on the load cookie stuff biesi raises in comment 9 (see my comments on it in comment 11)? I'm thinking I should try to keep calling SetLoadCookie for now and just pass the docshell.... and file a followup to consider eliminating that call. > documentChannel == loadGroup.defaultLoadRequest, right? Yes. I'll add some comments to nsIDocumentLoader. Note that biesi and I were thinking that the interface could really just work on going away. The only thing it provides at this point that's useful is the loadgroup, and we're not sure we need a whole interface for that. > ...ChildOfRoot perhaps? Will do. > RemoveChildFromGroup / AddChildToGroup? or maybe just Remove/AddChild? If I do AddChild/RemoveChild, I end up with shadowing from the nsIDocShellTreeNode methods, so I'd have to declare the methods in docshell and forward... How about AddChildLoader/RemoveChildLoader? > any value in caching a pointer to the root docloader? Maybe. This isn't really code that should run that often, so I didn't bother for now. > what does this mean anyways? can we fix the callers to just use > the docloader as the container? I'm not sure what it means, to be truthful. I just left it as-is for now, but I plan to do a followup bug on this. > it might be better to list nsDocLoader first in the inherits > from list. OK. That will change the nsISupports pointer that gets passed in when "this" is passed to methods... but that really shouldn't matter, I guess. > Hmm... is this going to be fragile across compilers? No, because nsIWebNavigation::Stop has a different signature. The problem is that the method shadowing stuff shadows even methods with different signatures, for some reason. > Should we rename the stop method on nsIDocumentLoader? I think we should work on removing the stop method on nsIDocumentLoader... ;) > this might not compile on MSVC 6. I'll watch out for that. I assume we have a tinderbox that uses it? I'll post a patch updated to these comments tomorrow (and check it in, once the loadcookie issue is decided one way or another).
> Darin, what are your thoughts on the load cookie stuff biesi raises... I don't think we should mess with frozen interfaces (nsIURLContentListener:: loadCookie I presume), even if they happen to be poorly documented. It might be wise to preserve as much of the behavior as possible. Opaque or not, people may have grown up depending on this interface for some unfathomable reason. > How about AddChildLoader/RemoveChildLoader? Yeah, that works. > > any value in caching a pointer to the root docloader? > > Maybe. This isn't really code that should run that often, so I didn't bother > for now. OK > > it might be better to list nsDocLoader first in the inherits > > from list. > > OK. That will change the nsISupports pointer that gets passed in when "this" > is passed to methods... but that really shouldn't matter, I guess. /me hopes we don't have any weird dependencies on that. > > Should we rename the stop method on nsIDocumentLoader? > > I think we should work on removing the stop method on nsIDocumentLoader... ;) Sounds good! > > this might not compile on MSVC 6. > > I'll watch out for that. I assume we have a tinderbox that uses it? Yes, of course! The release builds also use VC6 IIRC.
Attached patch Updated to Darin's comments (obsolete) — Splinter Review
I added a hack to deal with the loadcookie thing, but I do think it's safe to remove it. The only people who would be bitten are those using the unfrozen nsIURILoader api and passing a context that's not a window to it. I certainly hope there aren't very many such people.... (the mailnews code in question was simply copy-pasted from docshell, comments included; I will file a followup bug to remove that code altogether).
Attachment #167899 - Attachment is obsolete: true
Attachment #168169 - Attachment is obsolete: true
Attached patch Fix more indent nits (obsolete) — Splinter Review
Attachment #168224 - Attachment is obsolete: true
Attached patch For checkinSplinter Review
The last diff had an unwanted hunk in the exthandler; I removed that here.
Attachment #168226 - Attachment is obsolete: true
Blocks: 273760
Checked in. This actually increased codesize a tad, due to increases in the sizes of vtables for docshell and webshell and more thunks (for the overridden addref/release methods, etc). Hopefully working on merging webshell into docshell will help with that. I filed bug 273760 on deciding what to do with the loadcookie issue.
Status: NEW → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: