Before implementing callbacks into MouseLock, I followed David Humphrey’s suggestion and decided to take a closer look at how the nsGeolocation was implementing callbacks.
The nsGeolocation class has a lot more functionality than the MouseLock, so at the beginning it was hard to pin point the exact place where the callbacks were handled.
After a few hours searching on mxr and a bunch of printf’s , I was able to find how the callbacks were being stored and fired
- Ensures that a callback is passed, the failure callback is optional, but the successful is mandatory
- Checks if geolocation is enabled by the user
- When the nsGeolocation is instantiated, it creates a nsGeolocationService object, the geolocationService object calls the static function GeoEnabledChangedCallback, that sets the static bool variables sGeoEnabled(by default its set to true)
- The geolocation preference is called “geo.enabled” and can be set via the browser on the about:config page.
- Check if the pending callbacks are bigger than the max number of geo requests per window(1500)
- Creates a nsGeolocationRequest object
- Checks if the request object was successfully created
- Checks if the request object was successfully initialized
- Not sure why this check happens, since the Init method for the nsGeolocationRequest returns NS_OK
- Calls nsGeolocation::RegisterRequestWithPrompt and check if the action was successful
- Checks if the geo.prompt.testing preference is enabled( I couldn’t access this preference in the about:config page)
- Check if the XREGetProcessType() equals to GeckProcessTypeContent
- The XRE_GetProcessType returns a mozilla::startup::sChildProcessType, that it is a GeckProcessType
- GeckoProcessType_Content it’s an enum.
- I didn’t go any further on this topic, so I’m not sure what is exactly doing.
- If the XRE_GetProcessType() is equal to GeckoProcessTypeContent, it then get a reference to a nsPIDOMWindow, by querying mOwener(a reference to the current inner window)
- Gets a TabChild from the nsPIDOMWindow
- It calls the AddRef method of the nsGeolocationRequest object, here is the comment in the source code: “Retain a reference so the object isn’t deleted without IPDL’s knowledge.
- Corresponding release occurs in DeallocPContentPermissionRequest.” When implementing the callbacks for MouseLock I got stuck in this part, I’ll comeback later and inspect to see what really happends when the AddRef mehtod is called
- The TabChild calls the SendPContentPermissionRequestConstructor
- After doing a search on mxr for SendPCOntentPermissionRequestConstructor, the only other file that used this method was the nsDesktopNotification.cpp. Looking at the code in the DesktioNotification file I saw that was very similar to the one used in the Geolocation, probably because was the same person who wrote it Doug Turner(dougt). Anyway, the DesktopNotification class seems like a good class to dive into next.
- Back to the SendPContentPermissionRequestConstructor, the closest method I could find on mxr was SendPContentDialogConcstructor. Digging a little bit more I found the nsIContentPermissionPrompt.idl.
- The nsGeolocationRequest extends the nsIContentPermissionRequest, that’s where the nsIContentPermissionRequest is defined.
- My guess is that SendPContentPermissionRequestConstructor allows something make requests from a browser tab, in this case, probably requesting the geolocation status.
- Calls Sendpromt on the nsGeolocationRequest object
- Creates a new RequestPromptEvent object
nsGeolocation::RegisterRequestWithPrompt – Dispatch to Main Thread
- Dispatches the object to the main thread
- This is the furthest I go into inspecting threads
- RequestPromptEvent::Run method is called
- It creates a nsIContentPermissionPrompt object by getting the service from NSCONTENTPERMISSIONPROMPTCONTRACTID
- Some comments for the interface: “Interface provides a way for the application to handle the UI prompts associated with geo position.”
- If we implement prompts on MouseLock, we may need to create an interface as well, or maybe just reuse this one?
- If the nsIContentPermissionPrompt was successfully created, it then calls the method Prompt on the object, passing the nsGeolocationRequest as an argument.
- I searched on mxr but couldn’t find the Prompt method
- **I don’t know why or where the following methods are called, because I couldn’t find the definition for the Prompt method, but by examining the stack that’s the chain of method calls that happens
- nsGeolocationRequest::GetType gets called. It returns the string “geolocation”
- nsGeolocationRequest::GetUri gets called.
- It then calls the GetURI method of the nsGeolocation, since the nsGeolocationRequest holds a reference to the nsGeolocation object.
- The GetURI method of the nsGeolocation returns the mURI.
- mURI is of type nsIURI.
- To get mURI on the Init method, it gets from the doc->NodePrincipal->GetURI
- After it has a nsIURI object, it calls the forget method passing the aRequestingURI as a parameter.
- Here is the comments for the nsHtml5RefPtr::forget method:
- “Set the target of rhs to the value of mRawPtr and null out mRawPtr. Useful to avoid unnecessary AddRef/Release pairs with “out” parameters where rhs bay be a T* or an I* where I is a base class of T.”
- Not sure what is happening there
- nsGeolocationRequest::GetWindow gets called.
- Follow the same steps as the GetUri method.
- Waits for user response.
- After the user clicks OK on the prompt window, and shares the geolocation, the nsGeolocationRequest::Allow method gets called.
- I don’t know how the Allow gets called, since the only places in the nsGeolocation.cpp code that call Allow are: nsGeolocationRequest::Recvdelete, nsGeolocationRequest::WatchPosition and RequestAllowEvent::Run. I’ll keep debugging to see if I can find the stack trace for the Allow call, but I suspect it has something to do with the Prompt called in the RequestPromptEvent::Run.
- Anyway, the Allow gets called and this is what happens:
- Calls thensGeolocationService::GetIncstance method
- Starts the GeolocationService device
- Checks if the geolocation is enabled
- Calls SetDisconnectTimer
- If a nsTimer doesn’t exist, creates one by do_CreateInstance(“@mozilla.org/timer;1″)
- Looks like do_CreateInstance is another way to instantiate objects
- The timer is initialized passing as arguments, ‘this’ referring to the nsGeolocationService object, the delay for the timer, with a flag of TYPEONESHOT, that means it fires only once
- Checks if the XREGetProcessType() equals to GeckProcessTypeContent.
- If so, creates a ContentChild object and calls the SendAddGeolocationListener method
- Creates a nsIObserverService object. Still not clear what a nsIObserver does.
- It loops through the number of nsIGeolocationProvider the nsGeolocationService has it stored.
- It then starts the provider.
- Set the provider to watch the nsGeolocationService object
- Then start observing the provider.
- Some comments from the nsIGeolocationProvider.idl:
- “Interface provides location information to the nsGeolocator via the nsIDOMGeolocationCallback interface. After startup is called, any geo location change should call callback.update().”
- I guess that’s why the Update method gets called later on.
- Still lots of different components, but things are starting to make sense
- Checks if the geoService was successfully started.
- If not it Calls the NotifyError method passing as an argument nsIDOMGEOPositionError::POSITIONUNAVAILABLE
- nsIDOMGeoPositionError seems to be an idl with the error codes for the Geolocation class
- Notifying an error is how the failure callback gets called
- First thing that happens on the nsGeolocationRequest::NotifyError method is the creating of a nsDOMGeoPositionError object.
- It checks if the object was successfully created
- Calls the nsDOMGeoPositionError::NotifyCallback method passing the callback provided by the user as an argument
- On the nsDOMGeoPositionError:NotifyCallback. First thing it checks to see if the callback exists, because the failure callback is optional, sometimes it might be null. if it doesn’t exist, it returns.
- Creates a nsIJSContextStack by passing do_GetService(“@mozilla.org/js/xpc/ContextStack;1″) to its constructor.
- Checks if the stack was successfully created, if not, returns
- Calls the HandleEvent method of the error callback: nsIDOMGeoPositionCallback
- Removes the JSContext stack
- (Not sure what goes on here)
- Creates a nsIDOMGeoPosition object by calling the nsGeolocationService::GetCachedPosition
- GetCachedPosition returns the last GeoPosition stored in the GeolocationService
- Gets the cached position time by calling the GetTimeStamp.
- Checks if the cached value can be used by calling the GetMaximumAge of the nsIDOMGeoPositionOptions
- More checking if the cached position can be returned
- Then for some reason, the nsGeolocation::Update method gets called
- It checks if the window still exists. If not, it calls the nsGeolocationRequest::Shutdown.
- The Shutdown method cancels the nsITimer and sets the callbacks to null, and clear flag to true
- Loops through all pending callbacks(a nsGeolocationRequest array) and call Update() on all of them
- Checks if the callback should be allowed
- Creates a RequestSendLocationEvent object.
nsGeolocationRequest::Update – Dispatch to Main Thread
- Dispatches the object to the main thred
- Calls the RequestSendLocationEvent::Run.
- Calls the nsGeolocationRequest::SendLocation method
- Checks if the request was cleared, or if its not allowed
- Checks if the timer exists If so, cancel it.
- Checks if the nsIDOMGeoPostion is not null, since it can’t send a null position ins a successful callback.
- If the position is null, it calls the nsGeolocationRequest::NotifyError method, that will fire the failure callback
- Checks the nsIJSContextStack
- Calls the HandleEvent method on the success callback nsIDOMGeoPositionCallback
- Removes the JSContext from the stack
- If the watchPositionRequest is set to true, then calls the nsGeoLocationRequest::SetTimeoutTImer
- Back to the RequestSendLocationEvent::Run.
- If the object was initialized with a reference to the Geolocation object, it then calls the nsGeolocation::RemoveRequest method
- The Request method removes the request from the pendingCallback array, and then call the nsGeolocationRequest::MarkCleared method
- nsGeolocationRequest::MarkCleared method sets the mCleared to true
Back to nsGeolocation::GetCurrentPosition
- If the RegisterRequestWithPrompt was successful, it then stores the request on the mPendingCallbacks array
- Checks if the caller is the Chrome, I’m still not 100% of what Chrome means on firefox, but my guess here is that it’s checking if it’s actually the Chrome(Browser) is making the request and not somebody else?
- Curious that the value returned by the IsCallerChrome depends on the result of SubkectPrincipalIsSystem, and on the SubjectPrincipalIsSystem the argument passed it’s always set to true. Not sure why that is done, since it will always evaluate to true.
- Appends the request to the mPendingCallbacks.
- Creates a new RequestAllowEvent
- Dispatches the event to the main thread
- **Step 7 all the way to step 8 only happens if the mOwner exists, not sure why the mOwner wouldn’t exist, since here validates mOwner, and if it doesn’t exist returns NSERRORFAILURE