title: Multi-threaded C# Selenium WebDriver automation with Uris not known beforehand
body: <p>I need to perform some simultaneous webdrivers manipulation, but I am uncertain as to how to do this.</p>
  
  <p>What I am asking here is: </p>
  
  <ol>
  <li>What is the correct way to achieve this ?</li>
  <li>What is the reason for the exception I am getting (revealed below)</li>
  </ol>
  
  <p>After some research I ended up with:</p>
  
  <p><strong>1</strong>. The way I see people doing this (and the one I ended up using after playing with the API, before searching) is to loop over the window handles my WebDriver has at hand, and perform a switch to and out of the window handle I want to process, closing it when I am finished.</p>
  
  <p><strong>2</strong>. Selenium Grid does not seem like an option fore me - am I wrong or it is intended for parallel processing ? Since am running everything in a single computer, it will be of no use for me.</p>
  
  <hr>
  
  <p>In trying the 1st option, I have the following scenario (a code sample is available below, I skipped stuff that is not relevant/repeat itself (where ever I added 3 dots:</p>
  
  <p>I have a html page, with several submit buttons, stacked.</p>
  
  <p>Clicking each of them will open a new browser/tab (interestingly enough, using ChromeDriver opens tabs, while FirefoxDriver opens separate windows for each.)</p>
  
  <p>As a side note: I can't determine the uris of each submit beforehand (they must be determined by javascript, and at this point, let's just assume I want to handle everything knowing nothing about the client code.</p>
  
  <p>Now, after looping over all the submit buttons, and issuing webElement.Click() on the corresponding elements, the tabs/windows open. The code flows to create a list of tasks to be executed, one for each new tab/window.</p>
  
  <p><strong>The problem is</strong>: since all tasks all depend upon the same instance of webdriver to switch to the window handles, seems I will need to add resource sharing locks/control. I am uncertain as whether I am correct, since I saw no mention of locks/resource access control in searching for multi-threaded web driver examples.</p>
  
  <p>On the other hand, if I am able to determine the tabs/windows uris beforehand, I would be able to skip all the automation steps needed to reach this point, and then creating a webDriver instance for each thread, via <code>Navigate().GoToUrl()</code> would be straightforward. But this looks like a deadlock! I don't see webDriver's API providing any access to the newly opened tab/window without performing a switch. And I only want to switch if I do not have to repeat all the automation steps that lead me to the current window !</p>
  
  <p>...</p>
  
  <p>In any case, I keep getting the exception:</p>
  
  <p><strong><code>Element belongs to a different frame than the current one - switch to its containing frame to use it</code></strong></p>
  
  <p>at </p>
  
  <pre><code>IWebElement element = cell.FindElement
  </code></pre>
  
  <p>inside the <code>ToDictionary()</code> block.</p>
  
  <p>I obviously checked that all my selectors are returning results, in chrome's console. </p>
  
  <pre><code>foreach (WebElement resultSet in resultSets)
      resultSet.Click();
  
  
  foreach(string windowHandle in webDriver.WindowHandles.Skip(1))
  {
      dataCollectionTasks.Add(Task.Factory.StartNew&lt;List&lt;DataTable&gt;&gt;(obj =&gt;
      {
          List&lt;DataTable&gt; collectedData = new List&lt;DataTable&gt;();
          string window = obj as string;
  
          if (window != null)
          {
              webDriver.SwitchTo().Window(windowHandle);
              List&lt;WebElement&gt; dataSets = webDriver.FindElements(By.JQuerySelector(utils.GetAppSetting("selectors.ResultSetData"))).ToList();
  
              DataTable data = null;
  
              for (int i = 0; i &lt; dataSets.Count; i += 2)
              {
                  data = new DataTable();
  
                  data.Columns.Add("Col1", typeof(string));
                  data.Columns.Add("Col2", typeof(string));
                  data.Columns.Add("Col3", typeof(string));
  
                  ///...
  
                  //data set header
                  if (i % 2 != 0)
                  {
                      IWebElement headerElement = dataSets[i].FindElement(OpenQA.Selenium.By.CssSelector(utils.GetAppSetting("selectors.ResultSetDataHeader")));
                      data.TableName = string.Join(" ", headerElement.Text.Split().Take(3));
                  }
                  //data set records
                  else
                  {
                      Dictionary&lt;string, string&gt; cells = dataSets[i]
                          .FindElements(OpenQA.Selenium.By.CssSelector(utils.GetAppSetting("selectors.ResultSetDataCell")))
                          .ToDictionary(
                              cell =&gt;
                              {
                                  IWebElement element = cell.FindElement(OpenQA.Selenium.By.CssSelector(utils.GetAppSetting("selectors.ResultSetDataHeaderColumn")));
                                  return element == null ? string.Empty : element.Text;
                              },
                              cell =&gt;
                              {
                                  return cell == null ? string.Empty : cell.Text;
                              });
  
                      string col1Value, col2Value, col3Value; //...
                      cells.TryGetValue("Col1", out col1Value);
                      cells.TryGetValue("Col2", out col2Value);
                      cells.TryGetValue("Col3", out col3Value);
                      //...
  
                      data.Rows.Add(col1Value, col2Value, col3Value /*...*/);
                  }
              }
  
              collectedData.Add(data);
          }
  
          webDriver.SwitchTo().Window(mainWindow);
          webDriver.Close();
  
          return collectedData;
      }, windowHandle));
  } //foreach
  
  Task.WaitAll(dataCollectionTasks.ToArray());
  foreach (Task&lt;List&lt;DataTable&gt;&gt; dataCollectionTask in dataCollectionTasks)
  {
      results.AddRange(dataCollectionTask.Result);
  }
  
  return results;
  </code></pre>