Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.

cookies

Post Reply
aneal
Posts: 9
Joined: Tue Jun 16, 2020 6:30 am

cookies

Post by aneal »

Hi,
I am trying to automate several web-based processes using a logged-in single user, where logged in/out state is stored with cookes. Although it would be more efficient to do this in OSM, one obvious way to demonstrate this would be to extend the CookieVisitor example by
1. drop in a TPageControl and two TTabsheets
2. set each tabsheet have its own TCEFWindowParent component and linked TChromium.
3. add a button mimicking 'Go' to enable the second CEFWindowParent to display the same website as the other tab

After making a few small code modifications I can confirm this does work in that I now have two tabs, each pointing at the same website.

However, if I login on one tab and refresh the other tab then it automatically logs in because it gets the login state by reading the cookie. This is not the desired state because I need each tab to be know whether it is logged-in independently of the other. Is it possible to do this?

Note if I try blocking the domain's cookie then the user is not able to login at all. Is it possible to do this?
Thanks in advance
Aneal
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: cookies

Post by salvadordf »

Hi,

If you need independent browsers in the same application then you have to create them using a different "request context".

Each set of browsers that have the same "request context" will share the cache and cookies so your user will be logged in in all of them. However, the browsers with a different "request context" will use different cache and cookies and your user will not be logged in.

See the MDIBrowser demo for all the details.
aneal
Posts: 9
Joined: Tue Jun 16, 2020 6:30 am

Re: cookies

Post by aneal »

Hi Salvadorf
The 'MDI browser' example does indeed fix my cookie issue - thank you.

Using the 'MDI browser' it is possible for each child browser window to be logged into the same website as a different user. Integrating code adapted from your 'domvisitor' example, it is also quite easy to automate this login process and do a PostMessage back to the VCL so I can see log each childs progress in another window. However, if I run these login processes concurrently rather than sequentially, only the last generated child completes the process while the others terminate early.

Do you have any clues as to why this is?
Thanks again
Aneal
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: cookies

Post by salvadordf »

Are you using a different request context for each of them and a different cache directory?

Are you setting GlobalCEFApp.RootCache to a common parent directory to all cache directories?
aneal
Posts: 9
Joined: Tue Jun 16, 2020 6:30 am

Re: cookies

Post by aneal »

Honestly I don't think its a cookie or a cache issue, although I can't be certain. Yes I do use a different request context for each MDI Child otherwise all the following browser windows would report 'logged in as aneal'.

To illustrate the issue I am having here's my log showing happens if I open two MDI children but only after waiting for the first window to complete the logging in (via TChromium1.ExecuteJavaScript) and posting of the messages (i.e. sequential processing). This is the desired state because both windows are independently doing work and reporting back:
17/08/2020 9:16:09 PM OSR Browser 1 opening...
17/08/2020 9:16:09 PM OSR Browser 1 opened
17/08/2020 9:16:09 PM OSR Browser 1 Go clicked
17/08/2020 9:16:17 PM OSR Browser 1 'Logged in as Aneal'
17/08/2020 9:16:29 PM OSR Browser 2 opening...
17/08/2020 9:16:29 PM OSR Browser 2 opened
17/08/2020 9:16:29 PM OSR Browser 2 Go clicked
17/08/2020 9:16:36 PM OSR Browser 2 'Logged in as Aneal2'

However, if I repeat the steps above but open two windows in quick succession to simulate concurrent processing then only the last window completes the login process (i.e. browser 3 never logs in) as follows
17/08/2020 9:16:50 PM OSR Browser 3 opening...
17/08/2020 9:16:50 PM OSR Browser 3 opened
17/08/2020 9:16:50 PM OSR Browser 3 Go clicked
17/08/2020 9:16:51 PM OSR Browser 4 opening...
17/08/2020 9:16:51 PM OSR Browser 4 opened
17/08/2020 9:16:51 PM OSR Browser 4 Go clicked
17/08/2020 9:16:57 PM OSR Browser 4 'Logged in as Aneal2'

I suspect my Go Button event may be at fault. It looks a little like this:

procedure TChildForm.GoBtnClick(Sender: TObject);
var
J : Integer;
TempMessage, TempJSCode : string;
begin
LogForm.AddToLog(Caption + ' Go clicked');
for J := 0 to 2 do
begin
Ffinished := False;
if J = 0 then
begin
AddressCb.Text := YOUR_URL;
chrmosr.LoadURL(AddressCb.Text);
end
else if J = 1 then
begin
chrmosr.ExecuteJavaScript(SETUSERNAME, 'about:blank', 0);
chrmosr.ExecuteJavaScript(SETPASSWORD, 'about:blank', 0);
chrmosr.ExecuteJavaScript(CLICKLOGINBUTTON, 'about:blank', 0);
end
else if J = 2 then
begin
TempMessage := GETLOGINFROMELEMENT;
TempJSCode := 'console.log("' + CONSOLE_MSG_PREAMBLE + '" + ' + TempMessage + ');';
chrmosr.ExecuteJavaScript(TempJSCode, 'about:blank'); //this invokes a PostMessage logging the authenticated username in VCL
end;
//note: Ffinished is set to True in the TChildForm.chrmosrLoadingStateChange event
while (Ffinished = FALSE) do
begin
Sleep(100);
Application.ProcessMessages;
end
end;
LogForm.AddToLog(Caption + ' finished');
end;


In any case I am beginning to think this approach is not correct. Maybe I should be using an external pump browser to do this? What advantages does an OSR browser have over the simple browser? Or maybe I should be invoking/controlling each browser via a DLL? To me it seems you have produced many examples which look interesting, but sadly there is not enough documentation for me to understand what exactly they are meant to demonstrate.
w1ld32
Posts: 19
Joined: Wed Feb 12, 2020 10:28 am

Re: cookies

Post by w1ld32 »

Sorry to interrupt, and for my English. I am wondering at what exact moment the event is called, perhaps at the time of execution of your scripts, the page is not yet ready to execute them. My app logs in to 100+ accounts and everything is fine.
aneal
Posts: 9
Joined: Tue Jun 16, 2020 6:30 am

Re: cookies

Post by aneal »

Yes and thank you for your reply w1ld32 - you are correct. After a little more investigation a few faults were removed allowing my code to work exactly as I hoped.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: cookies

Post by salvadordf »

The "ExternalPumpBrowser" and other demos with an "external pump" use a different message loop processing in the browser. It's not related to your issue but here you have the original CEF code comments for GlobalCEFApp.DoMessageLoopWork :

Code: Select all

///
// Perform a single iteration of CEF message loop processing. This function is
// provided for cases where the CEF message loop must be integrated into an
// existing application message loop. Use of this function is not recommended
// for most users; use either the cef_run_message_loop() function or
// CefSettings.multi_threaded_message_loop if possible. When using this function
// care must be taken to balance performance against excessive CPU usage. It is
// recommended to enable the CefSettings.external_message_pump option when using
// this function so that
// cef_browser_process_handler_t::on_schedule_message_pump_work() callbacks can
// facilitate the scheduling process. This function should only be called on the
// main application thread and only if cef_initialize() is called with a
// CefSettings.multi_threaded_message_loop value of false (0). This function
// will not block.
///
As you can see, this is has nothing to do with your issue. The external pump is used by applications that have a different way to handle Windows messages like some Firemonkey apps. Other VCL apps with very specific needs may also use it but the default message loop processing is recomended.

Going back to your issue, I would recomend that you split that function in several smaller functions, one for each "state". Each "state" would send a message to start the next "state" for that browser. For example :
  • State 0 : Load the url. Send a windows message to the form when it finishes loading.
  • State 1 : Set the username with JS and send a message with the "console trick" to start the next state.
  • State 2 : Set the password with JS and send a message with the "console trick" to start the next state.
  • State 3 : Click the login button with JS and send a message with the "console trick" to start the next state.
  • State 4 : Wait for the page to finish loading and send a windows message to the form to let it know that the user is now fully logged in.
States 0-3 would have different functions that load a url or execute some JS code. The State 4 only needs the TChromium.OnLoadingStateChange event to know when it's finished.

Chromium uses several processes and threads to work. This forces us to work with asynchronous functions and "states".
It's easy to run into troubles if you try to work any other way like :
  • Using TThread.Synchronize to do something in the main thread.
  • Using sleep and Application.ProcessMessages to wait for events.
I'm not saying the you should never use those tricks but in the case of Chromium browsers they are not recomended.
User avatar
salvadordf
Posts: 4016
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: cookies

Post by salvadordf »

I see now that you found a way to fix those issues. :)
Post Reply